├── .github └── workflows │ ├── docs.yml │ └── tests.yml ├── .gitignore ├── .phpcs.xml ├── .readthedocs.yml ├── .scrutinizer.yml ├── AUTHORS.rst ├── CHANGES.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.rst ├── DEVELOP.rst ├── LICENSE ├── NOTICE ├── README.rst ├── Vagrantfile ├── codecov.yml ├── composer.json ├── devtools └── create_tag.sh ├── docs ├── Makefile ├── _extra │ └── robots.txt ├── appendices │ ├── compatibility.rst │ ├── data-types.rst │ ├── index.rst │ └── table-options.rst ├── build.json ├── conf.py ├── connect.rst ├── docutils.conf ├── getting-started.rst ├── index.rst └── requirements.txt ├── phpunit.xml.dist ├── provisioning.sh ├── src └── Crate │ └── DBAL │ ├── Driver │ └── PDOCrate │ │ ├── CrateStatement.php │ │ ├── Driver.php │ │ └── PDOConnection.php │ ├── Platforms │ ├── CratePlatform.php │ ├── CratePlatform1.php │ ├── CratePlatform4.php │ └── Keywords │ │ └── CrateKeywords.php │ ├── Schema │ └── CrateSchemaManager.php │ └── Types │ ├── ArrayType.php │ ├── MapType.php │ └── TimestampType.php └── test └── Crate └── Test └── DBAL ├── DBALFunctionalTestCase.php ├── Functional ├── BindingTest.php ├── ConnectionTest.php ├── DataAccessTest.php ├── ModifyLimitQueryTest.php ├── NamedParametersTest.php ├── Schema │ └── SchemaManagerTest.php ├── TableOptionsTest.php ├── TypeConversionTest.php ├── Types │ └── MapTypeTest.php └── WriteTest.php ├── PDOCrate └── DriverTest.php └── Platforms └── CratePlatformTest.php /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | schedule: 10 | - cron: '0 7 * * *' 11 | 12 | # Allow job to be triggered manually. 13 | workflow_dispatch: 14 | 15 | # Cancel in-progress jobs when pushing to the same branch. 16 | concurrency: 17 | cancel-in-progress: true 18 | group: ${{ github.workflow }}-${{ github.ref }} 19 | 20 | jobs: 21 | documentation: 22 | name: Build docs on ${{ matrix.os }} 23 | runs-on: ${{ matrix.os }} 24 | 25 | strategy: 26 | matrix: 27 | os: [ubuntu-latest] 28 | 29 | steps: 30 | 31 | - name: Acquire sources 32 | uses: actions/checkout@v3 33 | 34 | - name: Set up Python 35 | uses: actions/setup-python@v4 36 | with: 37 | python-version: "3.11" 38 | 39 | - name: Build docs 40 | run: | 41 | cd docs && make check 42 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | # Allow job to be triggered manually. 10 | workflow_dispatch: 11 | 12 | # Cancel in-progress jobs when pushing to the same branch. 13 | concurrency: 14 | cancel-in-progress: true 15 | group: ${{ github.workflow }}-${{ github.ref }} 16 | 17 | jobs: 18 | tests: 19 | 20 | runs-on: ${{ matrix.os }} 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | os: [ 'ubuntu-latest' ] #, macos-latest, windows-latest ] 25 | php-version: [ '8.0', '8.1', '8.2', '8.3', '8.4' ] 26 | 27 | # https://docs.github.com/en/free-pro-team@latest/actions/guides/about-service-containers 28 | services: 29 | cratedb: 30 | image: crate/crate:nightly 31 | ports: 32 | - 4200:4200 33 | 34 | # https://github.com/marketplace/actions/setup-php-action 35 | name: PHP ${{ matrix.php-version }} on OS ${{ matrix.os }} 36 | steps: 37 | 38 | - name: Acquire sources 39 | uses: actions/checkout@v3 40 | with: 41 | fetch-depth: 2 42 | 43 | - name: Setup PHP 44 | uses: shivammathur/setup-php@v2 45 | with: 46 | php-version: ${{ matrix.php-version }} 47 | # Select PHPUnit version suitable for PHP 7.2. 48 | tools: composer, phpunit:^8.5 49 | 50 | - uses: ramsey/composer-install@v2 51 | 52 | # Remark: --prefer-source is needed for "Doctrine\Tests\DBAL\Platforms\AbstractPlatformTestCase" 53 | - name: Install doctrine/dbal from source 54 | run: | 55 | rm -rf vendor/doctrine/dbal 56 | composer update doctrine/dbal --prefer-source 57 | 58 | - name: Run code style checks 59 | run: composer run check-style 60 | 61 | - name: Run tests 62 | run: composer run test 63 | 64 | # https://github.com/codecov/codecov-action 65 | - name: Upload coverage results to Codecov 66 | uses: codecov/codecov-action@v4 67 | if: always() && (matrix.php-version == '7.4' || startsWith(matrix.php-version, '8.')) 68 | with: 69 | files: ./build/logs/clover.xml 70 | fail_ci_if_error: false 71 | 72 | - name: Upload coverage results to Scrutinizer CI 73 | if: always() && (matrix.php-version == '7.4' || startsWith(matrix.php-version, '8.')) 74 | run: | 75 | composer global require scrutinizer/ocular 76 | ocular code-coverage:upload --format=php-clover build/logs/clover.xml 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .installed.cfg 3 | .project 4 | .vagrant 5 | .phpunit.result.cache 6 | *-console.log 7 | .crate-docs 8 | /composer.lock 9 | bin/ 10 | build/ 11 | composer.phar 12 | out/ 13 | report/ 14 | vendor/ 15 | -------------------------------------------------------------------------------- /.phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ./src 7 | ./vendor/* 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | 4 | build: 5 | os: "ubuntu-22.04" 6 | tools: 7 | python: "3.11" 8 | 9 | python: 10 | install: 11 | - requirements: docs/requirements.txt 12 | 13 | sphinx: 14 | builder: html 15 | configuration: docs/conf.py 16 | fail_on_warning: true 17 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Scrutinizer code analysis for PHP. 3 | # 4 | # https://scrutinizer-ci.com/docs/guides/php/ 5 | # https://scrutinizer-ci.com/docs/guides/php/continuous-integration-deployment 6 | # https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/ 7 | # https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/checks 8 | 9 | 10 | filter: 11 | 12 | # Everything in a root level "test" directory will be excluded 13 | excluded_paths: 14 | - "test/" 15 | 16 | # Everything in a root level "vendor" directory will be excluded from analysis 17 | # but treated as a dependency 18 | dependency_paths: 19 | - "vendor/" 20 | 21 | 22 | before_commands: 23 | - "composer install --dev --prefer-source" 24 | 25 | 26 | # Use new PHP Analysis Engine. 27 | # https://scrutinizer-ci.com/docs/tools/php/php-analyzer/guides/migrate_to_new_php_analysis 28 | build: 29 | nodes: 30 | analysis: 31 | environment: 32 | php: 33 | version: 8.1 34 | tests: 35 | override: 36 | - php-scrutinizer-run --enable-security-analysis 37 | - phpcs-run --standard=.phpcs.xml 38 | 39 | 40 | # https://scrutinizer-ci.com/docs/tools/external-code-coverage/ 41 | tools: 42 | external_code_coverage: 43 | 44 | enabled: true 45 | 46 | # Scrutinizer will wait for two code coverage submissions 47 | # in order to cover both PHP7 and PHP8. 48 | runs: 2 49 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Main Authors 2 | ============ 3 | 4 | Christian Haudum (@chaudum) 5 | Sebastian Utz (@seut) 6 | Tom Kapanka (@spanktar) 7 | 8 | Code Contributors 9 | ================= 10 | 11 | Kamil Adryjanek (@kadryjanek) 12 | Alexander Schranz (@alexander-schranz) 13 | 14 | Note: (@user) means a github user name. 15 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | ====================== 2 | CHANGES for crate-dbal 3 | ====================== 4 | 5 | Unreleased 6 | ========== 7 | 8 | - Verified support on PHP 8.4 9 | 10 | 2024/02/02 4.0.2 11 | ================ 12 | 13 | - Update internal version number 14 | 15 | 2024/02/02 4.0.1 16 | ================ 17 | 18 | - README: Update list of supported PHP versions 19 | 20 | 2024/02/02 4.0.0 21 | ================ 22 | 23 | - Dropped support for PHP 7.x, it has reached end of life 24 | - Added support for PHP 8.3 25 | 26 | 2022/11/29 3.0.1 27 | ================ 28 | 29 | - Added support for PHP 8.1 and PHP 8.2 30 | 31 | 2021/04/28 3.0.0 32 | ================ 33 | 34 | - Dropped support for PHP 7.2, it has reached end of life 35 | 36 | - Upgraded to CrateDB PDO Adapter 2.1.2 37 | 38 | - Fixed compatibility with Doctrine DBAL 2.13 39 | 40 | - Added support for PHP 8.0 41 | 42 | 2020/06/18 2.3.0 43 | ================ 44 | 45 | Changes 46 | ------- 47 | 48 | - Added support for CrateDB specific table options when retrieving table information 49 | from the schema manager. 50 | 51 | Fixes 52 | ----- 53 | 54 | None 55 | 56 | 2020/01/08 2.2.0 57 | ================ 58 | 59 | Changes 60 | ------- 61 | 62 | - Updated Doctrine DBAL version to 2.10 63 | 64 | Fixes 65 | ----- 66 | 67 | None 68 | 69 | 2019/11/21 2.1.0 70 | ================ 71 | 72 | Changes 73 | ------- 74 | 75 | - Added support for CrateDB specific table options incl. usage documentation. 76 | 77 | Fixes 78 | ----- 79 | 80 | - Fixed handling of ``PRIMARY KEY`` contraints. 81 | Tables *without* any PK raised an exception while reading, on Tables 82 | *with* any PK, the constraints weren't read/processed correctly. 83 | 84 | 85 | 2019/08/20 2.0.0 86 | ================ 87 | 88 | - Added support for CrateDB >= 4.0 89 | 90 | - Added ARRAY type mapping for all typed arrays supported by CrateDB. Before 91 | only a subset was supported. 92 | 93 | - Updated Doctrine DBAL version to 2.9 94 | 95 | - BREAKING: Drop support for PHP < 7.2 96 | 97 | 2018/09/05 1.1.0 98 | ================ 99 | 100 | - Added support for latest (< 2.7.0) Doctrine version 101 | 102 | 2018/06/15 1.0.0 103 | ================ 104 | 105 | - Updated crate-pdo to ``1.0.0`` which includes following changes: 106 | 107 | - Added support for SSL via ``PDO::CRATE_ATTR_SSL_MODE`` 108 | 109 | - BREAKING: Upgraded the library to use PHP 7.2 features 110 | 111 | 2017/02/06 0.3.1 112 | ================ 113 | 114 | - Fixed an issue that ignored provided ``UNIQUE`` constraints. The client now 115 | throws an exception and informs the user to use the ``PRIMARY KEY`` 116 | constraint instead. 117 | 118 | 2016/11/07 0.3.0 119 | ================ 120 | 121 | - Updated crate-pdo to ``0.6.0`` which includes following changes: 122 | 123 | - Expose ``getServerVersion()`` and ``getServerInfo()`` on the PDO implementation 124 | which return the version number of the Crate server connected to. 125 | 126 | - Fix: having the same named parameter multiple times in a prepared SQL 127 | statement caused incorrect parameter substitution with bound values. 128 | 129 | - Fixed an issue that occur if parameters are passed in a different order 130 | than specified in the sql statement. 131 | 132 | - Updated dependency: guzzlehttp/guzzle to ~6.0 133 | WARNING: This is a backward incompatible change! 134 | 135 | - Support for multiple hosts in DSN connection string 136 | 137 | - Added support for using a default schema in PDO connection 138 | via ``/schema`` suffix in connection string. 139 | 140 | 2016/01/12 0.2.1 141 | ================ 142 | 143 | - updated crate-pdo to ``0.3.1`` which includes following changes: 144 | 145 | - Added support for PHP 7 146 | 147 | 2015/05/08 0.2.0 148 | ================ 149 | 150 | - updated crate-pdo to ``0.2.1`` which includes following changes: 151 | 152 | - Support guzzle http basic auth 153 | through doctrine dbal connection user credentials 154 | 155 | 2015/02/11 0.1.1 156 | ================ 157 | 158 | - add support for latest (up to 2.5.1) Doctrine version 159 | 160 | 2015/01/08 0.1.0 161 | ================ 162 | 163 | - updated dependency ``crate-pdo 0.1.0`` which includes following 164 | changes: 165 | 166 | - Fix performance issues by switching http client library to 167 | ``guzzle``. 168 | 169 | 2014/12/19 0.0.5 170 | ================ 171 | 172 | - updated dependency ``crate-pdo 0.0.7`` which includes following changes: 173 | 174 | - Fix: Literals containing a `:` character were misinterpreted as 175 | named parameters. 176 | 177 | - Nailed dependency versions of amphp/artax and amphp/amp 178 | to prevent composer from fetching newer, incompatible releases 179 | 180 | 2014/11/27 0.0.4 181 | ================ 182 | 183 | - updated dependency ``crate-pdo 0.0.5``, this version will support 184 | crate ``array`` and ``object`` data types 185 | 186 | 2014/10/30 0.0.3 187 | ================ 188 | 189 | - updated dependency ``crate-pdo 0.0.4`` 190 | 191 | 2014/10/27 0.0.2 192 | ================ 193 | 194 | - updated dependencies: 195 | - php 5.5 196 | - crate-pdo 0.0.3 197 | 198 | 2014/10/27 0.0.1 199 | ================ 200 | 201 | - initial release 202 | see ``README.rst`` for supported types, limitations and not supported DBAL features 203 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at support@crate.io. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | 5 | Thank you for your interest in contributing. 6 | 7 | Please see the CrateDB `contribution guide`_ for more information. Everything in 8 | the CrateDB contribution guide also applies to this repository. 9 | 10 | .. _contribution guide: https://github.com/crate/crate/blob/master/CONTRIBUTING.rst 11 | -------------------------------------------------------------------------------- /DEVELOP.rst: -------------------------------------------------------------------------------- 1 | ############### 2 | Developer Guide 3 | ############### 4 | 5 | 6 | ************* 7 | Using Vagrant 8 | ************* 9 | 10 | 11 | Prerequisites 12 | ============= 13 | 14 | You will need Vagrant_ and one of its providers. 15 | 16 | We currently use VirtualBox_ but any provider should work just as well. 17 | 18 | Installation 19 | ============ 20 | 21 | Clone the project:: 22 | 23 | git clone git@github.com:crate/crate-dbal.git 24 | cd crate-dbal 25 | 26 | Start up the Vagrant machine:: 27 | 28 | vagrant up 29 | 30 | When run for the first time, it will also run the needed provisioning. 31 | 32 | If you are using IntelliJ or PhpStorm IDE you can follow the `IDE guide`_ to 33 | set up your remote interpreter and test environment. 34 | 35 | Running the Tests 36 | ================= 37 | 38 | You can run the tests like:: 39 | 40 | vagrant ssh 41 | cd /vagrant 42 | 43 | # Run test suite 44 | composer run test 45 | 46 | # Run code style checks 47 | composer run check-style 48 | 49 | # Some code style quirks can be automatically fixed 50 | composer run fix-style 51 | 52 | 53 | 54 | ************ 55 | Using Docker 56 | ************ 57 | 58 | 59 | Installation 60 | ============ 61 | 62 | Install prerequisites:: 63 | 64 | # Install different PHP releases. 65 | brew install php@7.3 php@7.4 php@8.0 brew-php-switcher composer 66 | 67 | # Select PHP version. 68 | brew-php-switcher 7.3 69 | brew-php-switcher 7.4 70 | brew-php-switcher 8.0 71 | 72 | # Install xdebug extension for tracking code coverage. 73 | pecl install xdebug 74 | 75 | Get the sources:: 76 | 77 | git clone git@github.com:crate/crate-dbal.git 78 | cd crate-dbal 79 | 80 | Setup project dependencies:: 81 | 82 | composer install 83 | 84 | 85 | Running the Tests 86 | ================= 87 | 88 | :: 89 | 90 | # Run CrateDB 91 | docker run -it --rm --publish 4200:4200 --publish 5432:5432 crate/crate:nightly 92 | 93 | # Run test suite 94 | composer run test 95 | 96 | # Run code style checks 97 | composer run style 98 | 99 | # Run code style checks 100 | ./vendor/bin/phpcs ./src 101 | 102 | 103 | **************************** 104 | Working on the documentation 105 | **************************** 106 | 107 | - The documentation is written using `Sphinx`_ and `ReStructuredText`_. 108 | - Python>=3.7 is required. 109 | 110 | Change into the ``docs`` directory: 111 | 112 | .. code-block:: console 113 | 114 | $ cd docs 115 | 116 | For help, run: 117 | 118 | .. code-block:: console 119 | 120 | $ make 121 | 122 | Crate Docs Build 123 | 124 | Run `make `, where is one of: 125 | 126 | dev Run a Sphinx development server that builds and lints the 127 | documentation as you edit the source files 128 | 129 | html Build the static HTML output 130 | 131 | check Build, test, and lint the documentation 132 | 133 | qa Generate QA telemetry 134 | 135 | reset Reset the build 136 | 137 | You must install `fswatch`_ to use the ``dev`` target. 138 | 139 | 140 | Continuous integration and deployment 141 | ===================================== 142 | 143 | CI is configured to run ``make check`` from the ``docs`` directory. 144 | 145 | `Read the Docs`_ (RTD) automatically deploys the documentation whenever a 146 | configured branch is updated. 147 | 148 | To make changes to the RTD configuration (e.g., to activate or deactivate a 149 | release version), please contact the `@crate/tech-writing`_ team. 150 | 151 | 152 | Archiving Docs Versions 153 | ======================= 154 | 155 | Check the `versions hosted on ReadTheDocs`_. 156 | 157 | We should only be hosting the docs for `latest`, the last three minor release 158 | branches of the last major release, and the last minor release branch 159 | corresponding to the last two major releases. 160 | 161 | For example: 162 | 163 | - ``latest`` 164 | - ``0.3`` 165 | - ``0.2`` 166 | - ``0.1`` 167 | 168 | Because this project has not yet had a major release, as of yet, there are no 169 | major releases before `0` to include in this list. 170 | 171 | To make changes to the RTD configuration (e.g., to activate or deactivate a 172 | release version), please contact the `@crate/docs`_ team. 173 | 174 | 175 | ********* 176 | Releasing 177 | ********* 178 | 179 | - Bump version number in ``src/Crate/DBAL/Driver/PDOCrate/Driver.php`` 180 | - Edit ``CHANGES.txt`` correspondingly 181 | - Commit changes: ``Release x.x.x`` 182 | - Tag repository: ``git tag x.x.x`` 183 | - Push to GitHub: ``git push && git push --tags`` 184 | - Verify publishing worked: https://packagist.org/packages/crate/crate-dbal 185 | 186 | 187 | .. _@crate/docs: https://github.com/orgs/crate/teams/docs 188 | .. _@crate/tech-writing: https://github.com/orgs/crate/teams/tech-writing 189 | .. _Composer: https://getcomposer.org 190 | .. _IDE guide: https://gist.github.com/mikethebeer/d8feda1bcc6b6ef6ea59 191 | .. _ReStructuredText: http://docutils.sourceforge.net/rst.html 192 | .. _Sphinx: http://sphinx-doc.org/ 193 | .. _Vagrant: https://www.vagrantup.com/downloads.html 194 | .. _versions hosted on ReadTheDocs: https://readthedocs.org/projects/crate-dbal/versions/ 195 | .. _VirtualBox: https://www.virtualbox.org/ 196 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright [yyyy] [name of copyright owner] 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | 205 | 206 | =============================================================================== 207 | 208 | For the `docs` directory: 209 | 210 | The source files for the documentation are licensed under the Apache License 211 | Version 2.0. These source files are used by the project maintainers to build 212 | online documentation for end-users: 213 | 214 | 215 | 216 | If you want to make contributions to the documentation, it may be necessary for 217 | you to build the documentation yourself by following the instructions in the 218 | `DEVELOP.rst` file. If you do this, a number of third-party software components 219 | are necessary. 220 | 221 | We do not ship the source code for these optional third-party software 222 | components or their dependencies, so we cannot make any guarantees about the 223 | licensing status of these components. 224 | 225 | However, for convenience, the documentation build system explicitly references 226 | the following software components (grouped by license): 227 | 228 | PSF License: 229 | 230 | - Python 3 231 | 232 | MIT License: 233 | 234 | - pip 235 | - setuptools 236 | - sphinx-autobuild 237 | 238 | BSD License: 239 | 240 | - alabaster 241 | - sphinx 242 | 243 | Apache License 2.0: 244 | 245 | - crate-docs-theme 246 | 247 | Please note that each of these components may specify its own dependencies and 248 | those dependencies may be licensed differently. 249 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | CrateDB DBAL Driver 2 | Copyright 2013-2018 Crate.IO GmbH ("Crate") 3 | 4 | 5 | Licensed to Crate.IO GmbH (referred to in this notice as "Crate") under one or 6 | more contributor license agreements. 7 | 8 | Unless otherwise stated, every component of the CrateDB DBAL Driver is licensed 9 | under the Apache License, Version 2.0. 10 | 11 | Third-party components bundled with the CrateDB DBAL Driver are listed in the 12 | LICENSE file along with their respective licenses. Any additional mandatory 13 | notices pertaining to those dependencies will be reproduced in this file. 14 | 15 | Crate provides pre-built releases for convenience, also known as convenience 16 | releases. Building the CrateDB DBAL Driver from source pulls in a number of 17 | additional third-party dependencies. Hence, convenience releases will contain a 18 | mix of CCrateDB DBAL Driver source code and third-party build-time dependencies 19 | not listed in the LICENSE file or in this file. 20 | 21 | Crate is committed to only using permissively licensed third-party code. 22 | However, for the time being, if you make use of convenience releases, it is 23 | your responsibility to check the licensing status of the bundled third-party 24 | build-time dependencies. 25 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | CrateDB DBAL Driver 3 | =================== 4 | 5 | .. image:: https://github.com/crate/crate-dbal/workflows/Tests/badge.svg 6 | :target: https://github.com/crate/crate-dbal/actions?workflow=Tests 7 | :alt: Build status (code) 8 | 9 | .. image:: https://github.com/crate/crate-dbal/workflows/Docs/badge.svg 10 | :target: https://github.com/crate/crate-dbal/actions?workflow=Docs 11 | :alt: Build status (documentation) 12 | 13 | .. image:: https://codecov.io/gh/crate/crate-dbal/branch/main/graph/badge.svg 14 | :target: https://app.codecov.io/gh/crate/crate-dbal 15 | :alt: Coverage 16 | 17 | .. image:: https://scrutinizer-ci.com/g/crate/crate-dbal/badges/quality-score.png?b=main 18 | :target: https://scrutinizer-ci.com/g/crate/crate-dbal 19 | :alt: Quality 20 | 21 | .. image:: https://poser.pugx.org/crate/crate-dbal/v/stable 22 | :target: https://packagist.org/packages/crate/crate-dbal 23 | :alt: Latest stable version 24 | 25 | .. image:: https://img.shields.io/badge/PHP-8.0%2C%208.1%2C%208.2%2C%208.3%2C%208.4-green.svg 26 | :target: https://packagist.org/packages/crate/crate-dbal 27 | :alt: Supported PHP versions 28 | 29 | .. image:: https://poser.pugx.org/crate/crate-dbal/d/monthly 30 | :target: https://packagist.org/packages/crate/crate-dbal 31 | :alt: Monthly downloads 32 | 33 | .. image:: https://poser.pugx.org/crate/crate-dbal/license 34 | :target: https://packagist.org/packages/crate/crate-dbal 35 | :alt: License 36 | 37 | | 38 | 39 | The CrateDB DBAL driver is an implementation of the `DBAL`_ abstraction layer 40 | for CrateDB_. 41 | 42 | `DBAL`_ is a PHP database abstraction layer that comes with database schema 43 | introspection, schema management, and `PDO`_ abstraction. 44 | 45 | Prerequisites 46 | ============= 47 | 48 | You need to be using PHP and Composer_. 49 | 50 | Installation 51 | ============ 52 | 53 | The CrateDB PDO adapter is available as a Composer package. Install it like:: 54 | 55 | composer require crate/crate-dbal 56 | 57 | See the `installation documentation`_ for more info. 58 | 59 | Contributing 60 | ============ 61 | 62 | This project is primarily maintained by `Crate.io`_, but we welcome community 63 | contributions! 64 | 65 | See the `developer docs`_ and the `contribution docs`_ for more information. 66 | 67 | Help 68 | ==== 69 | 70 | Looking for more help? 71 | 72 | - Read the `project docs`_ 73 | - Check out our `support channels`_ 74 | 75 | .. _`DBAL`: http://www.doctrine-project.org/projects/dbal.html 76 | .. _`PDO`: http://php.net/manual/en/book.pdo.php 77 | .. _Composer: https://getcomposer.org/ 78 | .. _contribution docs: CONTRIBUTING.rst 79 | .. _Crate.io: http://crate.io/ 80 | .. _CrateDB: https://github.com/crate/crate 81 | .. _developer docs: DEVELOP.rst 82 | .. _installation documentation: https://crate.io/docs/reference/dbal/installation.html 83 | .. _project docs: https://crate.io/docs/reference/dbal/ 84 | .. _support channels: https://crate.io/support/ 85 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | VAGRANTFILE_API_VERSION = "2" 2 | 3 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 4 | 5 | config.vm.box = "ubuntu/bionic64" 6 | 7 | config.vm.network "forwarded_port", guest: 4200, host: 44200 8 | 9 | config.vm.provision "shell", path: "provisioning.sh" 10 | 11 | config.vm.provider "virtualbox" do |vb| 12 | vb.customize ["modifyvm", :id, "--memory", "1024"] 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # https://docs.codecov.io/docs/common-recipe-list 2 | # https://docs.codecov.io/docs/commit-status#patch-status 3 | 4 | coverage: 5 | status: 6 | project: 7 | default: 8 | target: 85% # the required coverage value 9 | threshold: 3% # the leniency in hitting the target 10 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crate/crate-dbal", 3 | "description": "Doctrine Database Abstraction Layer (DBAL) adapter for CrateDB", 4 | "authors": [ 5 | { 6 | "name": "Crate.io", 7 | "email": "office@crate.io", 8 | "homepage": "https://crate.io", 9 | "role": "maintainer" 10 | } 11 | ], 12 | "license": "Apache-2.0", 13 | "homepage": "https://github.com/crate/crate-dbal", 14 | "keywords": ["database", "dbal", "doctrine", "cratedb"], 15 | "minimum-stability": "dev", 16 | "prefer-stable": true, 17 | "require": { 18 | "php": "^8.0|^8.1|^8.2|^8.3|^8.4", 19 | "doctrine/dbal": "^2", 20 | "crate/crate-pdo": "^2" 21 | }, 22 | "autoload": { 23 | "psr-0": { 24 | "Crate\\DBAL": "src" 25 | } 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^9.0", 29 | "squizlabs/php_codesniffer": "^3.5" 30 | }, 31 | "autoload-dev": { 32 | "psr-0": { 33 | "Crate\\Test": "test", 34 | "Doctrine\\Tests": "vendor/doctrine/dbal/tests" 35 | } 36 | }, 37 | "config": { 38 | "preferred-install": { 39 | "doctrine/dbal": "source" 40 | } 41 | }, 42 | "scripts": { 43 | "test": "XDEBUG_MODE=coverage phpunit --coverage-clover build/logs/clover.xml", 44 | "check-style": "phpcs", 45 | "fix-style": "phpcbf" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /devtools/create_tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Licensed to Crate.IO GmbH ("Crate") under one or more contributor 4 | # license agreements. See the NOTICE file distributed with this work for 5 | # additional information regarding copyright ownership. Crate licenses 6 | # this file to you under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. You may 8 | # obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | # 18 | # However, if you have executed another commercial license agreement 19 | # with Crate these terms will supersede the license and you may use the 20 | # software solely pursuant to the terms of the relevant commercial agreement. 21 | 22 | function print_green() { 23 | echo -e "\033[2;32m$1\033[0m" 24 | } 25 | function print_red() { 26 | echo -e "\033[1;31m$1\033[0m" 27 | } 28 | 29 | # check if everything is committed 30 | CLEAN=`git status -s` 31 | if [ ! -z "$CLEAN" ] 32 | then 33 | print_red "Working directory not clean. Please commit all changes before tagging!" 34 | echo "Aborting." 35 | exit 1 36 | fi 37 | 38 | echo "Fetching origin..." 39 | git fetch origin > /dev/null 40 | 41 | # get current branc 42 | BRANCH=`git branch | grep "^*" | cut -d " " -f 2` 43 | echo "Current branch is $BRANCH." 44 | 45 | # check if BRANCH == origin/BRANCH 46 | LOCAL_COMMIT=$(git log --pretty=oneline -n 1 ${BRANCH} | cat) 47 | ORIGIN_COMMIT=$(git log --pretty=oneline -n 1 origin/${BRANCH} | cat) 48 | 49 | if [ "$LOCAL_COMMIT" != "$ORIGIN_COMMIT" ] 50 | then 51 | print_red "Local $BRANCH is not up to date." 52 | echo "Local commit: $LOCAL_COMMIT" 53 | echo "Origin commit: $ORIGIN_COMMIT" 54 | echo "Aborting." 55 | exit 1 56 | fi 57 | 58 | # get version from Driver class 59 | VERSION=$(grep "\ for more 27 | # information. 28 | 29 | # This file is taken from the demo Sphinx project available at 30 | # . This demo docs project 31 | # provides a reference implementation that should be copied for all Sphinx 32 | # projects at Crate.io. 33 | 34 | # The Crate Docs build system works by centralizing its core components in the 35 | # `crate-docs` repository. At build time, this Makefile creates a copy of the 36 | # this project under the `.crate-docs` directory. This Makefile is an interface 37 | # for those core components. 38 | 39 | 40 | # Upgraded instructions 41 | # ----------------------------------------------------------------------------- 42 | 43 | # You must pin your Sphinx project to a specific version of the Crate Docs 44 | # project. You can do this by editing the `build.json` file. Change the JSON 45 | # `message` value to the desired release number. A list of releases is 46 | # available at . 47 | 48 | # Although care has been taken to restrict changes to the core components, you 49 | # may occasionally need to update your project to match the reference 50 | # implementation. Check the release notes for any special instructions. 51 | 52 | 53 | # Project-specific customization 54 | # ============================================================================= 55 | 56 | # If you want to customize the build system for your Sphinx project, add lines 57 | # to this section. 58 | 59 | 60 | # Build system integration 61 | # ============================================================================= 62 | 63 | # This is a boilerplate section is required for integration with the Crate Docs 64 | # build system. All Sphinx projects using a the same Crate Docs version 65 | # should have exactly the same boilerplate. 66 | 67 | # IF YOU ARE EDITING THIS FILE IN A REAL SPHINX PROJECT, YOU SHOULD NOT MAKE 68 | # ANY CHANGES TO THIS SECTION. 69 | 70 | # If you want to make changes to the boilerplate, please make a pull request on 71 | # the demo Sphinx project in the Crate Docs repository available at 72 | # . 73 | 74 | .EXPORT_ALL_VARIABLES: 75 | 76 | DOCS_DIR := docs 77 | TOP_DIR := .. 78 | BUILD_JSON := build.json 79 | BUILD_REPO := https://github.com/crate/crate-docs.git 80 | LATEST_BUILD := https://github.com/crate/crate-docs/releases/latest 81 | CLONE_DIR := .crate-docs 82 | SRC_DIR := $(CLONE_DIR)/common-build 83 | SELF_SRC := $(TOP_DIR)/common-build 84 | SELF_MAKEFILE := $(SELF_SRC)/rules.mk 85 | SRC_MAKE := $(MAKE) -f $(SRC_DIR)/rules.mk 86 | 87 | # Parse the JSON file 88 | BUILD_VERSION := $(shell cat $(BUILD_JSON) | \ 89 | python -c 'import json, sys; print(json.load(sys.stdin)["message"])') 90 | 91 | ifeq ($(BUILD_VERSION),) 92 | $(error No build version specified in `$(BUILD_JSON)`.) 93 | endif 94 | 95 | # This is a non-essential check so we timeout after only two seconds so as not 96 | # to frustrate the user when there are network issues 97 | LATEST_VERSION := $(shell curl -sI --connect-timeout 2 '$(LATEST_BUILD)' | \ 98 | grep -i 'Location:' | grep -Eoh '[^/]+$$') 99 | 100 | # Skip if no version could be determined (i.e., because of a network error) 101 | ifneq ($(LATEST_VERSION),) 102 | # Only issue a warning if there is a version mismatch 103 | ifneq ($(BUILD_VERSION),$(LATEST_VERSION)) 104 | define version_warning 105 | You are using Crate Docs version $(BUILD_VERSION), however version \ 106 | $(LATEST_VERSION) is available. You should consider upgrading. Follow the \ 107 | instructions in `Makefile` and then run `make reset`. 108 | endef 109 | endif 110 | endif 111 | 112 | # Default rule 113 | .PHONY: help 114 | help: $(CLONE_DIR) 115 | @ $(MAKE) version-warn 116 | @ $(SRC_MAKE) $@ 117 | 118 | .PHONY: version-warn 119 | version-warn: 120 | @ # Because version numbers may vary in length, we must wrap the warning 121 | @ # message at run time to be sure of correct output 122 | @ if test -n '$(version_warning)'; then \ 123 | printf '\033[33m'; \ 124 | echo '$(version_warning)' | fold -w 79 -s; \ 125 | printf '\033[00m\n'; \ 126 | fi 127 | 128 | ifneq ($(wildcard $(SELF_MAKEFILE)),) 129 | # The project detects itself and fakes an install of its own core build rules 130 | # so that it can test itself 131 | $(CLONE_DIR): 132 | @ printf '\033[1mInstalling the build system...\033[00m\n' 133 | @ mkdir -p $@ 134 | @ cp -R $(SELF_SRC) $(SRC_DIR) 135 | @ printf 'Created: $(CLONE_DIR)\n' 136 | else 137 | # All other projects install a versioned copy of the core build rules 138 | $(CLONE_DIR): 139 | @ printf '\033[1mInstalling the build system...\033[00m\n' 140 | @ git clone --depth=1 -c advice.detachedHead=false \ 141 | --branch=$(BUILD_VERSION) $(BUILD_REPO) $(CLONE_DIR) 142 | @ printf 'Created: $(CLONE_DIR)\n' 143 | endif 144 | 145 | # Don't pass through this target 146 | .PHONY: Makefile 147 | Makefile: 148 | 149 | # By default, pass targets through to the core build rules 150 | .PHONY: 151 | %: $(CLONE_DIR) 152 | @ $(MAKE) version-warn 153 | @ $(SRC_MAKE) $@ 154 | 155 | .PHONY: reset 156 | reset: 157 | @ printf '\033[1mResetting the build system...\033[00m\n' 158 | @ rm -rf $(CLONE_DIR) 159 | @ printf 'Removed: $(CLONE_DIR)\n' 160 | -------------------------------------------------------------------------------- /docs/_extra/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | 4 | Sitemap: https://cratedb.com/docs/dbal/en/latest/site.xml 5 | -------------------------------------------------------------------------------- /docs/appendices/compatibility.rst: -------------------------------------------------------------------------------- 1 | .. _compatibility: 2 | 3 | ============= 4 | Compatibility 5 | ============= 6 | 7 | .. rubric:: Table of contents 8 | 9 | .. contents:: 10 | :local: 11 | 12 | .. _versions: 13 | 14 | Version notes 15 | ============= 16 | 17 | .. _cratedb-versions: 18 | 19 | CrateDB 20 | ------- 21 | 22 | Consult the following table for CrateDB version compatibility notes: 23 | 24 | +----------------+-----------------+-------------------------------------------+ 25 | | Driver Version | CrateDB Version | Notes | 26 | +================+=================+===========================================+ 27 | | Any | >= 2.1.x | Client needs to connect with a valid | 28 | | | | database user to access CrateDB. | 29 | | | | | 30 | | | | The default CrateDB user is ``crate`` and | 31 | | | | has no password is set. | 32 | | | | | 33 | +----------------+-----------------+-------------------------------------------+ 34 | | >= 1.0 | >= 2.3.x | | 35 | +----------------+-----------------+-------------------------------------------+ 36 | | >= 2.0 | >= 4.0.x | | 37 | +----------------+-----------------+-------------------------------------------+ 38 | 39 | .. _implementations: 40 | 41 | Implementation notes 42 | ==================== 43 | 44 | .. _dbal-implementation: 45 | 46 | DBAL 47 | ---- 48 | 49 | +----------------+----------------------------------------------+------------+ 50 | | Driver Version | Feature | Notes | 51 | +================+==============================================+============+ 52 | | >= 0.2.0 | HTTP basic auth. | Supported. | 53 | +----------------+----------------------------------------------+------------+ 54 | | >= 0.2.1 | PHP 7. | Supported. | 55 | +----------------+----------------------------------------------+------------+ 56 | | >= 0.3 | Exposed PDO methods: | Supported. | 57 | | | | | 58 | | | - ``getServerVersion()`` | | 59 | | | - ``getServerInfo()`` | | 60 | + +----------------------------------------------+------------+ 61 | | | Connection to multiple CrateDB nodes. | Supported. | 62 | + +----------------------------------------------+------------+ 63 | | | Default schema selection. | Supported. | 64 | + +----------------------------------------------+------------+ 65 | | | CrateDB SQL features: | Supported. | 66 | | | | | 67 | | | - Joins (with or without a `query builder`_) | | 68 | | | - Fulltext indexes (without a query builder) | | 69 | +----------------+----------------------------------------------+------------+ 70 | | >= 1.1 | [BREAKING]: PDO library requires PHP 7.2 | Supported. | 71 | + +----------------------------------------------+------------+ 72 | | | Added SSL support | Supported. | 73 | + +----------------------------------------------+------------+ 74 | | | Based on Doctrine 2.6.3 | Supported. | 75 | +----------------+----------------------------------------------+------------+ 76 | | >= 2.0 | Based on Doctrine 2.9 | Supported. | 77 | +----------------+----------------------------------------------+------------+ 78 | | >= 2.2 | Based on Doctrine 2.10 | Supported. | 79 | +----------------+----------------------------------------------+------------+ 80 | 81 | .. _query builder: https://www.doctrine-project.org/projects/doctrine-dbal/en/3.0/reference/query-builder.html#join-clauses 82 | -------------------------------------------------------------------------------- /docs/appendices/data-types.rst: -------------------------------------------------------------------------------- 1 | .. _data-types: 2 | 3 | ========== 4 | Data types 5 | ========== 6 | 7 | .. _type-map: 8 | 9 | Type map 10 | ======== 11 | 12 | This driver maps CrateDB types to the following PHP types: 13 | 14 | .. csv-table:: 15 | :header: "CrateDB Type", "PHP Type" 16 | 17 | "`boolean`__", "`boolean`__" 18 | "`byte`__", "`integer`__" 19 | "`short`__", "`integer`__" 20 | "`integer`__", "`integer`__", 21 | "`long`__", "`string`__" 22 | "`float`__", "`float`__" 23 | "`double`__", "`float`__" 24 | "`string`__", "`string`__" 25 | "`ip`__", "`string`__" 26 | "`timestamp`__", "`DateTime`__" 27 | "`geo_point`__", "`array`__" 28 | "`geo_shape`__", "`array`__" 29 | "`object`__", "`array`__" 30 | "`array`__", "`array`__" 31 | 32 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#boolean 33 | __ https://www.php.net/manual/en/language.types.boolean.php 34 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#numeric-data 35 | __ https://www.php.net/manual/en/language.types.integer.php 36 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#numeric-data 37 | __ https://www.php.net/manual/en/language.types.integer.php 38 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#numeric-data 39 | __ https://www.php.net/manual/en/language.types.integer.php 40 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#numeric-data 41 | __ https://www.php.net/manual/en/language.types.string.php 42 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#numeric-data 43 | __ https://www.php.net/manual/en/language.types.float.php 44 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#numeric-data 45 | __ https://www.php.net/manual/en/language.types.float.php 46 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#character-data 47 | __ https://www.php.net/manual/en/language.types.string.php 48 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#ip 49 | __ https://www.php.net/manual/en/language.types.string.php 50 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#dates-and-times 51 | __ https://www.php.net/manual/en/class.datetime.php 52 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#geo-point 53 | __ https://www.php.net/manual/en/language.types.array.php 54 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#geo-shape 55 | __ https://www.php.net/manual/en/language.types.array.php 56 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#object 57 | __ https://www.php.net/manual/en/language.types.array.php 58 | __ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#array 59 | __ https://www.php.net/manual/en/language.types.array.php 60 | 61 | .. _column-type-definitions: 62 | 63 | Column type definitions 64 | ======================= 65 | 66 | When defining CrateDB `timestamp`_, `object`_ or `array`_ type columns, you 67 | must construct the DBAL column definition programatically, using the types and 68 | attributes provided by the CrateDB DBAL driver. 69 | 70 | Primitive column types (e.g. string, integer, and so on) can be defined in 71 | the regular DBAL way. 72 | 73 | The custom type objects provided by the CrateDB DBAL driver are: 74 | 75 | - `TimestampType`_ 76 | - `MapType`_ 77 | - `ArrayType`_ 78 | 79 | Here's an example of how the ``MapType`` can be used: 80 | 81 | .. code-block:: php 82 | 83 | use Doctrine\DBAL\Schema\Column; 84 | use Doctrine\DBAL\Schema\Table; 85 | use Doctrine\DBAL\Types\Type; 86 | use Crate\DBAL\Types\MapType; 87 | 88 | $table = new Table('test_table'); 89 | $objDefinition = array( 90 | 'type' => MapType::STRICT, 91 | 'fields' => array( 92 | new Column('id', Type::getType('integer'), array()), 93 | new Column('name', Type::getType('string'), array()), 94 | ), 95 | ); 96 | $table->addColumn( 97 | 'object_column', MapType::NAME, 98 | array('platformOptions'=>$objDefinition)); 99 | $schemaManager->createTable($table); 100 | 101 | Here, the ``MapType`` class being used to model a CrateDB object. Standard DBAL 102 | types, like ``string``, are being used to construct the schema of the object, 103 | via calls to the the use of the ``Column`` class and calls to the 104 | ``Type::getType`` static method, and so on. 105 | 106 | .. SEEALSO:: 107 | 108 | The Doctrine `ORM documentation`_ has more about type mapping. 109 | 110 | .. _array: https://crate.io/docs/crate/reference/en/latest/general/ddl/data-types.html#array 111 | .. _ArrayType: https://github.com/crate/crate-dbal/blob/main/src/Crate/DBAL/Types/ArrayType.php 112 | .. _MapType: https://github.com/crate/crate-dbal/blob/main/src/Crate/DBAL/Types/MapType.php 113 | .. _object: https://crate.io/docs/crate/reference/en/latest/general/ddl/data-types.html#object 114 | .. _ORM documentation: https://www.doctrine-project.org/projects/doctrine-orm/en/3.0/reference/basic-mapping.html 115 | .. _timestamp: https://crate.io/docs/crate/reference/en/latest/general/ddl/data-types.html#dates-and-times 116 | .. _TimestampType: https://github.com/crate/crate-dbal/blob/main/src/Crate/DBAL/Types/TimestampType.php 117 | -------------------------------------------------------------------------------- /docs/appendices/index.rst: -------------------------------------------------------------------------------- 1 | .. _appendices: 2 | 3 | ========== 4 | Appendices 5 | ========== 6 | 7 | Supplementary information for the CrateDB DBAL driver. 8 | 9 | .. rubric:: Table of contents 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | table-options 15 | data-types 16 | compatibility 17 | -------------------------------------------------------------------------------- /docs/appendices/table-options.rst: -------------------------------------------------------------------------------- 1 | .. _table-options: 2 | 3 | ================================ 4 | CrateDB specific table options 5 | ================================ 6 | 7 | CrateDB supports a custom ``CREATE TABLE`` syntax for adjusting the table 8 | configuration incl. ``SHARDING`` and ``PARTITIONING`` of the table. 9 | See `CrateDB CREATE TABLE Documentation`_ for all supported configuration 10 | options. 11 | 12 | All CrateDB specific table options must be passed in using the ``$options`` 13 | argument of the DBAL ``Table()`` constructor. 14 | 15 | Example: 16 | 17 | .. code-block:: php 18 | 19 | $options = []; 20 | $options['sharding_shards'] = 5; 21 | $myTable = new Table('my_table', [], [], [], 0, $options); 22 | 23 | 24 | Sharding Options 25 | ================ 26 | 27 | Following table options can used to adjust the sharding of a table. 28 | See also `CrateDB Sharding Documentation`_. 29 | 30 | .. list-table:: 31 | :header-rows: 1 32 | 33 | * - Name 34 | - Description 35 | * - ``$options['sharding_num_shards']`` 36 | - Specifies the number of shards a table is stored in. Must be greater than 0. 37 | * - ``$options['sharding_routing_column']`` 38 | - Allows to explicitly specify a column or field on which rows are sharded by. 39 | 40 | 41 | Partitioning Options 42 | ==================== 43 | 44 | Following table options can used to define the partitioning columns of a table. 45 | See also `CrateDB Partitioned Tables Documentation`_. 46 | 47 | 48 | .. list-table:: 49 | :header-rows: 1 50 | 51 | * - Name 52 | - Description 53 | * - ``$options['partition_columns']`` 54 | - Specifies the columns on which partitions should be created on. 55 | 56 | General Options 57 | =============== 58 | 59 | All general CrateDB specific table options used inside the ``WITH (...)`` clause 60 | can be configured under the ``table_options`` option key. 61 | 62 | Example on how to adjust the replicas: 63 | 64 | .. code-block:: php 65 | 66 | $options = []; 67 | $options['table_options'] = []; 68 | $options['table_options']['number_of_replicas'] = '2'; 69 | $myTable = new Table('my_table', [], [], [], 0, $options); 70 | 71 | 72 | .. _CrateDB CREATE TABLE Documentation: https://cratedb.com/docs/crate/reference/en/latest/sql/statements/create-table.html 73 | .. _CrateDB Sharding Documentation: https://cratedb.com/docs/crate/reference/en/latest/general/ddl/sharding.html 74 | .. _CrateDB Partitioned Tables Documentation: https://cratedb.com/docs/crate/reference/en/latest/general/ddl/partitioned-tables.html 75 | -------------------------------------------------------------------------------- /docs/build.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "label": "docs build", 4 | "message": "2.1.2" 5 | } 6 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | from crate.theme.rtd.conf.dbal import * 2 | 3 | from sphinx.highlighting import lexers 4 | from pygments.lexers.web import PhpLexer 5 | lexers["php"] = PhpLexer(startinline=True, linenos=1) 6 | lexers["php-annotations"] = PhpLexer(startinline=True, linenos=1) 7 | # primary_domain = "php" 8 | 9 | # Disable version chooser. 10 | html_context.update({ 11 | "display_version": False, 12 | "current_version": None, 13 | "versions": [], 14 | }) 15 | -------------------------------------------------------------------------------- /docs/connect.rst: -------------------------------------------------------------------------------- 1 | .. _connect: 2 | 3 | ================== 4 | Connect to CrateDB 5 | ================== 6 | 7 | .. rubric:: Table of contents 8 | 9 | .. contents:: 10 | :local: 11 | 12 | Authentication 13 | ============== 14 | 15 | .. NOTE:: 16 | 17 | These examples authenticate as ``crate``, the default database user in 18 | CrateDB versions 2.1.x and later. 19 | 20 | If you are using CrateDB 2.1.x or later, you must supply a username. If you 21 | are using earlier versions of CrateDB, this parameter is not supported. 22 | 23 | See the :ref:`compatibility notes ` for more information. 24 | 25 | If you have not configured a custom `database user`_, you probably want to 26 | authenticate as the CrateDB superuser, which is ``crate``. The superuser 27 | does not have a password, so you can omit the ``password`` argument. 28 | 29 | DBAL 30 | ==== 31 | 32 | If you plan to query CrateDB via DBAL, you can get a connection from the 33 | ``DriverManager`` class using `standard DBAL parameters`_, like so: 34 | 35 | .. code-block:: php 36 | 37 | $params = array( 38 | 'driverClass' => 'Crate\DBAL\Driver\PDOCrate\Driver', 39 | 'user' => 'crate', 40 | 'host' => 'localhost', 41 | 'port' => 4200 42 | ); 43 | $connection = \Doctrine\DBAL\DriverManager::getConnection($params); 44 | $schemaManager = $connection->getSchemaManager(); 45 | 46 | With these connection parameters, the ``DriverManager`` will attempt to 47 | authenticate as ``crate`` with a CrateDB node listening on ``localhost:4200``. 48 | 49 | .. SEEALSO:: 50 | 51 | For more help using DBAL_, consult the `DBAL documentation`_. 52 | 53 | Doctrine ORM 54 | ============ 55 | 56 | If you want to use the `Object-Relational Mapping`_ (ORM) features of Doctrine, 57 | you must set up an ``EntityManager`` instance. 58 | 59 | Here's a slightly modified version of the `Doctrine provided example`_: 60 | 61 | .. code-block:: php 62 | 63 | use Doctrine\ORM\Tools\Setup; 64 | use Doctrine\ORM\EntityManager; 65 | 66 | $paths = array("/path/to/entity-files"); 67 | $isDevMode = false; 68 | 69 | // the connection configuration 70 | $params = array( 71 | 'driverClass' => 'Crate\DBAL\Driver\PDOCrate\Driver', 72 | 'user' => 'crate' 73 | 'host' => 'localhost', 74 | 'port' => 4200 75 | ); 76 | 77 | $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode); 78 | $entityManager = EntityManager::create($params, $config); 79 | 80 | Here's what we changed in the above example: 81 | 82 | - Specified the CrateDB driver class 83 | - Specified the ``crate`` user 84 | - Configured the connection for ``localhost:4200`` 85 | 86 | .. SEEALSO:: 87 | 88 | The CrateDB DBAL driver provides three custom type objects. Consult the 89 | :ref:`data types ` appendix for more information about type 90 | maps and column type definitions. 91 | 92 | Next steps 93 | ========== 94 | 95 | Use the standard the `DBAL documentation`_ or `Doctrine ORM documentation`_ for the rest of 96 | your setup process. 97 | 98 | .. _database user: https://cratedb.com/docs/crate/reference/en/latest/admin/user-management.html 99 | .. _DBAL: https://www.doctrine-project.org/projects/dbal.html 100 | .. _DBAL documentation: https://www.doctrine-project.org/projects/doctrine-dbal/en/3.0/index.html 101 | .. _Doctrine provided example: https://www.doctrine-project.org/projects/doctrine-orm/en/3.0/reference/configuration.html#obtaining-an-entitymanager 102 | .. _Object-Relational Mapping: https://www.doctrine-project.org/projects/orm.html 103 | .. _Doctrine ORM documentation: https://www.doctrine-project.org/projects/doctrine-orm/en/3.0/index.html 104 | .. _standard DBAL parameters: https://www.doctrine-project.org/projects/doctrine-dbal/en/3.0/reference/configuration.html 105 | -------------------------------------------------------------------------------- /docs/docutils.conf: -------------------------------------------------------------------------------- 1 | [html4css1 writer] 2 | field_name_limit: 40 3 | -------------------------------------------------------------------------------- /docs/getting-started.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Getting started 3 | =============== 4 | 5 | Learn how to install and get started with the :ref:`CrateDB DBAL driver 6 | `. 7 | 8 | .. rubric:: Table of contents 9 | 10 | .. contents:: 11 | :local: 12 | 13 | Prerequisites 14 | ============= 15 | 16 | Your project must be using `Composer`_. 17 | 18 | Set up as a dependency 19 | ====================== 20 | 21 | The driver is available as a package at `crate/crate-dbal`_. 22 | 23 | Add the driver package to your project's `composer.json`_:: 24 | 25 | composer require crate/crate-dbal 26 | 27 | If you're using `Doctrine ORM`_, you should add the ``doctrine/orm`` dependency 28 | too:: 29 | 30 | composer require doctrine/orm 31 | 32 | 33 | Install 34 | ======= 35 | 36 | Once the package has been configured as a dependency, you can install it, like:: 37 | 38 | composer install 39 | 40 | Afterwards, if you are not already doing so, you must require the Composer 41 | `autoload.php`_ file. You can do this by adding a line like this to your PHP 42 | application: 43 | 44 | .. code-block:: php 45 | 46 | require __DIR__ . '/vendor/autoload.php'; 47 | 48 | .. SEEALSO:: 49 | 50 | For more help with Composer, consult the `Composer documentation`_. 51 | 52 | Next steps 53 | ========== 54 | 55 | Learn how to :ref:`connect to CrateDB `. 56 | 57 | .. _autoload.php: https://getcomposer.org/doc/01-basic-usage.md#autoloading 58 | .. _Composer documentation: https://getcomposer.org 59 | .. _Composer: https://getcomposer.org/ 60 | .. _composer.json: https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup 61 | .. _crate/crate-dbal: https://packagist.org/packages/crate/crate-dbal 62 | .. _Doctrine ORM: https://www.doctrine-project.org/projects/orm.html 63 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. _index: 2 | 3 | =================== 4 | CrateDB DBAL Driver 5 | =================== 6 | 7 | A `DBAL`_ driver for `CrateDB`_. 8 | 9 | DBAL is a PHP database abstraction layer that comes with database schema 10 | introspection, schema management, and `PDO`_ support. 11 | 12 | This driver also works with `Doctrine ORM`_, an `Object-Relational Mapper`_. 13 | 14 | .. NOTE:: 15 | 16 | This is a basic CrateDB driver reference. 17 | 18 | For general help setting up and using Doctrine software, please consult the 19 | `DBAL documentation`_ or the `Doctrine ORM documentation`_. 20 | 21 | .. SEEALSO:: 22 | 23 | The CrateDB PHP DBAL driver is an open source project and is `hosted on 24 | GitHub`_. 25 | 26 | .. rubric:: Table of contents 27 | 28 | .. toctree:: 29 | :maxdepth: 2 30 | 31 | getting-started 32 | connect 33 | appendices/index 34 | 35 | 36 | .. _CrateDB: https://cratedb.com/product 37 | .. _DBAL documentation: https://www.doctrine-project.org/projects/doctrine-dbal/en/3.0/index.html 38 | .. _DBAL: https://www.doctrine-project.org/projects/dbal.html 39 | .. _Doctrine ORM: https://www.doctrine-project.org/projects/orm.html 40 | .. _Doctrine ORM documentation: https://www.doctrine-project.org/projects/doctrine-orm/en/3.0/index.html 41 | .. _hosted on GitHub: https://github.com/crate/crate-dbal 42 | .. _Object-Relational Mapper: https://www.doctrine-project.org/projects/orm.html 43 | .. _open source: https://en.wikipedia.org/wiki/Open-source_software 44 | .. _PDO: https://www.php.net/manual/en/intro.pdo.php 45 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | crate-docs-theme 2 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | ./test/Crate/Test 15 | 16 | 17 | 18 | ./src 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /provisioning.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export DEBIAN_FRONTEND=noninteractive 3 | apt-get update 4 | apt-get upgrade 5 | add-apt-repository -y ppa:ondrej/php 6 | wget https://cdn.crate.io/downloads/deb/DEB-GPG-KEY-crate 7 | apt-key add DEB-GPG-KEY-crate 8 | . /etc/os-release 9 | echo "deb https://cdn.crate.io/downloads/deb/stable/ $UBUNTU_CODENAME main" > /etc/apt/sources.list.d/crate-stable-$UBUNTU_CODENAME.list 10 | apt-get update 11 | # test classes of DBAL which we depend on are only available inside the source, so DON'T install zip|unzip to force 12 | # composer to install depencenies from source 13 | apt-get install -y crate php7.3-cli php7.3-xml php7.3-curl php7.3-mbstring php7.3-xdebug 14 | curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer 15 | cd /vagrant && su vagrant -c 'composer install' -------------------------------------------------------------------------------- /src/Crate/DBAL/Driver/PDOCrate/CrateStatement.php: -------------------------------------------------------------------------------- 1 | constructPdoDsn($params), $username, $password, $driverOptions); 46 | } 47 | 48 | /** 49 | * Constructs the Crate PDO DSN. 50 | * 51 | * @return string The DSN. 52 | */ 53 | private function constructPdoDsn(array $params) 54 | { 55 | $dsn = self::NAME . ':'; 56 | if (isset($params['host']) && $params['host'] != '') { 57 | $dsn .= $params['host']; 58 | } else { 59 | $dsn .= 'localhost'; 60 | } 61 | $dsn .= ':' . (isset($params['port']) ? $params['port'] : '4200'); 62 | 63 | return $dsn; 64 | } 65 | 66 | /** 67 | * {@inheritDoc} 68 | */ 69 | public function getDatabasePlatform() 70 | { 71 | return new CratePlatform(); 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | public function getSchemaManager(Connection $conn) 78 | { 79 | return new CrateSchemaManager($conn); 80 | } 81 | 82 | /** 83 | * {@inheritDoc} 84 | */ 85 | public function getName() 86 | { 87 | return self::NAME; 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | */ 93 | public function getDatabase(Connection $conn) 94 | { 95 | return null; 96 | } 97 | 98 | /** 99 | * {@inheritDoc} 100 | */ 101 | public function createDatabasePlatformForVersion($version) 102 | { 103 | if (version_compare($version, self::VERSION_057, "<")) { 104 | return new CratePlatform(); 105 | } elseif (version_compare($version, self::VERSION_4, "<")) { 106 | return new CratePlatform1(); 107 | } else { 108 | return new CratePlatform4(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Crate/DBAL/Driver/PDOCrate/PDOConnection.php: -------------------------------------------------------------------------------- 1 | setAttribute(PDO::ATTR_STATEMENT_CLASS, CrateStatement::class); 40 | $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 41 | } 42 | 43 | /** 44 | * Checks whether a query is required to retrieve the database server version. 45 | * 46 | * @return boolean True if a query is required to retrieve the database server version, false otherwise. 47 | */ 48 | public function requiresQueryForServerVersion() 49 | { 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Crate/DBAL/Platforms/CratePlatform1.php: -------------------------------------------------------------------------------- 1 | doctrineTypeMapping = array( 33 | 'integer' => 'integer', 34 | 'int' => 'integer', 35 | 'bool' => 'boolean', 36 | 'boolean' => 'boolean', 37 | 'timestamp' => 'timestamp', 38 | 39 | 'object' => 'map', 40 | 'array' => 'array', 41 | 42 | // postgresql compatible type names, default for CrateDB >= 4.0 43 | 'text' => 'string', 44 | 'char' => 'string', 45 | 'smallint' => 'smallint', 46 | 'bigint' => 'bigint', 47 | 'real' => 'float', 48 | 'double precision' => 'float', 49 | 'timestamp without time zone' => 'timestamp', 50 | 'timestamp with time zone' => 'timestamp', 51 | ); 52 | } 53 | 54 | /** 55 | * {@inheritDoc} 56 | */ 57 | public function getBigIntTypeDeclarationSQL(array $field) 58 | { 59 | return 'BIGINT'; 60 | } 61 | 62 | /** 63 | * {@inheritDoc} 64 | */ 65 | public function getSmallIntTypeDeclarationSQL(array $field) 66 | { 67 | return 'SMALLINT'; 68 | } 69 | 70 | /** 71 | * {@inheritDoc} 72 | */ 73 | public function getFloatDeclarationSQL(array $field) 74 | { 75 | return 'DOUBLE PRECISION'; 76 | } 77 | 78 | /** 79 | * {@inheritDoc} 80 | */ 81 | protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) 82 | { 83 | return 'TEXT'; 84 | } 85 | 86 | /** 87 | * {@inheritDoc} 88 | */ 89 | public function getClobTypeDeclarationSQL(array $field) 90 | { 91 | return 'TEXT'; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Crate/DBAL/Platforms/Keywords/CrateKeywords.php: -------------------------------------------------------------------------------- 1 | $row['constraint_name'], 42 | 'column_name' => $row['column_name'], 43 | 'non_unique' => ! $isPrimary, 44 | 'primary' => $isPrimary, 45 | 'where' => '', 46 | ]; 47 | } 48 | 49 | return parent::_getPortableTableIndexesList($buffer, $tableName); 50 | } 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | protected function _getPortableTableColumnDefinition($tableColumn) 55 | { 56 | $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); 57 | 58 | if (!isset($tableColumn['column_name'])) { 59 | $tableColumn['column_name'] = ''; 60 | } 61 | if (!isset($tableColumn['is_nullable'])) { 62 | $tableColumn['is_nullable'] = true; 63 | } 64 | 65 | $dbType = strtolower($tableColumn['data_type']); 66 | $type = $this->_platform->getDoctrineTypeMapping($dbType); 67 | 68 | $options = array( 69 | 'length' => null, 70 | 'notnull' => ! $tableColumn['is_nullable'], 71 | 'default' => null, 72 | 'precision' => null, 73 | 'scale' => null, 74 | 'fixed' => null, 75 | 'unsigned' => false, 76 | 'autoincrement' => false, 77 | 'comment' => '', 78 | ); 79 | 80 | return new Column($tableColumn['column_name'], Type::getType($type), $options); 81 | } 82 | 83 | /** 84 | * {@inheritDoc} 85 | */ 86 | protected function _getPortableTablesList($tables) 87 | { 88 | $tableNames = array(); 89 | foreach ($tables as $tableRow) { 90 | $tableRow = array_change_key_case($tableRow, \CASE_LOWER); 91 | $tableNames[] = $tableRow['table_name']; // ignore schema for now 92 | } 93 | 94 | return $tableNames; 95 | } 96 | 97 | /** 98 | * Flattens a multidimensional array into a 1 dimensional array, where 99 | * keys are concatinated with '.' 100 | * 101 | * @return array 102 | */ 103 | private function flatten(array $array, string $prefix = '') : array 104 | { 105 | $result = array(); 106 | foreach ($array as $key => $value) { 107 | if (is_array($value)) { 108 | $result = $result + self::flatten($value, $prefix . $key . '.'); 109 | } else { 110 | $result[$prefix . $key] = $value; 111 | } 112 | } 113 | return $result; 114 | } 115 | 116 | /** 117 | * {@inheritDoc} 118 | */ 119 | public function listTableDetails($tableName) : Table 120 | { 121 | $columns = $this->listTableColumns($tableName); 122 | $indexes = $this->listTableIndexes($tableName); 123 | $options = []; 124 | 125 | $s = $this->_conn->fetchAssoc($this->_platform->getTableOptionsSQL($tableName)); 126 | 127 | $options['sharding_routing_column'] = $s['clustered_by']; 128 | $options['sharding_num_shards'] = $s['number_of_shards']; 129 | $options['partition_columns'] = $s['partitioned_by']; 130 | $options['table_options'] = self::flatten($s['settings']); 131 | $options['table_options']['number_of_replicas'] = $s['number_of_replicas']; 132 | $options['table_options']['column_policy'] = $s['column_policy']; 133 | return new Table($tableName, $columns, $indexes, [], [], $options); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Crate/DBAL/Types/ArrayType.php: -------------------------------------------------------------------------------- 1 | 0 && !(array_keys($value) === range(0, count($value) - 1)))) { 62 | return null; 63 | } 64 | return $value; 65 | } 66 | 67 | public function convertToPHPValue($value, AbstractPlatform $platform) 68 | { 69 | return $value; 70 | } 71 | 72 | /** 73 | * Gets the SQL declaration snippet for a field of this type. 74 | * 75 | * @param array $fieldDeclaration The field declaration. 76 | * @param AbstractPlatform $platform The currently used database platform. 77 | * @return string 78 | * @throws \Doctrine\DBAL\DBALException 79 | */ 80 | public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) 81 | { 82 | $options = !array_key_exists('platformOptions', $fieldDeclaration) ? 83 | array() : $fieldDeclaration['platformOptions']; 84 | return $this->getArrayTypeDeclarationSQL($platform, $fieldDeclaration, $options); 85 | } 86 | 87 | /** 88 | * Gets the SQL snippet used to declare an ARRAY column type. 89 | * 90 | * @param AbstractPlatform $platform 91 | * @param array $field 92 | * 93 | * @param array $options 94 | * @return string 95 | * @throws \Doctrine\DBAL\DBALException 96 | */ 97 | public function getArrayTypeDeclarationSQL(AbstractPlatform $platform, array $field, array $options) 98 | { 99 | $type = array_key_exists('type', $options) ? $options['type'] : Type::STRING; 100 | return 'ARRAY ( ' . Type::getType($type)->getSQLDeclaration($field, $platform) . ' )'; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Crate/DBAL/Types/MapType.php: -------------------------------------------------------------------------------- 1 | 0 && !(array_keys($value) !== range(0, count($value) - 1)))) { 68 | return null; 69 | } 70 | 71 | return $value; 72 | } 73 | 74 | public function convertToPHPValue($value, AbstractPlatform $platform) 75 | { 76 | return $value == null ?: (array) $value; 77 | } 78 | 79 | /** 80 | * Gets the SQL declaration snippet for a field of this type. 81 | * 82 | * @param array $fieldDeclaration The field declaration. 83 | * @param AbstractPlatform $platform The currently used database platform. 84 | * @return string 85 | * @throws \Doctrine\DBAL\DBALException 86 | */ 87 | public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) 88 | { 89 | $options = !array_key_exists('platformOptions', $fieldDeclaration) ? 90 | array() : $fieldDeclaration['platformOptions']; 91 | 92 | return $this->getMapTypeDeclarationSQL($platform, $fieldDeclaration, $options); 93 | } 94 | 95 | /** 96 | * Gets the SQL snippet used to declare an OBJECT column type. 97 | * 98 | * @param array $field 99 | * 100 | * @return string 101 | * @throws \Doctrine\DBAL\DBALException 102 | */ 103 | public function getMapTypeDeclarationSQL(AbstractPlatform $platform, array $field, array $options) 104 | { 105 | $type = array_key_exists('type', $options) ? $options['type'] : MapType::DYNAMIC; 106 | 107 | $fields = array_key_exists('fields', $options) ? $options['fields'] : array(); 108 | $columns = array(); 109 | foreach ($fields as $field) { 110 | $columns[$field->getQuotedName($platform)] = CratePlatform::prepareColumnData($platform, $field); 111 | } 112 | $objectFields = $platform->getColumnDeclarationListSQL($columns); 113 | 114 | $declaration = count($columns) > 0 ? ' AS ( ' . $objectFields . ' )' : ''; 115 | return 'OBJECT ( ' . $type . ' )' . $declaration ; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Crate/DBAL/Types/TimestampType.php: -------------------------------------------------------------------------------- 1 | getTimestamp()*self::S_TO_MS : null; 51 | } 52 | 53 | public function convertToPHPValue($value, AbstractPlatform $platform) 54 | { 55 | if ($value === null || $value instanceof DateTime) { 56 | return $value; 57 | } 58 | 59 | if (!is_int($value)) { 60 | return null; 61 | } 62 | 63 | $val = new DateTime(); 64 | $val->setTimestamp($value/self::S_TO_MS); 65 | 66 | return $val; 67 | } 68 | 69 | /** 70 | * Gets the SQL declaration snippet for a field of this type. 71 | * 72 | * @return string 73 | * @param array $fieldDeclaration The field declaration. 74 | * @param AbstractPlatform $platform The currently used database platform. 75 | */ 76 | public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) 77 | { 78 | return $platform->getDateTimeTypeDeclarationSQL($fieldDeclaration); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/DBALFunctionalTestCase.php: -------------------------------------------------------------------------------- 1 | close(); 53 | self::$_sharedConn = null; 54 | } 55 | } 56 | 57 | public function setUp() : void 58 | { 59 | if ( ! isset(self::$_sharedConn)) { 60 | $params = array( 61 | 'driverClass' => 'Crate\DBAL\Driver\PDOCrate\Driver', 62 | 'host' => 'localhost', 63 | 'port' => 4200 64 | ); 65 | self::$_sharedConn = DriverManager::getConnection($params); 66 | } 67 | $this->_conn = self::$_sharedConn; 68 | 69 | $this->_sqlLoggerStack = new DebugStack(); 70 | $this->_conn->getConfiguration()->setSQLLogger($this->_sqlLoggerStack); 71 | } 72 | 73 | protected function onNotSuccessfulTest(Throwable $e) : void 74 | { 75 | if ($e instanceof AssertionFailedError) { 76 | throw $e; 77 | } 78 | 79 | if(isset($this->_sqlLoggerStack->queries) && count($this->_sqlLoggerStack->queries)) { 80 | $queries = ""; 81 | $i = count($this->_sqlLoggerStack->queries); 82 | foreach (array_reverse($this->_sqlLoggerStack->queries) AS $query) { 83 | $params = array_map(function($p) { if (is_object($p)) return get_class($p); else return "'".print_r($p, true)."'"; }, $query['params'] ?: array()); 84 | $queries .= ($i+1).". SQL: '".$query['sql']."' Params: ".implode(", ", $params).PHP_EOL; 85 | $i--; 86 | } 87 | 88 | $trace = $e->getTrace(); 89 | $traceMsg = ""; 90 | foreach($trace AS $part) { 91 | if(isset($part['file'])) { 92 | if(strpos($part['file'], "PHPUnit/") !== false) { 93 | // Beginning with PHPUnit files we don't print the trace anymore. 94 | break; 95 | } 96 | 97 | $traceMsg .= $part['file'].":".$part['line'].PHP_EOL; 98 | } 99 | } 100 | 101 | $message = "[".get_class($e)."] ".$e->getMessage().PHP_EOL.PHP_EOL."With queries:".PHP_EOL.$queries.PHP_EOL."Trace:".PHP_EOL.$traceMsg; 102 | 103 | throw new \Exception($message, (int)$e->getCode(), $e); 104 | } 105 | throw $e; 106 | } 107 | 108 | public function execute($stmt) 109 | { 110 | return $this->_conn->query($stmt); 111 | } 112 | 113 | public function refresh($table_name) 114 | { 115 | $this->_conn->query('REFRESH TABLE ' . $table_name); 116 | } 117 | 118 | public function prepareStatement($sql) 119 | { 120 | return $this->_conn->prepare($sql); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/BindingTest.php: -------------------------------------------------------------------------------- 1 | prepareStatement('SELECT * FROM sys.cluster WHERE name = ?'); 35 | $stmt->bindParam(1, $name); 36 | $stmt->execute(); 37 | 38 | $noName = 'i0ejfNlzSFCloGYtSzddTw'; 39 | 40 | $stmt = $this->prepareStatement('SELECT * FROM sys.cluster WHERE name = ? OR master_node = ?'); 41 | $stmt->bindParam(1, $name); 42 | $stmt->bindParam(2, $noName); 43 | $this->assertTrue($stmt->execute()); 44 | } 45 | 46 | public function testBindPositionalValue() 47 | { 48 | $stmt = $this->prepareStatement('SELECT * FROM sys.cluster WHERE name = ?'); 49 | $stmt->bindValue(1, 'crate'); 50 | $stmt->execute(); 51 | 52 | $stmt = $this->prepareStatement('SELECT * FROM sys.cluster WHERE name = ? OR master_node = ?'); 53 | $stmt->bindValue(1, 'crate'); 54 | $stmt->bindValue(2, 'i0ejfNlzSFCloGYtSzddTw'); 55 | $this->assertTrue($stmt->execute()); 56 | } 57 | 58 | public function testBindNamedParam() 59 | { 60 | $name = 'crate'; 61 | 62 | $stmt = $this->prepareStatement('SELECT * FROM sys.cluster WHERE name = :name'); 63 | $stmt->bindParam('name', $name); 64 | $stmt->execute(); 65 | 66 | $noName = 'i0ejfNlzSFCloGYtSzddTw'; 67 | 68 | $stmt = $this->prepareStatement('SELECT * FROM sys.cluster WHERE name = :name OR master_node = :master_node'); 69 | $stmt->bindParam('name', $name); 70 | $stmt->bindParam('master_node', $noName); 71 | $this->assertTrue($stmt->execute()); 72 | } 73 | 74 | public function testBindNamedValue() 75 | { 76 | $stmt = $this->prepareStatement('SELECT * FROM sys.cluster WHERE name = :name'); 77 | $stmt->bindValue('name', 'crate'); 78 | $stmt->execute(); 79 | 80 | $stmt = $this->prepareStatement('SELECT * FROM sys.cluster WHERE name = :name OR master_node = :master_node'); 81 | $stmt->bindValue('name', 'crate'); 82 | $stmt->bindValue('master_node', 'i0ejfNlzSFCloGYtSzddTw'); 83 | $this->assertTrue($stmt->execute()); 84 | } 85 | 86 | public function testBindTimestamp() 87 | { 88 | if ($this->_conn->getSchemaManager()->tablesExist("foo")) { 89 | $this->execute("DROP TABLE foo"); 90 | } 91 | 92 | $this->execute("CREATE TABLE foo (id int, ts timestamp) with (number_of_replicas=0)"); 93 | $this->execute("INSERT INTO foo (id, ts) VALUES (1, 1413901591000)"); 94 | $this->execute("INSERT INTO foo (id, ts) VALUES (2, 1413901592000)"); 95 | $this->execute("INSERT INTO foo (id, ts) VALUES (3, 1413901593000)"); 96 | $this->execute("REFRESH TABLE foo"); 97 | 98 | $date = new \DateTime("2014-10-21 14:26:32"); // => 1413901592000 99 | 100 | $stmt = $this->prepareStatement('SELECT * FROM foo WHERE ts > ?'); 101 | $stmt->bindValue(1, $date, 'datetimetz'); 102 | $stmt->execute(); 103 | $row = $stmt->fetchAll(); 104 | $this->assertEquals($row[0]['id'], 3); 105 | $this->assertEquals($row[0]['ts'], 1413901593000); 106 | 107 | $stmt = $this->prepareStatement('SELECT * FROM foo WHERE ts < ?'); 108 | $stmt->bindValue(1, $date, 'datetime'); 109 | $stmt->execute(); 110 | $row = $stmt->fetchAll(); 111 | $this->assertEquals($row[0]['id'], 1); 112 | $this->assertEquals($row[0]['ts'], 1413901591000); 113 | 114 | $this->execute("DROP TABLE foo"); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/ConnectionTest.php: -------------------------------------------------------------------------------- 1 | resetSharedConn(); 32 | parent::setUp(); 33 | } 34 | 35 | public function tearDown() : void 36 | { 37 | parent::tearDown(); 38 | $this->resetSharedConn(); 39 | } 40 | 41 | public function testBasicAuthConnection() 42 | { 43 | $auth = ['crate', 'secret']; 44 | $params = array( 45 | 'driverClass' => 'Crate\DBAL\Driver\PDOCrate\Driver', 46 | 'host' => 'localhost', 47 | 'port' => 4200, 48 | 'user' => $auth[0], 49 | 'password' => $auth[1], 50 | ); 51 | $conn = \Doctrine\DBAL\DriverManager::getConnection($params); 52 | $this->assertEquals($auth[0], $conn->getUsername()); 53 | $this->assertEquals($auth[1], $conn->getPassword()); 54 | $auth_attr = $conn->getWrappedConnection()->getAttribute(PDO::CRATE_ATTR_HTTP_BASIC_AUTH); 55 | $this->assertEquals($auth_attr, $auth); 56 | } 57 | 58 | public function testGetConnection() 59 | { 60 | $this->assertInstanceOf('Doctrine\DBAL\Connection', $this->_conn); 61 | $this->assertInstanceOf('Crate\DBAL\Driver\PDOCrate\PDOConnection', $this->_conn->getWrappedConnection()); 62 | } 63 | 64 | public function testGetDriver() 65 | { 66 | $this->assertInstanceOf('Crate\DBAL\Driver\PDOCrate\Driver', $this->_conn->getDriver()); 67 | } 68 | 69 | public function testStatement() 70 | { 71 | $sql = 'SELECT * FROM sys.cluster'; 72 | $stmt = $this->_conn->prepare($sql); 73 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 74 | $this->assertInstanceOf('Crate\PDO\PDOStatement', $stmt->getWrappedStatement()); 75 | 76 | } 77 | 78 | public function testConnect() 79 | { 80 | $this->assertTrue($this->_conn->connect()); 81 | 82 | $stmt = $this->_conn->query('select * from sys.cluster'); 83 | $this->assertEquals(1, $stmt->rowCount()); 84 | 85 | $row = $stmt->fetch(); 86 | $this->assertEquals('crate', $row['name']); 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/DataAccessTest.php: -------------------------------------------------------------------------------- 1 | _conn->getSchemaManager(); 47 | $table = new \Doctrine\DBAL\Schema\Table("fetch_table"); 48 | $table->addColumn('test_int', 'integer'); 49 | $table->addColumn('test_string', 'string'); 50 | $table->addColumn('test_datetime', 'timestamp', array('notnull' => false)); 51 | $table->addColumn('test_array', 'array', array('columnDefinition'=>'ARRAY(STRING)')); 52 | $platformOptions = array( 53 | 'type' => MapType::STRICT, 54 | 'fields' => array( 55 | new Column('id', Type::getType('integer'), array()), 56 | new Column('name', Type::getType('string'), array()), 57 | new Column('value', Type::getType('float'), array()), 58 | ), 59 | ); 60 | $table->addColumn('test_object', MapType::NAME, 61 | array('platformOptions'=>$platformOptions)); 62 | $table->setPrimaryKey(array('test_int')); 63 | 64 | $sm->createTable($table); 65 | 66 | $this->_conn->insert('fetch_table', array( 67 | 'test_int' => 1, 68 | 'test_string' => 'foo', 69 | 'test_datetime' => new \DateTime('2010-01-01 10:10:10'), 70 | 'test_array' => array('foo','bar'), 71 | 'test_object' => array('id'=>1, 'name'=>'foo', 'value'=>1.234,), 72 | ), array('integer','string','timestamp','array','map')); 73 | $this->refresh('fetch_table'); 74 | } 75 | } 76 | 77 | public function tearDown() : void 78 | { 79 | if (self::$generated === true) { 80 | $this->_conn->getSchemaManager()->dropTable('fetch_table'); 81 | self::$generated = false; 82 | } 83 | } 84 | 85 | public function testPrepareWithBindValue() 86 | { 87 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 88 | $stmt = $this->_conn->prepare($sql); 89 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 90 | 91 | $stmt->bindValue(1, 1, PDO::PARAM_INT); 92 | $stmt->bindValue(2, 'foo', PDO::PARAM_STR); 93 | $stmt->execute(); 94 | 95 | $row = $stmt->fetch(\PDO::FETCH_ASSOC); 96 | $row = array_change_key_case($row, \CASE_LOWER); 97 | $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $row); 98 | } 99 | 100 | public function testPrepareWithBindParam() 101 | { 102 | $paramInt = 1; 103 | $paramStr = 'foo'; 104 | 105 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 106 | $stmt = $this->_conn->prepare($sql); 107 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 108 | 109 | $stmt->bindParam(1, $paramInt, PDO::PARAM_INT); 110 | $stmt->bindParam(2, $paramStr, PDO::PARAM_STR); 111 | $stmt->execute(); 112 | 113 | $row = $stmt->fetch(\PDO::FETCH_ASSOC); 114 | $row = array_change_key_case($row, \CASE_LOWER); 115 | $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $row); 116 | } 117 | 118 | public function testPrepareWithFetchAll() 119 | { 120 | $paramInt = 1; 121 | $paramStr = 'foo'; 122 | 123 | $sql = "SELECT test_int, test_string, test_datetime, test_array, test_object FROM fetch_table WHERE test_int = ? AND test_string = ?"; 124 | $stmt = $this->_conn->prepare($sql); 125 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 126 | 127 | $stmt->bindParam(1, $paramInt, PDO::PARAM_INT); 128 | $stmt->bindParam(2, $paramStr, PDO::PARAM_STR); 129 | $stmt->execute(); 130 | 131 | $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC); 132 | $rows[0] = array_change_key_case($rows[0], \CASE_LOWER); 133 | $this->assertEquals(array( 134 | 'test_int' => 1, 135 | 'test_string' => 'foo', 136 | 'test_datetime' => 1262340610000, 137 | 'test_array' => array('foo', 'bar'), 138 | 'test_object' => array('id'=>1, 'name'=>'foo', 'value'=>1.234) 139 | ), $rows[0]); 140 | 141 | $this->assertEquals($this->_conn->convertToPHPValue($rows[0]['test_datetime'], 'timestamp'), 142 | new \DateTime('2010-01-01 10:10:10')); 143 | $this->assertEquals($this->_conn->convertToPHPValue($rows[0]['test_object'], 'map'), 144 | array('id'=>1, 'name'=>'foo', 'value'=>1.234)); 145 | $this->assertEquals($this->_conn->convertToPHPValue($rows[0]['test_array'], 'array'), 146 | array('foo','bar')); 147 | } 148 | 149 | /** 150 | * @group DBAL-228 151 | */ 152 | public function testPrepareWithFetchAllBoth() 153 | { 154 | $paramInt = 1; 155 | $paramStr = 'foo'; 156 | 157 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 158 | $stmt = $this->_conn->prepare($sql); 159 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 160 | 161 | $stmt->bindParam(1, $paramInt, PDO::PARAM_INT); 162 | $stmt->bindParam(2, $paramStr, PDO::PARAM_STR); 163 | $stmt->execute(); 164 | 165 | $rows = $stmt->fetchAll(\PDO::FETCH_BOTH); 166 | $rows[0] = array_change_key_case($rows[0], \CASE_LOWER); 167 | $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo', 0 => 1, 1 => 'foo'), $rows[0]); 168 | } 169 | 170 | public function testPrepareWithFetchColumn() 171 | { 172 | $paramInt = 1; 173 | $paramStr = 'foo'; 174 | 175 | $sql = "SELECT test_int FROM fetch_table WHERE test_int = ? AND test_string = ?"; 176 | $stmt = $this->_conn->prepare($sql); 177 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 178 | 179 | $stmt->bindParam(1, $paramInt, PDO::PARAM_INT); 180 | $stmt->bindParam(2, $paramStr, PDO::PARAM_STR); 181 | $stmt->execute(); 182 | 183 | $column = $stmt->fetchColumn(); 184 | $this->assertEquals(1, $column); 185 | } 186 | 187 | public function testPrepareWithIterator() 188 | { 189 | $paramInt = 1; 190 | $paramStr = 'foo'; 191 | 192 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 193 | $stmt = $this->_conn->prepare($sql); 194 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 195 | 196 | $stmt->bindParam(1, $paramInt, PDO::PARAM_INT); 197 | $stmt->bindParam(2, $paramStr, PDO::PARAM_STR); 198 | $stmt->execute(); 199 | 200 | $rows = array(); 201 | $stmt->setFetchMode(\PDO::FETCH_ASSOC); 202 | foreach ($stmt as $row) { 203 | $rows[] = array_change_key_case($row, \CASE_LOWER); 204 | } 205 | 206 | $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $rows[0]); 207 | } 208 | 209 | public function testPrepareWithQuoted() 210 | { 211 | $table = 'fetch_table'; 212 | $paramInt = 1; 213 | $paramStr = 'foo'; 214 | 215 | $sql = "SELECT test_int, test_string FROM " . $this->_conn->quoteIdentifier($table) . " ". 216 | "WHERE test_int = " . $this->_conn->quote($paramInt, ParameterType::INTEGER) . " AND test_string = '" . $paramStr . "')"; 217 | $stmt = $this->_conn->prepare($sql); 218 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 219 | } 220 | 221 | public function testPrepareWithExecuteParams() 222 | { 223 | $paramInt = 1; 224 | $paramStr = 'foo'; 225 | 226 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 227 | $stmt = $this->_conn->prepare($sql); 228 | $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt); 229 | $stmt->execute(array($paramInt, $paramStr)); 230 | 231 | $row = $stmt->fetch(\PDO::FETCH_ASSOC); 232 | $this->assertTrue($row !== false); 233 | $row = array_change_key_case($row, \CASE_LOWER); 234 | $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $row); 235 | } 236 | 237 | public function testFetchAll() 238 | { 239 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 240 | $data = $this->_conn->fetchAll($sql, array(1, 'foo')); 241 | 242 | $this->assertEquals(1, count($data)); 243 | 244 | $row = $data[0]; 245 | $this->assertEquals(2, count($row)); 246 | 247 | $row = array_change_key_case($row, \CASE_LOWER); 248 | $this->assertEquals(1, $row['test_int']); 249 | $this->assertEquals('foo', $row['test_string']); 250 | } 251 | 252 | public function testFetchBoth() 253 | { 254 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 255 | $row = $this->_conn->executeQuery($sql, array(1, 'foo'))->fetch(\PDO::FETCH_BOTH); 256 | 257 | $this->assertTrue($row !== false); 258 | 259 | $row = array_change_key_case($row, \CASE_LOWER); 260 | 261 | $this->assertEquals(1, $row['test_int']); 262 | $this->assertEquals('foo', $row['test_string']); 263 | $this->assertEquals(1, $row[0]); 264 | $this->assertEquals('foo', $row[1]); 265 | } 266 | 267 | public function testFetchRow() 268 | { 269 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 270 | $row = $this->_conn->fetchAssoc($sql, array(1, 'foo')); 271 | 272 | $this->assertTrue($row !== false); 273 | 274 | $row = array_change_key_case($row, \CASE_LOWER); 275 | 276 | $this->assertEquals(1, $row['test_int']); 277 | $this->assertEquals('foo', $row['test_string']); 278 | } 279 | 280 | public function testFetchArray() 281 | { 282 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 283 | $row = $this->_conn->fetchArray($sql, array(1, 'foo')); 284 | 285 | $this->assertEquals(1, $row[0]); 286 | $this->assertEquals('foo', $row[1]); 287 | } 288 | 289 | public function testFetchColumn() 290 | { 291 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 292 | $testInt = $this->_conn->fetchColumn($sql, array(1, 'foo'), 0); 293 | 294 | $this->assertEquals(1, $testInt); 295 | 296 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 297 | $testString = $this->_conn->fetchColumn($sql, array(1, 'foo'), 1); 298 | 299 | $this->assertEquals('foo', $testString); 300 | } 301 | 302 | /** 303 | * @group DDC-697 304 | */ 305 | public function testExecuteQueryBindDateTimeType() 306 | { 307 | $sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?'; 308 | $stmt = $this->_conn->executeQuery($sql, 309 | array(1 => new \DateTime('2010-01-01 10:10:10')), 310 | array(1 => Type::DATETIME) 311 | ); 312 | 313 | $this->assertEquals(1, $stmt->fetchColumn()); 314 | } 315 | 316 | /** 317 | * @group DDC-697 318 | */ 319 | public function testExecuteUpdateBindDateTimeType() 320 | { 321 | $datetime = new \DateTime('2010-02-02 20:20:20'); 322 | 323 | $sql = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)'; 324 | $affectedRows = $this->_conn->executeUpdate($sql, 325 | array(1 => 50, 2 => 'foo', 3 => $datetime), 326 | array(1 => PDO::PARAM_INT, 2 => PDO::PARAM_STR, 3 => Type::DATETIME) 327 | ); 328 | $this->assertEquals(1, $affectedRows); 329 | $this->refresh('fetch_table'); 330 | 331 | $this->assertEquals(1, $this->_conn->executeQuery( 332 | 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?', 333 | array(1 => $datetime), 334 | array(1 => Type::DATETIME) 335 | )->fetchColumn()); 336 | } 337 | 338 | /** 339 | * @group DDC-697 340 | */ 341 | public function testPrepareQueryBindValueDateTimeType() 342 | { 343 | $sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?'; 344 | $stmt = $this->_conn->prepare($sql); 345 | $stmt->bindValue(1, new \DateTime('2010-01-01 10:10:10'), Type::DATETIME); 346 | $stmt->execute(); 347 | 348 | $this->assertEquals(1, $stmt->fetchColumn()); 349 | } 350 | 351 | /** 352 | * @group DBAL-78 353 | */ 354 | public function testNativeArrayListSupport() 355 | { 356 | for ($i = 100; $i < 110; $i++) { 357 | $this->_conn->insert('fetch_table', array('test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01T10:10:10')); 358 | } 359 | $this->refresh('fetch_table'); 360 | 361 | $stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_int IN (?) ORDER BY test_int', 362 | array(array(100, 101, 102, 103, 104)), array(Connection::PARAM_INT_ARRAY)); 363 | 364 | $data = $stmt->fetchAll(PDO::FETCH_NUM); 365 | $this->assertEquals(5, count($data)); 366 | $this->assertEquals(array(array(100), array(101), array(102), array(103), array(104)), $data); 367 | 368 | $stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_string IN (?) ORDER BY test_int', 369 | array(array('foo100', 'foo101', 'foo102', 'foo103', 'foo104')), array(Connection::PARAM_STR_ARRAY)); 370 | 371 | $data = $stmt->fetchAll(PDO::FETCH_NUM); 372 | $this->assertEquals(5, count($data)); 373 | $this->assertEquals(array(array(100), array(101), array(102), array(103), array(104)), $data); 374 | } 375 | 376 | /** 377 | * @group DDC-1014 378 | */ 379 | public function testDateArithmetics() 380 | { 381 | $this->markTestSkipped('Data add day expression not supported by crate platform'); 382 | 383 | $p = $this->_conn->getDatabasePlatform(); 384 | $sql = 'SELECT '; 385 | $sql .= $p->getDateDiffExpression('test_datetime', $p->getCurrentTimestampSQL()) .' AS diff, '; 386 | $sql .= $p->getDateAddDaysExpression('test_datetime', 10) .' AS add_days, '; 387 | $sql .= $p->getDateSubDaysExpression('test_datetime', 10) .' AS sub_days, '; 388 | $sql .= $p->getDateAddMonthExpression('test_datetime', 2) .' AS add_month, '; 389 | $sql .= $p->getDateSubMonthExpression('test_datetime', 2) .' AS sub_month '; 390 | $sql .= 'FROM fetch_table'; 391 | 392 | $row = $this->_conn->fetchAssoc($sql); 393 | $row = array_change_key_case($row, CASE_LOWER); 394 | 395 | $diff = floor( (strtotime('2010-01-01')-time()) / 3600 / 24); 396 | $this->assertEquals($diff, (int)$row['diff'], "Date difference should be approx. ".$diff." days.", 1); 397 | $this->assertEquals('2010-01-11', date('Y-m-d', strtotime($row['add_days'])), "Adding date should end up on 2010-01-11"); 398 | $this->assertEquals('2009-12-22', date('Y-m-d', strtotime($row['sub_days'])), "Subtracting date should end up on 2009-12-22"); 399 | $this->assertEquals('2010-03-01', date('Y-m-d', strtotime($row['add_month'])), "Adding month should end up on 2010-03-01"); 400 | $this->assertEquals('2009-11-01', date('Y-m-d', strtotime($row['sub_month'])), "Adding month should end up on 2009-11-01"); 401 | } 402 | 403 | public function testQuoteSQLInjection() 404 | { 405 | $this->expectException(DBALException::class); 406 | 407 | $sql = "SELECT * FROM fetch_table WHERE test_string = bar' OR '1'='1"; 408 | $this->_conn->fetchAll($sql); 409 | } 410 | 411 | /** 412 | * @group DDC-1213 413 | */ 414 | public function testBitComparisonExpressionSupport() 415 | { 416 | $this->markTestSkipped("Bit comparison expression not supported by crate"); 417 | 418 | $this->_conn->executeQuery('DELETE FROM fetch_table')->execute(); 419 | $platform = $this->_conn->getDatabasePlatform(); 420 | $bitmap = array(); 421 | 422 | for ($i = 2; $i < 9; $i = $i + 2) { 423 | $bitmap[$i] = array( 424 | 'bit_or' => ($i | 2), 425 | 'bit_and' => ($i & 2) 426 | ); 427 | $this->_conn->insert('fetch_table', array( 428 | 'test_int' => $i, 429 | 'test_string' => json_encode($bitmap[$i]), 430 | 'test_datetime' => '2010-01-01T10:10:10' 431 | )); 432 | } 433 | 434 | $sql[] = 'SELECT '; 435 | $sql[] = 'test_int, '; 436 | $sql[] = 'test_string, '; 437 | $sql[] = $platform->getBitOrComparisonExpression('test_int', 2) . ' AS bit_or, '; 438 | $sql[] = $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and '; 439 | $sql[] = 'FROM fetch_table'; 440 | 441 | $stmt = $this->_conn->executeQuery(implode(PHP_EOL, $sql)); 442 | $data = $stmt->fetchAll(PDO::FETCH_ASSOC); 443 | 444 | 445 | $this->assertEquals(4, count($data)); 446 | $this->assertEquals(count($bitmap), count($data)); 447 | foreach ($data as $row) { 448 | $row = array_change_key_case($row, CASE_LOWER); 449 | 450 | $this->assertArrayHasKey('test_int', $row); 451 | 452 | $id = $row['test_int']; 453 | 454 | $this->assertArrayHasKey($id, $bitmap); 455 | $this->assertArrayHasKey($id, $bitmap); 456 | 457 | $this->assertArrayHasKey('bit_or', $row); 458 | $this->assertArrayHasKey('bit_and', $row); 459 | 460 | $this->assertEquals($row['bit_or'], $bitmap[$id]['bit_or']); 461 | $this->assertEquals($row['bit_and'], $bitmap[$id]['bit_and']); 462 | } 463 | } 464 | 465 | public function testSetDefaultFetchMode() 466 | { 467 | $stmt = $this->_conn->query("SELECT * FROM fetch_table"); 468 | $stmt->setFetchMode(\PDO::FETCH_NUM); 469 | 470 | $row = array_keys($stmt->fetch()); 471 | $this->assertEquals(0, count( array_filter($row, function($v) { return ! is_numeric($v); })), "should be no non-numerical elements in the result."); 472 | } 473 | 474 | /** 475 | * @group DBAL-196 476 | */ 477 | public function testFetchAllSupportFetchClass() 478 | { 479 | $this->markTestSkipped("PDO::FETCH_CLASS not supported by crate PDO"); 480 | 481 | $this->setupFixture(); 482 | 483 | $sql = "SELECT test_int, test_string, test_datetime FROM fetch_table"; 484 | $stmt = $this->_conn->prepare($sql); 485 | $stmt->execute(); 486 | 487 | $results = $stmt->fetchAll( 488 | \PDO::FETCH_CLASS, 489 | __NAMESPACE__.'\\MyFetchClass' 490 | ); 491 | 492 | $this->assertEquals(1, count($results)); 493 | $this->assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]); 494 | 495 | $this->assertEquals(1, $results[0]->test_int); 496 | $this->assertEquals('foo', $results[0]->test_string); 497 | $this->assertStringStartsWith('2010-01-01T10:10:10', $results[0]->test_datetime); 498 | } 499 | 500 | /** 501 | * @group DBAL-241 502 | */ 503 | public function testFetchAllStyleColumn() 504 | { 505 | $sql = "DELETE FROM fetch_table"; 506 | $this->_conn->executeUpdate($sql); 507 | 508 | $this->_conn->insert('fetch_table', array('test_int' => 1, 'test_string' => 'foo')); 509 | $this->_conn->insert('fetch_table', array('test_int' => 10, 'test_string' => 'foo')); 510 | $this->refresh("fetch_table"); 511 | 512 | $sql = "SELECT test_int FROM fetch_table ORDER BY test_int ASC"; 513 | $rows = $this->_conn->query($sql)->fetchAll(\PDO::FETCH_COLUMN); 514 | 515 | $this->assertEquals(array(1, 10), $rows); 516 | } 517 | 518 | /** 519 | * @group DBAL-214 520 | */ 521 | public function testSetFetchModeClassFetchAll() 522 | { 523 | $this->markTestSkipped("PDO::FETCH_CLASS not supported crate PDO"); 524 | $this->setupFixture(); 525 | 526 | $sql = "SELECT * FROM fetch_table"; 527 | $stmt = $this->_conn->query($sql); 528 | $stmt->setFetchMode(\PDO::FETCH_CLASS, __NAMESPACE__ . '\\MyFetchClass', array()); 529 | 530 | $results = $stmt->fetchAll(); 531 | 532 | $this->assertEquals(1, count($results)); 533 | $this->assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]); 534 | 535 | $this->assertEquals(1, $results[0]->test_int); 536 | $this->assertEquals('foo', $results[0]->test_string); 537 | $this->assertStringStartsWith('2010-01-01T10:10:10', $results[0]->test_datetime); 538 | } 539 | 540 | /** 541 | * @group DBAL-214 542 | */ 543 | public function testSetFetchModeClassFetch() 544 | { 545 | $this->markTestSkipped("PDO::FETCH_CLASS not supported by crate PDO"); 546 | 547 | $this->setupFixture(); 548 | 549 | $sql = "SELECT * FROM fetch_table"; 550 | $stmt = $this->_conn->query($sql); 551 | $stmt->setFetchMode(\PDO::FETCH_CLASS, __NAMESPACE__ . '\\MyFetchClass', array()); 552 | 553 | $results = array(); 554 | while ($row = $stmt->fetch()) { 555 | $results[] = $row; 556 | } 557 | 558 | $this->assertEquals(1, count($results)); 559 | $this->assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]); 560 | 561 | $this->assertEquals(1, $results[0]->test_int); 562 | $this->assertEquals('foo', $results[0]->test_string); 563 | $this->assertStringStartsWith('2010-01-01T10:10:10', $results[0]->test_datetime); 564 | } 565 | 566 | /** 567 | * @group DBAL-257 568 | */ 569 | public function testEmptyFetchColumnReturnsFalse() 570 | { 571 | $this->_conn->executeQuery('DELETE FROM fetch_table')->execute(); 572 | $this->refresh("fetch_table"); 573 | $this->assertFalse($this->_conn->fetchColumn('SELECT test_int FROM fetch_table')); 574 | $this->assertFalse($this->_conn->query('SELECT test_int FROM fetch_table')->fetchColumn()); 575 | } 576 | 577 | /** 578 | * @group DBAL-339 579 | */ 580 | public function testSetFetchModeOnDbalStatement() 581 | { 582 | $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; 583 | $stmt = $this->_conn->executeQuery($sql, array(1, "foo")); 584 | $stmt->setFetchMode(\PDO::FETCH_NUM); 585 | 586 | while ($row = $stmt->fetch()) { 587 | $this->assertTrue(isset($row[0])); 588 | $this->assertTrue(isset($row[1])); 589 | } 590 | } 591 | 592 | private function setupFixture() 593 | { 594 | $this->_conn->executeQuery('DELETE FROM fetch_table')->execute(); 595 | $this->_conn->insert('fetch_table', array( 596 | 'test_int' => 1, 597 | 'test_string' => 'foo', 598 | 'test_datetime' => '2010-01-01T10:10:10' 599 | )); 600 | } 601 | 602 | } 603 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/ModifyLimitQueryTest.php: -------------------------------------------------------------------------------- 1 | addColumn('test_int', 'integer'); 41 | $table->setPrimaryKey(array('test_int')); 42 | 43 | $table2 = new \Doctrine\DBAL\Schema\Table("modify_limit_table2"); 44 | $table2->addColumn('id', 'integer', array('autoincrement' => true)); 45 | $table2->addColumn('test_int', 'integer'); 46 | $table2->setPrimaryKey(array('id')); 47 | 48 | $sm = $this->_conn->getSchemaManager(); 49 | $sm->createTable($table); 50 | $sm->createTable($table2); 51 | self::$tableCreated = true; 52 | } 53 | } 54 | 55 | public function tearDown() : void 56 | { 57 | parent::tearDown(); 58 | if (self::$tableCreated) { 59 | $sm = $this->_conn->getSchemaManager(); 60 | try { 61 | $sm->dropTable('modify_limit_table'); 62 | $sm->dropTable('modify_limit_table2'); 63 | self::$tableCreated = false; 64 | } catch (DBALException $e) {} 65 | } 66 | } 67 | 68 | public function testModifyLimitQuerySimpleQuery() 69 | { 70 | $this->_conn->insert('modify_limit_table', array('test_int' => 1)); 71 | $this->_conn->insert('modify_limit_table', array('test_int' => 2)); 72 | $this->_conn->insert('modify_limit_table', array('test_int' => 3)); 73 | $this->_conn->insert('modify_limit_table', array('test_int' => 4)); 74 | 75 | $this->refresh('modify_limit_table'); 76 | 77 | $sql = "SELECT * FROM modify_limit_table ORDER BY test_int ASC"; 78 | 79 | $this->assertLimitResult(array(1, 2, 3, 4), $sql, 10, 0); 80 | $this->assertLimitResult(array(1, 2), $sql, 2, 0); 81 | $this->assertLimitResult(array(3, 4), $sql, 2, 2); 82 | } 83 | 84 | public function testModifyLimitQueryOrderBy() 85 | { 86 | $this->_conn->insert('modify_limit_table', array('test_int' => 1)); 87 | $this->_conn->insert('modify_limit_table', array('test_int' => 2)); 88 | $this->_conn->insert('modify_limit_table', array('test_int' => 3)); 89 | $this->_conn->insert('modify_limit_table', array('test_int' => 4)); 90 | 91 | $this->refresh('modify_limit_table'); 92 | 93 | $sql = "SELECT * FROM modify_limit_table ORDER BY test_int DESC"; 94 | 95 | $this->assertLimitResult(array(4, 3, 2, 1), $sql, 10, 0); 96 | $this->assertLimitResult(array(4, 3), $sql, 2, 0); 97 | $this->assertLimitResult(array(2, 1), $sql, 2, 2); 98 | } 99 | 100 | public function testModifyLimitQueryGroupBy() 101 | { 102 | $this->_conn->insert('modify_limit_table2', array('test_int' => 1, 'id' => 1)); 103 | $this->_conn->insert('modify_limit_table2', array('test_int' => 1, 'id' => 2)); 104 | $this->_conn->insert('modify_limit_table2', array('test_int' => 1, 'id' => 3)); 105 | $this->_conn->insert('modify_limit_table2', array('test_int' => 2, 'id' => 4)); 106 | $this->_conn->insert('modify_limit_table2', array('test_int' => 2, 'id' => 5)); 107 | 108 | $this->refresh('modify_limit_table2'); 109 | 110 | $sql = "SELECT test_int FROM modify_limit_table2 GROUP BY test_int order by test_int"; 111 | $this->assertLimitResult(array(1, 2), $sql, 10, 0); 112 | $this->assertLimitResult(array(1), $sql, 1, 0); 113 | $this->assertLimitResult(array(2), $sql, 1, 1); 114 | } 115 | 116 | public function assertLimitResult($expectedResults, $sql, $limit, $offset) 117 | { 118 | $p = $this->_conn->getDatabasePlatform(); 119 | $data = array(); 120 | foreach ($this->_conn->fetchAll($p->modifyLimitQuery($sql, $limit, $offset)) AS $row) { 121 | $row = array_change_key_case($row, CASE_LOWER); 122 | $data[] = $row['test_int']; 123 | } 124 | $this->assertEquals($expectedResults, $data); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/NamedParametersTest.php: -------------------------------------------------------------------------------- 1 | 1,'bar'=> array(1, 2, 3)), 21 | array('foo'=>PDO::PARAM_INT,'bar'=> Connection::PARAM_INT_ARRAY,), 22 | array( 23 | array('id'=>1,'foo'=>1,'bar'=>1), 24 | array('id'=>2,'foo'=>1,'bar'=>2), 25 | array('id'=>3,'foo'=>1,'bar'=>3), 26 | ) 27 | ), 28 | 29 | array( 30 | 'SELECT * FROM ddc1372_foobar f WHERE f.foo = :foo AND f.bar IN (:bar) ORDER BY f.id', 31 | array('foo'=>1,'bar'=> array(1, 2, 3)), 32 | array('bar'=> Connection::PARAM_INT_ARRAY,'foo'=>PDO::PARAM_INT), 33 | array( 34 | array('id'=>1,'foo'=>1,'bar'=>1), 35 | array('id'=>2,'foo'=>1,'bar'=>2), 36 | array('id'=>3,'foo'=>1,'bar'=>3), 37 | ) 38 | ), 39 | 40 | array( 41 | 'SELECT * FROM ddc1372_foobar f WHERE f.bar IN (:bar) AND f.foo = :foo ORDER BY f.id', 42 | array('foo'=>1,'bar'=> array(1, 2, 3)), 43 | array('bar'=> Connection::PARAM_INT_ARRAY,'foo'=>PDO::PARAM_INT), 44 | array( 45 | array('id'=>1,'foo'=>1,'bar'=>1), 46 | array('id'=>2,'foo'=>1,'bar'=>2), 47 | array('id'=>3,'foo'=>1,'bar'=>3), 48 | ) 49 | ), 50 | 51 | array( 52 | 'SELECT * FROM ddc1372_foobar f WHERE f.bar IN (:bar) AND f.foo = :foo ORDER BY f.id', 53 | array('foo'=>1,'bar'=> array('1', '2', '3')), 54 | array('bar'=> Connection::PARAM_STR_ARRAY,'foo'=>PDO::PARAM_INT), 55 | array( 56 | array('id'=>1,'foo'=>1,'bar'=>1), 57 | array('id'=>2,'foo'=>1,'bar'=>2), 58 | array('id'=>3,'foo'=>1,'bar'=>3), 59 | ) 60 | ), 61 | 62 | array( 63 | 'SELECT * FROM ddc1372_foobar f WHERE f.bar IN (:bar) AND f.foo IN (:foo) ORDER BY f.id', 64 | array('foo'=>array('1'),'bar'=> array(1, 2, 3,4)), 65 | array('bar'=> Connection::PARAM_STR_ARRAY,'foo'=>Connection::PARAM_INT_ARRAY), 66 | array( 67 | array('id'=>1,'foo'=>1,'bar'=>1), 68 | array('id'=>2,'foo'=>1,'bar'=>2), 69 | array('id'=>3,'foo'=>1,'bar'=>3), 70 | array('id'=>4,'foo'=>1,'bar'=>4), 71 | ) 72 | ), 73 | 74 | array( 75 | 'SELECT * FROM ddc1372_foobar f WHERE f.bar IN (:bar) AND f.foo IN (:foo) ORDER BY f.id', 76 | array('foo'=>1,'bar'=> 2), 77 | array('bar'=>PDO::PARAM_INT,'foo'=>PDO::PARAM_INT), 78 | array( 79 | array('id'=>2,'foo'=>1,'bar'=>2), 80 | ) 81 | ), 82 | 83 | array( 84 | 'SELECT * FROM ddc1372_foobar f WHERE f.bar = :arg AND f.foo <> :arg ORDER BY f.id', 85 | array('arg'=>'1'), 86 | array('arg'=>PDO::PARAM_STR), 87 | array( 88 | array('id'=>5,'foo'=>2,'bar'=>1), 89 | ) 90 | ), 91 | 92 | array( 93 | 'SELECT * FROM ddc1372_foobar f WHERE f.bar NOT IN (:arg) AND f.foo IN (:arg) ORDER BY f.id', 94 | array('arg'=>array(1, 2)), 95 | array('arg'=>Connection::PARAM_INT_ARRAY), 96 | array( 97 | array('id'=>3,'foo'=>1,'bar'=>3), 98 | array('id'=>4,'foo'=>1,'bar'=>4), 99 | ) 100 | ), 101 | 102 | ); 103 | } 104 | 105 | public function setUp() : void 106 | { 107 | parent::setUp(); 108 | 109 | if (!$this->_conn->getSchemaManager()->tablesExist("ddc1372_foobar")) { 110 | try { 111 | $table = new \Doctrine\DBAL\Schema\Table("ddc1372_foobar"); 112 | $table->addColumn('id', 'integer'); 113 | $table->addColumn('foo','string'); 114 | $table->addColumn('bar','string'); 115 | $table->setPrimaryKey(array('id')); 116 | 117 | 118 | $sm = $this->_conn->getSchemaManager(); 119 | $sm->createTable($table); 120 | 121 | $this->_conn->insert('ddc1372_foobar', array( 122 | 'id' => 1, 'foo' => 1, 'bar' => 1 123 | )); 124 | $this->_conn->insert('ddc1372_foobar', array( 125 | 'id' => 2, 'foo' => 1, 'bar' => 2 126 | )); 127 | $this->_conn->insert('ddc1372_foobar', array( 128 | 'id' => 3, 'foo' => 1, 'bar' => 3 129 | )); 130 | $this->_conn->insert('ddc1372_foobar', array( 131 | 'id' => 4, 'foo' => 1, 'bar' => 4 132 | )); 133 | $this->_conn->insert('ddc1372_foobar', array( 134 | 'id' => 5, 'foo' => 2, 'bar' => 1 135 | )); 136 | $this->_conn->insert('ddc1372_foobar', array( 137 | 'id' => 6, 'foo' => 2, 'bar' => 2 138 | )); 139 | 140 | $this->refresh('ddc1372_foobar'); 141 | } catch(\Exception $e) { 142 | $this->fail($e->getMessage()); 143 | } 144 | } 145 | } 146 | 147 | public function tearDown() : void 148 | { 149 | parent::tearDown(); 150 | if ($this->_conn->getSchemaManager()->tablesExist("ddc1372_foobar")) { 151 | try { 152 | $sm = $this->_conn->getSchemaManager(); 153 | $sm->dropTable('ddc1372_foobar'); 154 | } catch(\Exception $e) { 155 | $this->fail($e->getMessage()); 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * @dataProvider ticketProvider 162 | * @param string $query 163 | * @param array $params 164 | * @param array $types 165 | * @param array $expected 166 | */ 167 | public function testTicket($query,$params,$types,$expected) 168 | { 169 | $stmt = $this->_conn->executeQuery($query, $params, $types); 170 | $result = $stmt->fetchAll(\PDO::FETCH_ASSOC); 171 | 172 | foreach ($result as $k => $v) { 173 | $result[$k] = array_change_key_case($v, CASE_LOWER); 174 | } 175 | 176 | $this->assertEquals($result, $expected); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/Schema/SchemaManagerTest.php: -------------------------------------------------------------------------------- 1 | _sm = $this->_conn->getSchemaManager(); 43 | } 44 | 45 | public function tearDown() : void 46 | { 47 | foreach ($this->_sm->listTableNames() as $tableName) { 48 | $this->_sm->dropTable($tableName); 49 | } 50 | } 51 | 52 | public function testListTables() 53 | { 54 | $this->createTestTable('list_tables_test'); 55 | $tables = $this->_sm->listTables(); 56 | 57 | $this->assertIsArray($tables); 58 | $this->assertTrue(count($tables) > 0, "List Tables has to find at least one table named 'list_tables_test'."); 59 | 60 | $foundTable = false; 61 | foreach ($tables AS $table) { 62 | $this->assertInstanceOf('Doctrine\DBAL\Schema\Table', $table); 63 | if (strtolower($table->getName()) == 'list_tables_test') { 64 | $foundTable = true; 65 | 66 | $this->assertTrue($table->hasColumn('id')); 67 | $this->assertTrue($table->hasColumn('test')); 68 | $this->assertTrue($table->hasColumn('foreign_key_test')); 69 | } 70 | } 71 | 72 | $this->assertTrue( $foundTable , "The 'list_tables_test' table has to be found."); 73 | } 74 | 75 | public function createListTableColumns() 76 | { 77 | $table = new Table('list_table_columns'); 78 | $table->addColumn('text', Type::STRING); 79 | $table->addColumn('ts', TimestampType::NAME); 80 | $table->addColumn('num_float_double', Type::FLOAT); 81 | $table->addColumn('num_short', Type::SMALLINT); 82 | $table->addColumn('num_int', Type::INTEGER); 83 | $table->addColumn('num_long', Type::BIGINT); 84 | $table->addColumn('id', 'integer', array('notnull' => true)); 85 | $table->setPrimaryKey(array('id')); 86 | 87 | // OBJECT schema definition via platform options 88 | $mapOpts = array( 89 | 'type' => MapType::STRICT, 90 | 'fields' => array( 91 | new Column('id', Type::getType('integer'), array()), 92 | new Column('name', Type::getType('string'), array()), 93 | ), 94 | ); 95 | $table->addColumn('obj', 'map', 96 | array('platformOptions'=>$mapOpts)); 97 | 98 | // OBJECT schema definition via columnDefinition 99 | $table->addColumn('obj2', 'map', 100 | array('columnDefinition'=>'OBJECT (STRICT) AS ( id INTEGER, name STRING )')); 101 | 102 | // ARRAY schema definition via platform options 103 | $arrOpts = array( 104 | 'type' => Type::FLOAT, 105 | ); 106 | $table->addColumn('arr_float', 'array', 107 | array('platformOptions'=>$arrOpts)); 108 | 109 | // ARRAY schema definition via columnDefinition 110 | $table->addColumn('arr_str', 'array', 111 | array('columnDefinition'=>'ARRAY (STRING)')); 112 | $table->addColumn('arr_obj', 'array', 113 | array('columnDefinition'=>'ARRAY (OBJECT (IGNORED) AS ( id INTEGER, name STRING ))')); 114 | 115 | return $table; 116 | } 117 | 118 | public function testListTableColumns() 119 | { 120 | $table = $this->createListTableColumns(); 121 | 122 | $this->_sm->dropAndCreateTable($table); 123 | 124 | $columns = $this->_sm->listTableColumns('list_table_columns'); 125 | $columnsKeys = array_keys($columns); 126 | 127 | self::assertArrayHasKey('id', $columns); 128 | self::assertEquals(0, array_search('id', $columnsKeys)); 129 | self::assertEquals('id', strtolower($columns['id']->getname())); 130 | self::assertInstanceOf(IntegerType::class, $columns['id']->gettype()); 131 | self::assertFalse($columns['id']->getunsigned()); 132 | self::assertTrue($columns['id']->getnotnull()); 133 | self::assertNull($columns['id']->getdefault()); 134 | self::assertIsArray($columns['id']->getPlatformOptions()); 135 | 136 | $this->assertArrayHasKey('text', $columns); 137 | $this->assertEquals('text', strtolower($columns['text']->getname())); 138 | $this->assertInstanceOf('Doctrine\DBAL\Types\StringType', $columns['text']->gettype()); 139 | 140 | $this->assertEquals('ts', strtolower($columns['ts']->getname())); 141 | $this->assertInstanceOf('Crate\DBAL\Types\TimestampType', $columns['ts']->gettype()); 142 | 143 | $this->assertEquals('num_float_double', strtolower($columns['num_float_double']->getname())); 144 | $this->assertInstanceOf('Doctrine\DBAL\Types\FloatType', $columns['num_float_double']->gettype()); 145 | 146 | $this->assertArrayHasKey('num_short', $columns); 147 | $this->assertEquals('num_short', strtolower($columns['num_short']->getname())); 148 | $this->assertInstanceOf('Doctrine\DBAL\Types\SmallIntType', $columns['num_short']->gettype()); 149 | 150 | $this->assertArrayHasKey('num_int', $columns); 151 | $this->assertEquals('num_int', strtolower($columns['num_int']->getname())); 152 | $this->assertInstanceOf('Doctrine\DBAL\Types\IntegerType', $columns['num_int']->gettype()); 153 | 154 | $this->assertArrayHasKey('num_long', $columns); 155 | $this->assertEquals('num_long', strtolower($columns['num_long']->getname())); 156 | $this->assertInstanceOf('Doctrine\DBAL\Types\BigIntType', $columns['num_long']->gettype()); 157 | 158 | $this->assertEquals('obj', strtolower($columns['obj']->getname())); 159 | $this->assertInstanceOf('Crate\DBAL\Types\MapType', $columns['obj']->gettype()); 160 | 161 | $this->assertEquals("obj['id']", strtolower($columns["obj['id']"]->getname())); 162 | $this->assertInstanceOf('Doctrine\DBAL\Types\IntegerType', $columns["obj['id']"]->gettype()); 163 | 164 | $this->assertEquals("obj['name']", strtolower($columns["obj['name']"]->getname())); 165 | $this->assertInstanceOf('Doctrine\DBAL\Types\StringType', $columns["obj['name']"]->gettype()); 166 | 167 | $this->assertEquals('obj2', strtolower($columns['obj2']->getname())); 168 | $this->assertInstanceOf('Crate\DBAL\Types\MapType', $columns['obj2']->gettype()); 169 | 170 | $this->assertEquals("obj2['id']", strtolower($columns["obj2['id']"]->getname())); 171 | $this->assertInstanceOf('Doctrine\DBAL\Types\IntegerType', $columns["obj2['id']"]->gettype()); 172 | 173 | $this->assertEquals("obj2['name']", strtolower($columns["obj2['name']"]->getname())); 174 | $this->assertInstanceOf('Doctrine\DBAL\Types\StringType', $columns["obj2['name']"]->gettype()); 175 | 176 | $this->assertEquals('arr_float', strtolower($columns['arr_float']->getname())); 177 | $this->assertInstanceOf('Crate\DBAL\Types\ArrayType', $columns['arr_float']->gettype()); 178 | 179 | $this->assertEquals('arr_str', strtolower($columns['arr_str']->getname())); 180 | $this->assertInstanceOf('Crate\DBAL\Types\ArrayType', $columns['arr_str']->gettype()); 181 | 182 | $this->assertEquals('arr_obj', strtolower($columns['arr_obj']->getname())); 183 | $this->assertInstanceOf('Crate\DBAL\Types\ArrayType', $columns['arr_obj']->gettype()); 184 | } 185 | 186 | 187 | public function testCreateSchema() 188 | { 189 | $this->createTestTable('test_table'); 190 | 191 | $schema = $this->_sm->createSchema(); 192 | $this->assertTrue($schema->hasTable('test_table')); 193 | } 194 | 195 | /** 196 | * @param string $name 197 | * @param array $data 198 | */ 199 | protected function createTestTable($name = 'test_table', $data = array()) 200 | { 201 | $options = array(); 202 | if (isset($data['options'])) { 203 | $options = $data['options']; 204 | } 205 | 206 | $table = $this->getTestTable($name, $options); 207 | $this->_sm->dropAndCreateTable($table); 208 | } 209 | 210 | protected function getTestTable($name, $options=array()) 211 | { 212 | $table = new Table($name, array(), array(), array(), false, $options); 213 | $table->setSchemaConfig($this->_sm->createSchemaConfig()); 214 | $table->addColumn('id', 'integer', array('notnull' => true)); 215 | $table->setPrimaryKey(array('id')); 216 | $table->addColumn('test', 'string', array('length' => 255)); 217 | $table->addColumn('foreign_key_test', 'integer'); 218 | return $table; 219 | } 220 | 221 | protected function getTestCompositeTable($name) 222 | { 223 | $table = new Table($name, array(), array(), array(), false, array()); 224 | $table->setSchemaConfig($this->_sm->createSchemaConfig()); 225 | $table->addColumn('id', 'integer', array('notnull' => true)); 226 | $table->addColumn('other_id', 'integer', array('notnull' => true)); 227 | $table->setPrimaryKey(array('id', 'other_id')); 228 | $table->addColumn('test', 'string', array('length' => 255)); 229 | $table->addColumn('test_other', 'string', array('length' => 255)); 230 | return $table; 231 | } 232 | 233 | protected function assertHasTable($tables, $tableName) 234 | { 235 | $foundTable = false; 236 | foreach ($tables AS $table) { 237 | $this->assertInstanceOf('Doctrine\DBAL\Schema\Table', $table, 'No Table instance was found in tables array.'); 238 | if (strtolower($table->getName()) == 'list_tables_test_new_name') { 239 | $foundTable = true; 240 | } 241 | } 242 | $this->assertTrue($foundTable, "Could not find new table"); 243 | } 244 | 245 | public function testListTableIndexes() 246 | { 247 | $table = $this->getTestCompositeTable('list_table_indexes_test'); 248 | 249 | $this->_sm->dropAndCreateTable($table); 250 | 251 | $tableIndexes = $this->_sm->listTableIndexes('list_table_indexes_test'); 252 | 253 | self::assertEquals(1, count($tableIndexes)); 254 | 255 | self::assertArrayHasKey('primary', $tableIndexes, 'listTableIndexes() has to return a "primary" array key.'); 256 | self::assertEquals(['id', 'other_id'], array_map('strtolower', $tableIndexes['primary']->getColumns())); 257 | self::assertTrue($tableIndexes['primary']->isUnique()); 258 | self::assertTrue($tableIndexes['primary']->isPrimary()); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/TableOptionsTest.php: -------------------------------------------------------------------------------- 1 | _conn->getSchemaManager()->tablesExist("ddc1372_foobar")) { 38 | try { 39 | $sm = $this->_conn->getSchemaManager(); 40 | $sm->dropTable('table_option_test'); 41 | } catch(\Exception $e) { 42 | $this->fail($e->getMessage()); 43 | } 44 | } 45 | } 46 | 47 | public function testAdditionalTableOptions() 48 | { 49 | $platform = $this->_conn->getDatabasePlatform(); 50 | 51 | $options = []; 52 | $options['sharding_routing_column'] = 'id'; 53 | $options['sharding_num_shards'] = 6; 54 | $options['partition_columns'] = ['parted', 'date']; 55 | $options['table_options'] = []; 56 | $options['table_options']['number_of_replicas'] = '0-2'; 57 | $options['table_options']['write.wait_for_active_shards'] = 'ALL'; 58 | 59 | $table = new Table('t1', [], [], [], 0, $options); 60 | $table->addColumn('id', 'integer'); 61 | $table->addColumn('parted', 'integer'); 62 | $table->addColumn('date', 'timestamp'); 63 | 64 | $sql = $platform->getCreateTableSQL($table); 65 | $this->assertEquals(array( 66 | 'CREATE TABLE t1 (id INTEGER, parted INTEGER, date TIMESTAMP)' 67 | . ' CLUSTERED BY (id) INTO 6 SHARDS' 68 | . ' PARTITIONED BY (parted, date)' 69 | . ' WITH ("number_of_replicas" = \'0-2\', "write.wait_for_active_shards" = \'ALL\')') 70 | , $sql); 71 | } 72 | 73 | public function testGetAdditionalTableOptions() 74 | { 75 | $options = []; 76 | $options['sharding_routing_column'] = 'id'; 77 | $options['sharding_num_shards'] = 6; 78 | $options['partition_columns'] = ['parted', 'date']; 79 | $options['table_options'] = []; 80 | $options['table_options']['number_of_replicas'] = '0-2'; 81 | $options['table_options']['write.wait_for_active_shards'] = 'ALL'; 82 | 83 | $table = new Table('table_option_test', [], [], [], 0, $options); 84 | $table->addColumn('id', 'integer'); 85 | $table->addColumn('parted', 'integer'); 86 | $table->addColumn('date', 'timestamp'); 87 | 88 | $sm = $this->_conn->getSchemaManager(); 89 | $sm->createTable($table); 90 | 91 | $schema = $sm->createSchema(); 92 | 93 | $retrievedTable = $schema->getTable($table->getName()); 94 | $options = $retrievedTable->getOptions(); 95 | 96 | $this->assertEquals($options['sharding_routing_column'], 'id'); 97 | $this->assertEquals($options['sharding_num_shards'], 6); 98 | $this->assertEquals($options['partition_columns'], ['parted', 'date']); 99 | $this->assertEquals($options['table_options']['number_of_replicas'], '0-2'); 100 | $this->assertEquals($options['table_options']['write.wait_for_active_shards'], 'ALL'); 101 | } 102 | 103 | public function testPartitionColumnsNotArray() 104 | { 105 | $platform = $this->_conn->getDatabasePlatform(); 106 | 107 | $options = []; 108 | $options['partition_columns'] = 'parted'; 109 | $table = new Table('t1', [], [], [], 0, $options); 110 | $table->addColumn('parted', 'integer'); 111 | 112 | $this->expectException(InvalidArgumentException::class); 113 | $this->expectExceptionMessage("Expecting array type at 'partition_columns'"); 114 | $platform->getCreateTableSQL($table); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/TypeConversionTest.php: -------------------------------------------------------------------------------- 1 | platform = new CratePlatform(); 40 | } 41 | 42 | public function testTimestampType() 43 | { 44 | $input = new \DateTime("2014-10-21 15:23:38"); 45 | 46 | // datetimetz 47 | $type = Type::getType(Type::DATETIMETZ); 48 | $expected = $input->format('Y-m-d\TH:i:sO'); 49 | $output = $type->convertToDatabaseValue($input, $this->platform); 50 | $this->assertEquals($output, $expected); 51 | $inputRestored = $type->convertToPHPValue($output, $this->platform); 52 | $this->assertEquals($inputRestored, $input); 53 | $inputRestored = $type->convertToPHPValue($input, $this->platform); 54 | $this->assertEquals($inputRestored, $input); 55 | 56 | // datetime 57 | $type = Type::getType(Type::DATETIME); 58 | $expected = $input->format('Y-m-d\TH:i:s'); 59 | $output = $type->convertToDatabaseValue($input, $this->platform); 60 | $this->assertEquals($output, $expected); 61 | $inputRestored = $type->convertToPHPValue($output, $this->platform); 62 | $this->assertEquals($inputRestored, $input); 63 | $inputRestored = $type->convertToPHPValue($input, $this->platform); 64 | $this->assertEquals($inputRestored, $input); 65 | 66 | // date 67 | $type = Type::getType(Type::DATE); 68 | $expected = $input->format('Y-m-d\TH:i:s'); 69 | $output = $type->convertToDatabaseValue($input, $this->platform); 70 | $this->assertEquals($output, $expected); 71 | $inputRestored = $type->convertToPHPValue($output, $this->platform); 72 | $this->assertEquals($inputRestored, $input); 73 | $inputRestored = $type->convertToPHPValue($input, $this->platform); 74 | $this->assertEquals($inputRestored, $input); 75 | 76 | // time 77 | $type = Type::getType(Type::TIME); 78 | $expected = $input->format('Y-m-d\TH:i:s'); 79 | $output = $type->convertToDatabaseValue($input, $this->platform); 80 | $this->assertEquals($output, $expected); 81 | $inputRestored = $type->convertToPHPValue($output, $this->platform); 82 | $this->assertEquals($inputRestored, $input); 83 | $inputRestored = $type->convertToPHPValue($input, $this->platform); 84 | $this->assertEquals($inputRestored, $input); 85 | 86 | // timestamp 87 | $type = Type::getType(TimestampType::NAME); 88 | $expected = $input->format('U')*TimestampType::S_TO_MS; 89 | $output = $type->convertToDatabaseValue($input, $this->platform); 90 | $this->assertEquals($output, $expected); 91 | $inputRestored = $type->convertToPHPValue($output, $this->platform); 92 | $this->assertEquals($inputRestored, $input); 93 | $inputRestored = $type->convertToPHPValue($input, $this->platform); 94 | $this->assertEquals($inputRestored, $input); 95 | } 96 | 97 | public function testTimestampTypeNull() 98 | { 99 | $types = array(Type::getType(Type::DATETIMETZ), 100 | Type::getType(Type::DATETIME), 101 | Type::getType(Type::DATE), 102 | Type::getType(Type::TIME), 103 | Type::getType(TimestampType::NAME) 104 | ); 105 | foreach ($types as $type) { 106 | // to DB value 107 | $value = $type->convertToDatabaseValue(null, $this->platform); 108 | $this->assertEquals($value, null); 109 | 110 | // to PHP value 111 | $value = $type->convertToPHPValue(null, $this->platform); 112 | $this->assertEquals($value, null); 113 | } 114 | } 115 | 116 | public function testMapType() 117 | { 118 | $type = Type::getType(MapType::NAME); 119 | 120 | // to DB value 121 | $output = $type->convertToDatabaseValue(array('foo'=>'bar'), $this->platform); 122 | $this->assertEquals($output, array('foo'=>'bar')); 123 | 124 | $output = $type->convertToDatabaseValue(array(), $this->platform); 125 | $this->assertEquals($output, array()); 126 | } 127 | 128 | public function testMapTypeNullValue() 129 | { 130 | $type = Type::getType(MapType::NAME); 131 | 132 | // to DB value 133 | $output = $type->convertToDatabaseValue(null, $this->platform); 134 | $this->assertEquals($output, null); 135 | } 136 | 137 | public function testMapTypeInvalid() 138 | { 139 | $type = Type::getType(MapType::NAME); 140 | 141 | // to DB value 142 | $notAMap = array('foo', 'bar'); 143 | $output = $type->convertToDatabaseValue($notAMap, $this->platform); 144 | $this->assertEquals($output, null); 145 | 146 | } 147 | 148 | public function testArrayType() 149 | { 150 | $type = Type::getType(ArrayType::NAME); 151 | 152 | // to DB value 153 | $output = $type->convertToDatabaseValue(array('foo', 'bar'), $this->platform); 154 | $this->assertEquals($output, array('foo', 'bar')); 155 | 156 | $output = $type->convertToDatabaseValue(array(), $this->platform); 157 | $this->assertEquals($output, array()); 158 | } 159 | 160 | public function testArrayTypeNullValue() 161 | { 162 | $type = Type::getType(ArrayType::NAME); 163 | 164 | // to DB value 165 | $output = $type->convertToDatabaseValue(null, $this->platform); 166 | $this->assertEquals($output, null); 167 | } 168 | 169 | public function testArrayTypeInvalid() 170 | { 171 | $type = Type::getType(ArrayType::NAME); 172 | 173 | // to DB value 174 | $notAnArray = array('foo'=>'bar'); 175 | $output = $type->convertToDatabaseValue($notAnArray, $this->platform); 176 | $this->assertEquals($output, null); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/Types/MapTypeTest.php: -------------------------------------------------------------------------------- 1 | _conn->getDatabasePlatform(); 37 | 38 | $table = new Table('items'); 39 | $objDefinition = array( 40 | 'type' => MapType::STRICT, 41 | 'fields' => array( 42 | new Column('id', Type::getType(Type::INTEGER), array()), 43 | new Column('name', Type::getType(Type::STRING), array()), 44 | ), 45 | ); 46 | $table->addColumn( 47 | 'object_column', MapType::NAME, 48 | array('platformOptions' => $objDefinition) 49 | ); 50 | 51 | $createFlags = CratePlatform::CREATE_INDEXES|CratePlatform::CREATE_FOREIGNKEYS; 52 | $sql = $platform->getCreateTableSQL($table, $createFlags); 53 | $this->assertEquals(array('CREATE TABLE items (object_column OBJECT ( strict ) AS ( id INTEGER, name TEXT ))'), $sql); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Functional/WriteTest.php: -------------------------------------------------------------------------------- 1 | addColumn('test_int', Type::INTEGER); 44 | $table->addColumn('test_string', Type::STRING); 45 | $table->addColumn('test_float', Type::FLOAT); 46 | $table->addColumn('test_array', Type::TARRAY, array('columnDefinition'=>'ARRAY(STRING)')); 47 | $table->addColumn("test_map", MapType::NAME); 48 | $table->addColumn("test_bool", Type::BOOLEAN); 49 | 50 | $platformOptions = array( 51 | 'type' => MapType::STRICT, 52 | 'fields' => array( 53 | new Column('id', Type::getType(Type::INTEGER), array()), 54 | new Column('name', Type::getType(Type::STRING), array()), 55 | new Column('value', Type::getType(Type::FLOAT), array()), 56 | ), 57 | ); 58 | $table->addColumn('test_obj', MapType::NAME, array('platformOptions'=>$platformOptions)); 59 | 60 | $sm = $this->_conn->getSchemaManager(); 61 | $sm->createTable($table); 62 | } 63 | } 64 | 65 | public function tearDown() : void 66 | { 67 | if (self::$generated === true) { 68 | $this->execute('drop table write_table'); 69 | self::$generated = false; 70 | } 71 | } 72 | 73 | 74 | /** 75 | * @group DBAL-80 76 | */ 77 | public function testExecuteUpdateFirstTypeIsNull() 78 | { 79 | $sql = "INSERT INTO write_table (test_string, test_int) VALUES (?, ?)"; 80 | $this->_conn->executeUpdate($sql, array("text", 1111), array(null, PDO::PARAM_INT)); 81 | $this->refresh('write_table'); 82 | 83 | $sql = "SELECT test_obj, test_string, test_int FROM write_table WHERE test_string = ? AND test_int = ?"; 84 | $this->assertEquals($this->_conn->fetchColumn($sql, array("text", 1111)), null); 85 | $this->assertEquals($this->_conn->fetchColumn($sql, array("text", 1111), 1), "text"); 86 | $this->assertEquals($this->_conn->fetchColumn($sql, array("text", 1111), 2), 1111); 87 | } 88 | 89 | public function testExecuteUpdate() 90 | { 91 | $sql = "INSERT INTO write_table (test_int) VALUES ( " . $this->_conn->quote(1, PDO::PARAM_INT) . ")"; 92 | $affected = $this->_conn->executeUpdate($sql); 93 | 94 | $this->assertEquals(1, $affected, "executeUpdate() should return the number of affected rows!"); 95 | } 96 | 97 | public function testExecuteUpdateWithTypes() 98 | { 99 | $sql = "INSERT INTO write_table (test_int, test_string) VALUES (?, ?)"; 100 | $affected = $this->_conn->executeUpdate($sql, array(1, 'foo'), array(\PDO::PARAM_INT, \PDO::PARAM_STR)); 101 | 102 | $this->assertEquals(1, $affected, "executeUpdate() should return the number of affected rows!"); 103 | } 104 | 105 | public function testPrepareRowCountReturnsAffectedRows() 106 | { 107 | $sql = "INSERT INTO write_table (test_int, test_string) VALUES (?, ?)"; 108 | $stmt = $this->_conn->prepare($sql); 109 | 110 | $stmt->bindValue(1, 1); 111 | $stmt->bindValue(2, "foo"); 112 | $stmt->execute(); 113 | 114 | $this->assertEquals(1, $stmt->rowCount()); 115 | } 116 | 117 | public function testPrepareWithPdoTypes() 118 | { 119 | $sql = "INSERT INTO write_table (test_int, test_string) VALUES (?, ?)"; 120 | $stmt = $this->_conn->prepare($sql); 121 | 122 | $stmt->bindValue(1, 1, \PDO::PARAM_INT); 123 | $stmt->bindValue(2, "foo", \PDO::PARAM_STR); 124 | $stmt->execute(); 125 | 126 | $this->assertEquals(1, $stmt->rowCount()); 127 | } 128 | 129 | public function testPrepareWithDbalTypes() 130 | { 131 | $sql = "INSERT INTO write_table (test_int, test_string, test_float, test_obj) VALUES (?, ?, ?, ?)"; 132 | $stmt = $this->_conn->prepare($sql); 133 | 134 | $stmt->bindValue(1, 1, Type::getType('integer')); 135 | $stmt->bindValue(2, "foo", Type::getType('string')); 136 | $stmt->bindValue(3, 3.141592, Type::getType('float')); 137 | $stmt->bindValue(4, array('id'=>1, 'name'=>'christian', 'value'=>1.234), Type::getType('map')); 138 | $stmt->execute(); 139 | 140 | $this->assertEquals(1, $stmt->rowCount()); 141 | } 142 | 143 | public function testPrepareWithDbalTypeNames() 144 | { 145 | $sql = "INSERT INTO write_table (test_int, test_string, test_float, test_map, test_bool) VALUES (?, ?, ?, ?, ?)"; 146 | $stmt = $this->_conn->prepare($sql); 147 | 148 | $stmt->bindValue(1, 1, 'integer'); 149 | $stmt->bindValue(2, "foo", 'string'); 150 | $stmt->bindValue(3, 3.141592, 'float'); 151 | $stmt->bindValue(4, array('id'=>1, 'name'=>'christian', 'value'=>1.234), 'map'); 152 | $stmt->bindValue(5, true, 'boolean'); 153 | $stmt->execute(); 154 | 155 | $this->assertEquals(1, $stmt->rowCount()); 156 | } 157 | 158 | public function insertRows() 159 | { 160 | $this->assertEquals(1, $this->_conn->insert('write_table', array( 161 | 'test_int' => 1, 162 | 'test_string' => 'foo', 163 | 'test_float' => 1.234, 164 | 'test_array' => array('foo','bar'), 165 | 'test_obj' => array('id'=>1, 'name'=>'foo', 'value'=>1.234), 166 | ), array('integer','string','float','array','map'))); 167 | $this->assertEquals(1, $this->_conn->insert('write_table', array( 168 | 'test_int' => 2, 169 | 'test_string' => 'bar', 170 | 'test_float' => 2.345, 171 | 'test_array' => array('bar','foo'), 172 | 'test_obj' => array('id'=>2, 'name'=>'bar', 'value'=>2.345), 173 | ), array('integer','string','float','array','map'))); 174 | 175 | $this->refresh('write_table'); 176 | } 177 | 178 | public function testInsert() 179 | { 180 | $this->insertRows(); 181 | } 182 | 183 | public function testDelete() 184 | { 185 | $this->insertRows(); 186 | 187 | $this->assertEquals(1, $this->_conn->delete('write_table', array('test_int' => 2))); 188 | $this->refresh('write_table'); 189 | $this->assertEquals(1, count($this->_conn->fetchAll('SELECT * FROM write_table'))); 190 | 191 | $this->assertEquals(1, $this->_conn->delete('write_table', array('test_int' => 1))); 192 | $this->refresh('write_table'); 193 | $this->assertEquals(0, count($this->_conn->fetchAll('SELECT * FROM write_table'))); 194 | } 195 | 196 | public function testUpdate() 197 | { 198 | $this->insertRows(); 199 | 200 | $this->assertEquals(1, $this->_conn->update('write_table', array('test_string' => 'bar'), array('test_string' => 'foo'))); 201 | $this->refresh('write_table'); 202 | $this->assertEquals(2, $this->_conn->update('write_table', array('test_string' => 'baz'), array('test_string' => 'bar'))); 203 | $this->refresh('write_table'); 204 | $this->assertEquals(0, $this->_conn->update('write_table', array('test_string' => 'baz'), array('test_string' => 'bar'))); 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/PDOCrate/DriverTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(CratePlatform::class, $driver->createDatabasePlatformForVersion("0.56.6")); 39 | 40 | } 41 | 42 | public function testCreatePlatformForVersionLess_4() 43 | { 44 | $driver = new Driver(); 45 | $this->assertInstanceOf(CratePlatform1::class, $driver->createDatabasePlatformForVersion("3.2")); 46 | 47 | } 48 | 49 | public function testCreatePlatformForVersionGreaterEquals_4() 50 | { 51 | $driver = new Driver(); 52 | $this->assertInstanceOf(CratePlatform4::class, $driver->createDatabasePlatformForVersion("4.0.0")); 53 | } 54 | } -------------------------------------------------------------------------------- /test/Crate/Test/DBAL/Platforms/CratePlatformTest.php: -------------------------------------------------------------------------------- 1 | createDatabasePlatformForVersion(self::CRATE_TEST_VERSION); 48 | } 49 | 50 | public function getGenerateTableSql() : string 51 | { 52 | return 'CREATE TABLE test (id INTEGER, test TEXT, PRIMARY KEY(id))'; 53 | } 54 | 55 | public function getGenerateTableWithMultiColumnUniqueIndexSql() : array 56 | { 57 | } 58 | 59 | public function getGenerateTableWithMultiColumnIndexSql() 60 | { 61 | return array( 62 | 'CREATE TABLE test (foo TEXT, bar TEXT, ' . 63 | 'INDEX IDX_D87F7E0C8C73652176FF8CAA USING FULLTEXT (foo, bar))' 64 | ); 65 | } 66 | 67 | public function getGenerateIndexSql() : string 68 | { 69 | $this->markTestSkipped('Platform does not support CREATE INDEX.'); 70 | } 71 | 72 | public function getGenerateUniqueIndexSql() : string 73 | { 74 | $this->markTestSkipped('Platform does not support CREATE UNIQUE INDEX.'); 75 | } 76 | 77 | public function testGeneratesForeignKeyCreationSql() : void 78 | { 79 | $this->markTestSkipped('Platform does not support FOREIGN KEY constraints.'); 80 | } 81 | 82 | public function getGenerateForeignKeySql() : string 83 | { 84 | $this->markTestSkipped('Platform does not support ADD FOREIGN KEY.'); 85 | } 86 | 87 | /** 88 | * @param mixed[] $column 89 | * 90 | * @group DBAL-1082 91 | * @dataProvider getGeneratesDecimalTypeDeclarationSQL 92 | */ 93 | public function testGeneratesDecimalTypeDeclarationSQL(array $column, $expectedSql) : void 94 | { 95 | $this->markTestSkipped('Platform does not support any decleration of datatype DECIMAL.'); 96 | } 97 | 98 | public function getGenerateAlterTableSql() : array 99 | { 100 | return array( 101 | 'ALTER TABLE mytable ADD quota INTEGER', 102 | ); 103 | } 104 | 105 | public function testAlterTableChangeQuotedColumn() : void 106 | { 107 | $this->markTestSkipped('Platform does not support ALTER TABLE.'); 108 | } 109 | 110 | protected function getQuotedColumnInPrimaryKeySQL() : array 111 | { 112 | return array( 113 | 'CREATE TABLE "quoted" ("create" TEXT, PRIMARY KEY("create"))', 114 | ); 115 | } 116 | 117 | protected function getQuotedColumnInIndexSQL() : array 118 | { 119 | return array( 120 | 'CREATE TABLE "quoted" ("create" TEXT, ' . 121 | 'INDEX IDX_22660D028FD6E0FB USING FULLTEXT ("create")' . 122 | ')' 123 | ); 124 | } 125 | 126 | protected function getQuotedNameInIndexSQL() : array 127 | { 128 | return array( 129 | 'CREATE TABLE test (column1 TEXT, INDEX key USING FULLTEXT (column1))' 130 | ); 131 | } 132 | 133 | /** 134 | * @group DBAL-374 135 | */ 136 | public function testQuotedColumnInForeignKeyPropagation() : void 137 | { 138 | $this->markTestSkipped('Platform does not support ADD FOREIGN KEY.'); 139 | } 140 | 141 | protected function getQuotedColumnInForeignKeySQL() : array {} 142 | 143 | protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string 144 | { 145 | return 'CONSTRAINT "select" UNIQUE (foo)'; 146 | } 147 | 148 | protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string 149 | { 150 | return 'INDEX "select" USING FULLTEXT (foo)'; 151 | } 152 | 153 | /** 154 | * @group DBAL-835 155 | */ 156 | public function testQuotesAlterTableRenameColumn() : void 157 | { 158 | $this->markTestSkipped('Platform does not support ALTER TABLE.'); 159 | } 160 | 161 | protected function getQuotedAlterTableRenameColumnSQL() : array {} 162 | 163 | /** 164 | * @group DBAL-835 165 | */ 166 | public function testQuotesAlterTableChangeColumnLength() : void 167 | { 168 | $this->markTestSkipped('Platform does not support ALTER TABLE.'); 169 | } 170 | 171 | protected function getQuotedAlterTableChangeColumnLengthSQL() : array {} 172 | 173 | /** 174 | * @group DBAL-807 175 | */ 176 | public function testQuotesAlterTableRenameIndexInSchema() : void 177 | { 178 | $this->markTestSkipped('Platform does not support ALTER TABLE.'); 179 | } 180 | 181 | protected function getCommentOnColumnSQL() : array 182 | { 183 | return array( 184 | "COMMENT ON COLUMN foo.bar IS 'comment'", 185 | "COMMENT ON COLUMN \"Foo\".\"BAR\" IS 'comment'", 186 | "COMMENT ON COLUMN \"select\".\"from\" IS 'comment'", 187 | ); 188 | } 189 | 190 | /** 191 | * @group DBAL-1010 192 | */ 193 | public function testGeneratesAlterTableRenameColumnSQL() : void 194 | { 195 | $this->markTestSkipped('Platform does not support ALTER TABLE.'); 196 | } 197 | 198 | public function getAlterTableRenameColumnSQL() : array {} 199 | 200 | /** 201 | * @group DBAL-1016 202 | */ 203 | public function testQuotesTableIdentifiersInAlterTableSQL() : void 204 | { 205 | $this->markTestSkipped('Platform does not support ALTER TABLE.'); 206 | } 207 | 208 | protected function getQuotesTableIdentifiersInAlterTableSQL() : array {} 209 | 210 | /** 211 | * @group DBAL-1062 212 | */ 213 | public function testGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : void 214 | { 215 | $this->markTestSkipped('Platform does not support ALTER TABLE.'); 216 | } 217 | 218 | protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array {} 219 | 220 | /** 221 | * @group DBAL-1090 222 | */ 223 | public function testAlterStringToFixedString() : void 224 | { 225 | $this->markTestSkipped('Platform does not support ALTER TABLE.'); 226 | } 227 | 228 | protected function getAlterStringToFixedStringSQL() : array {} 229 | 230 | public function testGenerateSubstrExpression() 231 | { 232 | $this->assertEquals($this->platform->getSubstringExpression('col', 0), "SUBSTR(col, 0)"); 233 | $this->assertEquals($this->platform->getSubstringExpression('col', 1, 2), "SUBSTR(col, 1, 2)"); 234 | } 235 | 236 | public function testGenerateNowExpression() 237 | { 238 | $this->expectException(DBALException::class); 239 | $this->expectExceptionMessage('Operation \'Crate\DBAL\Platforms\CratePlatform::getNowExpression\' is not supported by platform.'); 240 | $this->platform->getNowExpression(); 241 | } 242 | 243 | public function testGenerateRegexExpression() 244 | { 245 | $this->assertEquals($this->platform->getRegexpExpression(), "LIKE"); 246 | } 247 | 248 | public function testGenerateDateDiffExpression() 249 | { 250 | $this->expectException(DBALException::class); 251 | $this->expectExceptionMessage('Operation \'Crate\DBAL\Platforms\CratePlatform::getDateDiffExpression\' is not supported by platform.'); 252 | 253 | $this->platform->getDateDiffExpression('2014-10-10 10:10:10', '2014-10-20 20:20:20'); 254 | } 255 | 256 | public function testCreateDatabases() 257 | { 258 | $this->expectException(DBALException::class); 259 | $this->expectExceptionMessage('Operation \'Crate\DBAL\Platforms\CratePlatform::getCreateDatabaseSQL\' is not supported by platform.'); 260 | 261 | $this->platform->getCreateDatabaseSQL('foo'); 262 | } 263 | 264 | public function testListDatabases() 265 | { 266 | $this->expectException(DBALException::class); 267 | $this->expectExceptionMessage('Operation \'Crate\DBAL\Platforms\CratePlatform::getListDatabasesSQL\' is not supported by platform.'); 268 | 269 | $this->platform->getListDatabasesSQL(); 270 | } 271 | 272 | public function testDropDatabases() 273 | { 274 | $this->expectException(DBALException::class); 275 | $this->expectExceptionMessage('Operation \'Crate\DBAL\Platforms\CratePlatform::getDropDatabaseSQL\' is not supported by platform.'); 276 | 277 | $this->platform->getDropDatabaseSQL('foo'); 278 | } 279 | 280 | public function testGenerateBlobTypeGeneration() 281 | { 282 | $this->expectException(DBALException::class); 283 | $this->expectExceptionMessage('Operation \'Crate\DBAL\Platforms\CratePlatform::getBlobTypeDeclarationSQL\' is not supported by platform.'); 284 | 285 | $this->platform->getBlobTypeDeclarationSQL(array()); 286 | } 287 | 288 | public function testTruncateTableSQL() 289 | { 290 | $this->expectException(DBALException::class); 291 | 292 | $this->platform->getTruncateTableSQL('foo'); 293 | } 294 | 295 | public function testReadLockSQL() 296 | { 297 | $this->expectException(DBALException::class); 298 | 299 | $this->platform->getReadLockSQL(); 300 | } 301 | 302 | public function testConvertBooleans() 303 | { 304 | $this->assertEquals($this->platform->convertBooleans(false), 'false'); 305 | $this->assertEquals($this->platform->convertBooleans(true), 'true'); 306 | 307 | $this->assertEquals($this->platform->convertBooleans(0), 'false'); 308 | $this->assertEquals($this->platform->convertBooleans(1), 'true'); 309 | 310 | $this->assertEquals($this->platform->convertBooleans(array(true, 1, false, 0)), 311 | array('true', 'true', 'false', 'false')); 312 | } 313 | 314 | public function testSQLResultCasting() 315 | { 316 | $this->assertEquals($this->platform->getSQLResultCasing("LoWeRcAsE"), 'lowercase'); 317 | } 318 | 319 | public function testGenerateTableSqlWithoutColumns() 320 | { 321 | $this->expectException(DBALException::class); 322 | $this->expectExceptionMessage('No columns specified for table foo'); 323 | 324 | 325 | $table = new Table("foo"); 326 | $this->assertEquals($this->platform->getCreateTableSQL($table)[0], 327 | 'CREATE TABLE foo'); 328 | } 329 | 330 | public function testGenerateTableSql() 331 | { 332 | $table = new Table("foo"); 333 | $table->addColumn('col_bool', 'boolean'); 334 | $table->addColumn('col_int', 'integer'); 335 | $table->addColumn('col_float', 'float'); 336 | $table->addColumn('col_timestamp', 'timestamp'); 337 | $table->addColumn('col_datetimetz', 'datetimetz'); 338 | $table->addColumn('col_datetime', 'datetime'); 339 | $table->addColumn('col_date', 'date'); 340 | $table->addColumn('col_time', 'time'); 341 | $table->addColumn('col_array', 'array'); 342 | $table->addColumn('col_object', 'map'); 343 | $this->assertEquals($this->platform->getCreateTableSQL($table)[0], 344 | 'CREATE TABLE foo (col_bool BOOLEAN, col_int INTEGER, col_float DOUBLE PRECISION, col_timestamp TIMESTAMP, col_datetimetz TIMESTAMP, col_datetime TIMESTAMP, col_date TIMESTAMP, col_time TIMESTAMP, col_array ARRAY ( TEXT ), col_object OBJECT ( dynamic ))'); 345 | } 346 | 347 | public function testUnsupportedUniqueIndexConstraint() 348 | { 349 | $this->expectException(DBALException::class); 350 | $this->expectExceptionMessage("Unique constraints are not supported. Use `primary key` instead"); 351 | 352 | $table = new Table("foo"); 353 | $table->addColumn("unique_string", "string"); 354 | $table->addUniqueIndex(array("unique_string")); 355 | $this->platform->getCreateTableSQL($table); 356 | } 357 | 358 | public function testUniqueConstraintInCustomSchemaOptions() 359 | { 360 | $this->expectException(DBALException::class); 361 | $this->expectExceptionMessage("Unique constraints are not supported. Use `primary key` instead"); 362 | 363 | $table = new Table("foo"); 364 | $table->addColumn("unique_string", "string")->setCustomSchemaOption("unique", true); 365 | $this->platform->getCreateTableSQL($table); 366 | } 367 | 368 | public function testGeneratesTableAlterationSql() : void 369 | { 370 | $expectedSql = $this->getGenerateAlterTableSql(); 371 | 372 | $tableDiff = new TableDiff('mytable'); 373 | $tableDiff->addedColumns['quota'] = new \Doctrine\DBAL\Schema\Column('quota', \Doctrine\DBAL\Types\Type::getType('integer'), array('notnull' => false)); 374 | 375 | $sql = $this->platform->getAlterTableSQL($tableDiff); 376 | 377 | $this->assertEquals($expectedSql, $sql); 378 | } 379 | 380 | public function testGetAlterTableSqlDispatchEvent() : void 381 | { 382 | $events = array( 383 | 'onSchemaAlterTableAddColumn' 384 | ); 385 | 386 | $listenerMock = $this->getMockBuilder('GetAlterTableSqlDispatchEvenListener') 387 | ->setMethods($events) 388 | ->getMock(); 389 | $listenerMock 390 | ->expects($this->once()) 391 | ->method('onSchemaAlterTableAddColumn'); 392 | 393 | $eventManager = new EventManager(); 394 | $events = array( 395 | Events::onSchemaAlterTableAddColumn, 396 | ); 397 | $eventManager->addEventListener($events, $listenerMock); 398 | 399 | $this->platform->setEventManager($eventManager); 400 | 401 | $tableDiff = new TableDiff('mytable'); 402 | $tableDiff->addedColumns['added'] = new \Doctrine\DBAL\Schema\Column('added', \Doctrine\DBAL\Types\Type::getType('integer'), array()); 403 | 404 | $this->platform->getAlterTableSQL($tableDiff); 405 | } 406 | 407 | public function testGenerateTableWithMultiColumnUniqueIndex() : void 408 | { 409 | $table = new Table('test'); 410 | $table->addColumn('foo', 'string', array('notnull' => false, 'length' => 255)); 411 | $table->addColumn('bar', 'string', array('notnull' => false, 'length' => 255)); 412 | $table->addUniqueIndex(array("foo", "bar")); 413 | 414 | $this->expectException(DBALException::class); 415 | $this->expectExceptionMessage('Operation \'Unique constraints are not supported. Use `primary key` instead\' is not supported by platform.'); 416 | 417 | $this->platform->getCreateTableSQL($table); 418 | } 419 | 420 | public function testGenerateTableWithMultiColumnIndex() 421 | { 422 | $table = new Table('test'); 423 | $table->addColumn('foo', 'string', array('notnull' => false, 'length' => 255)); 424 | $table->addColumn('bar', 'string', array('notnull' => false, 'length' => 255)); 425 | $table->addIndex(array("foo", "bar")); 426 | 427 | $sql = $this->platform->getCreateTableSQL($table); 428 | $this->assertEquals($this->getGenerateTableWithMultiColumnIndexSql(), $sql); 429 | } 430 | 431 | /** 432 | * @param Column $column 433 | */ 434 | private function getSQLDeclaration($column) 435 | { 436 | $p = $this->platform; 437 | return $p->getColumnDeclarationSQL($column->getName(), CratePlatform::prepareColumnData($p, $column)); 438 | } 439 | 440 | public function testGenerateObjectSQLDeclaration() 441 | { 442 | 443 | $column = new Column('obj', Type::getType(MapType::NAME)); 444 | $this->assertEquals($this->getSQLDeclaration($column), 'obj OBJECT ( dynamic )'); 445 | 446 | $column = new Column('obj', Type::getType(MapType::NAME), 447 | array('platformOptions'=>array('type'=>MapType::STRICT))); 448 | $this->assertEquals($this->getSQLDeclaration($column), 'obj OBJECT ( strict )'); 449 | 450 | $column = new Column('obj', Type::getType(MapType::NAME), 451 | array('platformOptions'=>array('type'=>MapType::IGNORED, 'fields'=>array()))); 452 | $this->assertEquals($this->getSQLDeclaration($column), 'obj OBJECT ( ignored )'); 453 | 454 | $column = new Column('obj', Type::getType(MapType::NAME), 455 | array('platformOptions'=>array( 456 | 'type'=>MapType::STRICT, 457 | 'fields'=>array( 458 | new Column('num', Type::getType(Type::INTEGER)), 459 | new Column('text', Type::getType(Type::STRING)), 460 | new Column('arr', Type::getType(ArrayType::NAME)), 461 | new Column('obj', Type::getType(MapType::NAME)), 462 | ), 463 | ))); 464 | $this->assertEquals($this->getSQLDeclaration($column), 'obj OBJECT ( strict ) AS ( num INTEGER, text TEXT, arr ARRAY ( TEXT ), obj OBJECT ( dynamic ) )'); 465 | 466 | } 467 | 468 | public function testGenerateArraySQLDeclaration() 469 | { 470 | $column = new Column('arr', Type::getType(ArrayType::NAME)); 471 | $this->assertEquals($this->getSQLDeclaration($column), 'arr ARRAY ( TEXT )'); 472 | 473 | $column = new Column('arr', Type::getType(ArrayType::NAME), 474 | array('platformOptions'=> array('type'=>Type::INTEGER))); 475 | $this->assertEquals($this->getSQLDeclaration($column), 'arr ARRAY ( INTEGER )'); 476 | 477 | } 478 | 479 | public function testPlatformSupport() { 480 | $this->assertFalse($this->platform->supportsSequences()); 481 | $this->assertFalse($this->platform->supportsSchemas()); 482 | $this->assertTrue($this->platform->supportsIdentityColumns()); 483 | $this->assertFalse($this->platform->supportsIndexes()); 484 | $this->assertFalse($this->platform->supportsCommentOnStatement()); 485 | $this->assertFalse($this->platform->supportsForeignKeyConstraints()); 486 | $this->assertFalse($this->platform->supportsForeignKeyOnUpdate()); 487 | $this->assertFalse($this->platform->supportsViews()); 488 | $this->assertFalse($this->platform->prefersSequences()); 489 | } 490 | 491 | /** 492 | * @return string 493 | */ 494 | protected function getQuotesReservedKeywordInTruncateTableSQL() : string 495 | { 496 | $this->markTestSkipped('Platform does not support TRUNCATE TABLE.'); 497 | } 498 | 499 | /** 500 | * @return array}> 501 | */ 502 | public function asciiStringSqlDeclarationDataProvider() : array 503 | { 504 | return [ 505 | ['TEXT', ['length' => 12]], 506 | ['TEXT', ['length' => 12, 'fixed' => true]], 507 | ]; 508 | } 509 | } 510 | --------------------------------------------------------------------------------