├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── AUTHORS ├── BUGS ├── CONTRIBUTING.md ├── CREDITS ├── LICENSE ├── README.md ├── THANKS ├── TODO ├── config.m4 ├── config9.m4 ├── package.xml ├── php_pq.h ├── php_pq_type.h ├── scripts ├── gen_github_workflow_ci.php ├── gen_pq_type.sh ├── php_pq_type-pg11.php └── php_pq_type.awk ├── src ├── php_pq_callback.c ├── php_pq_callback.h ├── php_pq_misc.c ├── php_pq_misc.h ├── php_pq_module.c ├── php_pq_object.c ├── php_pq_object.h ├── php_pq_params.c ├── php_pq_params.h ├── php_pqcancel.c ├── php_pqcancel.h ├── php_pqconn.c ├── php_pqconn.h ├── php_pqconn_event.c ├── php_pqconn_event.h ├── php_pqcopy.c ├── php_pqcopy.h ├── php_pqcur.c ├── php_pqcur.h ├── php_pqexc.c ├── php_pqexc.h ├── php_pqlob.c ├── php_pqlob.h ├── php_pqres.c ├── php_pqres.h ├── php_pqstm.c ├── php_pqstm.h ├── php_pqtxn.c ├── php_pqtxn.h ├── php_pqtypes.c └── php_pqtypes.h └── tests ├── _setup.inc ├── _skipif.inc ├── async001.phpt ├── async002.phpt ├── async003.phpt ├── async004.phpt ├── async005.phpt ├── async006.phpt ├── async007.phpt ├── async008.phpt ├── async009.phpt ├── async010.phpt ├── basic001.phpt ├── basic002.phpt ├── basic003.phpt ├── bound002.phpt ├── callback001.phpt ├── callback002.phpt ├── callback003.phpt ├── cancel001.phpt ├── conv001.phpt ├── copy001.phpt ├── crash_cur_reverse_dep.phpt ├── crash_result_iterator.phpt ├── crash_stm_reverse_dep.phpt ├── crash_txn_reverse_dep.phpt ├── crash_unbuffered_async_prepare.phpt ├── cursor001.phpt ├── encoding001.phpt ├── exceptions001.phpt ├── exceptions002.phpt ├── fetch001.phpt ├── flush001.phpt ├── gh-issue015_listeners.phpt ├── gh-issue015_statements.phpt ├── gh-issue047_jsonb.phpt ├── info001.phpt ├── info002.phpt ├── lob001.phpt ├── lob002.phpt ├── lob003.phpt ├── lob004.phpt ├── map001.phpt ├── notify001.phpt ├── persistent001.phpt ├── res001.phpt ├── reset001.phpt ├── savepoint001.phpt ├── stm_bound001.phpt ├── stm_deallocate_prepare001.phpt ├── stm_desc001.phpt ├── stm_desc002.phpt ├── stm_props001.phpt ├── trans001.phpt ├── trans002.phpt ├── types001.phpt ├── types002.phpt └── unbuffered001.phpt /.editorconfig: -------------------------------------------------------------------------------- 1 | ; see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = tab 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.json] 15 | indent_style = space 16 | indent_size = 4 17 | 18 | [package.xml] 19 | indent_style = space 20 | indent_size = 1 21 | 22 | [config.w32] 23 | end_of_line = crlf 24 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | package.xml merge=touch 2 | php_pq.h merge=touch 3 | *.phar binary 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # generated file; do not edit! 2 | 3 | name: ci 4 | on: 5 | workflow_dispatch: 6 | push: 7 | pull_request: 8 | 9 | jobs: 10 | old-matrix-0: 11 | name: "old-matrix-0 (7.4)" 12 | env: 13 | PHP: "7.4" 14 | enable_debug: "yes" 15 | enable_maintainer_zts: "yes" 16 | enable_json: "yes" 17 | PQ_DSN: "postgres:///runner" 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - uses: actions/checkout@v2 21 | with: 22 | submodules: true 23 | - name: Install 24 | run: | 25 | sudo apt-get install -y \ 26 | php-cli \ 27 | php-pear \ 28 | libpq-dev \ 29 | re2c 30 | - name: Prepare 31 | run: | 32 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 33 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 34 | - name: Build 35 | run: | 36 | make -f scripts/ci/Makefile ext PECL=pq 37 | - name: Prepare Test 38 | run: | 39 | sudo systemctl start postgresql 40 | sudo -u postgres createuser --login runner 41 | sudo -u postgres createdb -O runner runner 42 | - name: Test 43 | run: | 44 | make -f scripts/ci/Makefile test 45 | 46 | old-matrix-1: 47 | name: "old-matrix-1 (8.0)" 48 | env: 49 | PHP: "8.0" 50 | enable_debug: "yes" 51 | enable_maintainer_zts: "yes" 52 | enable_json: "yes" 53 | PQ_DSN: "postgres:///runner" 54 | runs-on: ubuntu-22.04 55 | steps: 56 | - uses: actions/checkout@v2 57 | with: 58 | submodules: true 59 | - name: Install 60 | run: | 61 | sudo apt-get install -y \ 62 | php-cli \ 63 | php-pear \ 64 | libpq-dev \ 65 | re2c 66 | - name: Prepare 67 | run: | 68 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 69 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 70 | - name: Build 71 | run: | 72 | make -f scripts/ci/Makefile ext PECL=pq 73 | - name: Prepare Test 74 | run: | 75 | sudo systemctl start postgresql 76 | sudo -u postgres createuser --login runner 77 | sudo -u postgres createdb -O runner runner 78 | - name: Test 79 | run: | 80 | make -f scripts/ci/Makefile test 81 | 82 | old-matrix-2: 83 | name: "old-matrix-2 (8.1)" 84 | env: 85 | PHP: "8.1" 86 | enable_debug: "yes" 87 | enable_maintainer_zts: "yes" 88 | enable_json: "yes" 89 | PQ_DSN: "postgres:///runner" 90 | runs-on: ubuntu-22.04 91 | steps: 92 | - uses: actions/checkout@v2 93 | with: 94 | submodules: true 95 | - name: Install 96 | run: | 97 | sudo apt-get install -y \ 98 | php-cli \ 99 | php-pear \ 100 | libpq-dev \ 101 | re2c 102 | - name: Prepare 103 | run: | 104 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 105 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 106 | - name: Build 107 | run: | 108 | make -f scripts/ci/Makefile ext PECL=pq 109 | - name: Prepare Test 110 | run: | 111 | sudo systemctl start postgresql 112 | sudo -u postgres createuser --login runner 113 | sudo -u postgres createdb -O runner runner 114 | - name: Test 115 | run: | 116 | make -f scripts/ci/Makefile test 117 | 118 | old-matrix-3: 119 | name: "old-matrix-3 (8.2)" 120 | env: 121 | PHP: "8.2" 122 | enable_debug: "yes" 123 | enable_maintainer_zts: "yes" 124 | enable_json: "yes" 125 | PQ_DSN: "postgres:///runner" 126 | runs-on: ubuntu-22.04 127 | steps: 128 | - uses: actions/checkout@v2 129 | with: 130 | submodules: true 131 | - name: Install 132 | run: | 133 | sudo apt-get install -y \ 134 | php-cli \ 135 | php-pear \ 136 | libpq-dev \ 137 | re2c 138 | - name: Prepare 139 | run: | 140 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 141 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 142 | - name: Build 143 | run: | 144 | make -f scripts/ci/Makefile ext PECL=pq 145 | - name: Prepare Test 146 | run: | 147 | sudo systemctl start postgresql 148 | sudo -u postgres createuser --login runner 149 | sudo -u postgres createdb -O runner runner 150 | - name: Test 151 | run: | 152 | make -f scripts/ci/Makefile test 153 | 154 | next-0: 155 | name: "next-0 (master)" 156 | continue-on-error: true 157 | env: 158 | PHP: "master" 159 | enable_debug: "yes" 160 | enable_zts: "yes" 161 | PQ_DSN: "postgres:///runner" 162 | runs-on: ubuntu-22.04 163 | steps: 164 | - uses: actions/checkout@v2 165 | with: 166 | submodules: true 167 | - name: Install 168 | run: | 169 | sudo apt-get install -y \ 170 | php-cli \ 171 | php-pear \ 172 | libpq-dev \ 173 | re2c 174 | - name: Prepare 175 | run: | 176 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 177 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 178 | - name: Build 179 | run: | 180 | make -f scripts/ci/Makefile ext PECL=pq 181 | - name: Prepare Test 182 | run: | 183 | sudo systemctl start postgresql 184 | sudo -u postgres createuser --login runner 185 | sudo -u postgres createdb -O runner runner 186 | - name: Test 187 | run: | 188 | make -f scripts/ci/Makefile test 189 | 190 | cur-dbg-zts-0: 191 | name: "cur-dbg-zts-0 (8.3)" 192 | env: 193 | PHP: "8.3" 194 | enable_debug: "yes" 195 | enable_zts: "yes" 196 | PQ_DSN: "postgres:///runner" 197 | runs-on: ubuntu-22.04 198 | steps: 199 | - uses: actions/checkout@v2 200 | with: 201 | submodules: true 202 | - name: Install 203 | run: | 204 | sudo apt-get install -y \ 205 | php-cli \ 206 | php-pear \ 207 | libpq-dev \ 208 | re2c 209 | - name: Prepare 210 | run: | 211 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 212 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 213 | - name: Build 214 | run: | 215 | make -f scripts/ci/Makefile ext PECL=pq 216 | - name: Prepare Test 217 | run: | 218 | sudo systemctl start postgresql 219 | sudo -u postgres createuser --login runner 220 | sudo -u postgres createdb -O runner runner 221 | - name: Test 222 | run: | 223 | make -f scripts/ci/Makefile test 224 | 225 | cur-dbg-zts-1: 226 | name: "cur-dbg-zts-1 (8.3)" 227 | env: 228 | PHP: "8.3" 229 | enable_debug: "no" 230 | enable_zts: "yes" 231 | PQ_DSN: "postgres:///runner" 232 | runs-on: ubuntu-22.04 233 | steps: 234 | - uses: actions/checkout@v2 235 | with: 236 | submodules: true 237 | - name: Install 238 | run: | 239 | sudo apt-get install -y \ 240 | php-cli \ 241 | php-pear \ 242 | libpq-dev \ 243 | re2c 244 | - name: Prepare 245 | run: | 246 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 247 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 248 | - name: Build 249 | run: | 250 | make -f scripts/ci/Makefile ext PECL=pq 251 | - name: Prepare Test 252 | run: | 253 | sudo systemctl start postgresql 254 | sudo -u postgres createuser --login runner 255 | sudo -u postgres createdb -O runner runner 256 | - name: Test 257 | run: | 258 | make -f scripts/ci/Makefile test 259 | 260 | cur-dbg-zts-2: 261 | name: "cur-dbg-zts-2 (8.3)" 262 | env: 263 | PHP: "8.3" 264 | enable_debug: "yes" 265 | enable_zts: "no" 266 | PQ_DSN: "postgres:///runner" 267 | runs-on: ubuntu-22.04 268 | steps: 269 | - uses: actions/checkout@v2 270 | with: 271 | submodules: true 272 | - name: Install 273 | run: | 274 | sudo apt-get install -y \ 275 | php-cli \ 276 | php-pear \ 277 | libpq-dev \ 278 | re2c 279 | - name: Prepare 280 | run: | 281 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 282 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 283 | - name: Build 284 | run: | 285 | make -f scripts/ci/Makefile ext PECL=pq 286 | - name: Prepare Test 287 | run: | 288 | sudo systemctl start postgresql 289 | sudo -u postgres createuser --login runner 290 | sudo -u postgres createdb -O runner runner 291 | - name: Test 292 | run: | 293 | make -f scripts/ci/Makefile test 294 | 295 | cur-dbg-zts-3: 296 | name: "cur-dbg-zts-3 (8.3)" 297 | env: 298 | PHP: "8.3" 299 | enable_debug: "no" 300 | enable_zts: "no" 301 | PQ_DSN: "postgres:///runner" 302 | runs-on: ubuntu-22.04 303 | steps: 304 | - uses: actions/checkout@v2 305 | with: 306 | submodules: true 307 | - name: Install 308 | run: | 309 | sudo apt-get install -y \ 310 | php-cli \ 311 | php-pear \ 312 | libpq-dev \ 313 | re2c 314 | - name: Prepare 315 | run: | 316 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 317 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 318 | - name: Build 319 | run: | 320 | make -f scripts/ci/Makefile ext PECL=pq 321 | - name: Prepare Test 322 | run: | 323 | sudo systemctl start postgresql 324 | sudo -u postgres createuser --login runner 325 | sudo -u postgres createdb -O runner runner 326 | - name: Test 327 | run: | 328 | make -f scripts/ci/Makefile test 329 | 330 | cur-cov-0: 331 | name: "cur-cov-0 (8.3)" 332 | env: 333 | CFLAGS: "-O0 -g --coverage" 334 | CXXFLAGS: "-O0 -g --coverage" 335 | PHP: "8.3" 336 | PQ_DSN: "postgres:///runner" 337 | runs-on: ubuntu-22.04 338 | steps: 339 | - uses: actions/checkout@v2 340 | with: 341 | submodules: true 342 | - name: Install 343 | run: | 344 | sudo apt-get install -y \ 345 | php-cli \ 346 | php-pear \ 347 | libpq-dev \ 348 | re2c 349 | - name: Prepare 350 | run: | 351 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 352 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 353 | - name: Build 354 | run: | 355 | make -f scripts/ci/Makefile ext PECL=pq 356 | - name: Prepare Test 357 | run: | 358 | sudo systemctl start postgresql 359 | sudo -u postgres createuser --login runner 360 | sudo -u postgres createdb -O runner runner 361 | - name: Test 362 | run: | 363 | make -f scripts/ci/Makefile test 364 | - name: Coverage 365 | if: success() 366 | run: | 367 | cd src/.libs 368 | bash <(curl -s https://codecov.io/bash) -X xcode -X coveragepy 369 | 370 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # / 3 | *~ 4 | /*.tgz 5 | .deps 6 | *.dep 7 | *.o 8 | *.lo 9 | *.la 10 | /config.[^m]* 11 | /configure* 12 | /lib* 13 | /ac*.m4 14 | /ltmain.sh 15 | /install-sh 16 | /Make* 17 | /mk* 18 | /missing 19 | .libs 20 | /build 21 | /include 22 | /modules 23 | /autom4te* 24 | /.dep.inc 25 | run-tests.php 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "scripts/ci"] 2 | path = scripts/ci 3 | url = https://github.com/m6w6/pecl-ci.git 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Michael Wallner 2 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | Yay, no known and unresolved issues yet! 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all people who 5 | contribute through reporting issues, posting feature requests, updating 6 | documentation, submitting pull requests or patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free 9 | experience for everyone, regardless of level of experience, gender, gender 10 | identity and expression, sexual orientation, disability, personal appearance, 11 | body size, race, ethnicity, age, religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic 20 | addresses, without explicit permission 21 | * Other unethical or unprofessional conduct. 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or reject 24 | comments, commits, code, wiki edits, issues, and other contributions that are 25 | not aligned to this Code of Conduct. By adopting this Code of Conduct, project 26 | maintainers commit themselves to fairly and consistently applying these 27 | principles to every aspect of managing this project. Project maintainers who do 28 | not follow or enforce the Code of Conduct may be permanently removed from the 29 | project team. 30 | 31 | This code of conduct applies both within project spaces and in public spaces 32 | when an individual is representing the project or its community. 33 | 34 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 35 | reported by opening an issue or contacting one or more of the project maintainers. 36 | 37 | This Code of Conduct is adapted from the 38 | [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, 39 | available at http://contributor-covenant.org/version/1/2/0/. 40 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | pq 2 | Michael Wallner 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Michael Wallner . 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pecl/pq 2 | 3 | [![Build Status](https://github.com/m6w6/ext-pq/workflows/ci/badge.svg?branch=master)](https://github.com/m6w6/ext-pq/actions?query=branch%3Amaster+workflow%3Aci) 4 | [![codecov](https://codecov.io/gh/m6w6/ext-pq/branch/master/graph/badge.svg?token=Nku9tz8EMj)](https://codecov.io/gh/m6w6/ext-pq) 5 | 6 | ## About: 7 | 8 | This is a modern binding to the mature [libpq](http://www.postgresql.org/docs/current/static/libpq.html), the official PostgreSQL C-client library. 9 | 10 | ### Highlights: 11 | 12 | * Nearly 100% support for [asynchronous usage](https://mdref.m6w6.name/pq/Connection/:%20Asynchronous%20Usage). 13 | * Extended [type support by pg_type](https://mdref.m6w6.name/pq/Types/:%20Overview). 14 | * Fetching simple [multi-dimensional array maps](https://mdref.m6w6.name/pq/Result/map). 15 | * Working [Gateway implementation](https://github.com/m6w6/pq-gateway). 16 | 17 | ## Documentation 18 | 19 | See the [online markdown reference](https://mdref.m6w6.name/pq). 20 | 21 | Known issues are listed in [BUGS](./BUGS) and future ideas can be found in [TODO](./TODO). 22 | 23 | ## Install 24 | 25 | ### PECL 26 | 27 | pecl install pq 28 | 29 | ### PHARext 30 | 31 | Watch out for [PECL replicates](https://replicator.pharext.org?pq) 32 | and pharext packages attached to [releases](https://github.com/m6w6/ext-pq/releases). 33 | 34 | ### Checkout 35 | 36 | git clone github.com:m6w6/ext-pq 37 | cd ext-pq 38 | /path/to/phpize 39 | ./configure --with-php-config=/path/to/php-config 40 | make 41 | sudo make install 42 | 43 | ## Dependencies: 44 | 45 | This extension unconditionally depends on the pre-loaded presence of the following PHP extensions: 46 | 47 | - [raphf](http://pecl.php.net/package/raphf) 48 | - [spl](http://php.net/spl) 49 | 50 | It optionally depends on the following extensions: 51 | 52 | * [json](http://php.net/json) 53 | 54 | ## ChangeLog 55 | 56 | A comprehensive list of changes can be obtained from the 57 | [PECL website](https://pecl.php.net/package-changelog.php?package=pq). 58 | 59 | ## License 60 | 61 | ext-pq is licensed under the 2-Clause-BSD license, which can be found in 62 | the accompanying [LICENSE](./LICENSE) file. 63 | 64 | ## Contributing 65 | 66 | All forms of contribution are welcome! Please see the bundled 67 | [CONTRIBUTING](./CONTRIBUTING.md) note for the general principles followed. 68 | 69 | The list of past and current contributors is maintained in [THANKS](./THANKS). 70 | -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | Thanks go to the following people, who have contributed to this project: 2 | 3 | Remi Collet 4 | Chris Wright 5 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * COPY: getAsync & putAsync 2 | * fetchInto/fetchCtor? 3 | * binary protocol? 4 | * parse explicit array dimension information in front of arrays 5 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl phpize stub of config9.m4 for pecl/pq 2 | sinclude(config9.m4) 3 | -------------------------------------------------------------------------------- /config9.m4: -------------------------------------------------------------------------------- 1 | PHP_ARG_WITH(pq, [whether to enable libpq (PostgreSQL) support], 2 | [ --with-pq[=DIR] Include libpq support]) 3 | 4 | if test "$PHP_PQ" != "no"; then 5 | 6 | SEARCH_PATH="/usr/local /usr /opt" 7 | if test "$PHP_PQ" != "yes"; then 8 | SEARCH_PATH="$PHP_PQ $SEARCH_PATH" 9 | fi 10 | 11 | AC_MSG_CHECKING(for pg_config) 12 | for i in $SEARCH_PATH; do 13 | if test -x "$i/bin/pg_config"; then 14 | PG_CONFIG="$i/bin/pg_config" 15 | break 16 | fi 17 | done 18 | 19 | if test -z "$PG_CONFIG"; then 20 | AC_PATH_PROG(PG_CONFIG, pg_config, no) 21 | fi 22 | 23 | AC_MSG_RESULT($PG_CONFIG) 24 | 25 | if test "$PG_CONFIG" = "no"; then 26 | AC_MSG_ERROR(could not find a usable pg_config in $SEARCH_PATH) 27 | else 28 | if test "$PHP_PQ" != "yes" -a "$PHP_PQ/bin/pg_config" != "$PG_CONFIG"; then 29 | AC_MSG_WARN(Found pg_config is not in $PHP_PQ) 30 | fi 31 | 32 | AC_MSG_CHECKING(for PostgreSQL version) 33 | PQ_VERSION=$($PG_CONFIG --version | $SED 's/PostgreSQL //') 34 | 35 | if test -z "$PQ_VERSION"; then 36 | AC_MSG_RESULT(not found) 37 | AC_MSG_ERROR(\`$PG_CONFIG --version\` did not provide any meaningful output, please reinstall postgresql/libpq) 38 | else 39 | AC_MSG_RESULT($PQ_VERSION) 40 | AC_DEFINE_UNQUOTED(PHP_PQ_LIBVERSION, "$PQ_VERSION", [ ]) 41 | fi 42 | 43 | PQ_INCDIR=$($PG_CONFIG --includedir) 44 | PQ_LIBDIR=$($PG_CONFIG --libdir) 45 | fi 46 | 47 | PHP_ADD_INCLUDE($PQ_INCDIR) 48 | 49 | ifdef([AC_PROG_EGREP], [ 50 | AC_PROG_EGREP 51 | ], [ 52 | AC_CHECK_PROG(EGREP, egrep, egrep) 53 | ]) 54 | 55 | dnl 56 | dnl PQ_CHECK_CONST(name) 57 | dnl 58 | AC_DEFUN([PQ_CHECK_CONST], [ 59 | AC_MSG_CHECKING(for $1) 60 | if $EGREP -q $1 $PQ_INCDIR/libpq-fe.h; then 61 | AC_DEFINE(HAVE_$1, 1, [Have $1]) 62 | AC_MSG_RESULT(yep) 63 | else 64 | AC_MSG_RESULT(nope) 65 | fi 66 | ]) 67 | 68 | PQ_CHECK_CONST(PGRES_SINGLE_TUPLE) 69 | PQ_CHECK_CONST(PGRES_COPY_BOTH) 70 | 71 | PQ_CHECK_CONST(CONNECTION_CHECK_WRITABLE) 72 | PQ_CHECK_CONST(CONNECTION_CONSUME) 73 | PQ_CHECK_CONST(CONNECTION_GSS_STARTUP) 74 | 75 | dnl 76 | dnl PQ_CHECK_FUNC(sym, fail-hard) 77 | dnl 78 | AC_DEFUN([PQ_CHECK_FUNC], [ 79 | PQ_SYM=$1 80 | FAIL_HARD=$2 81 | save_LIBS="$LIBS" 82 | LIBS= 83 | PHP_CHECK_LIBRARY(pq, $1, [ 84 | AC_DEFINE([HAVE_]translit($1,a-z,A-Z), 1, Have $1) 85 | ], [ 86 | if test -n "$FAIL_HARD"; then 87 | if $FAIL_HARD; then 88 | AC_MSG_ERROR(could not find $PQ_SYM in -lpq -L$PQ_LIBDIR) 89 | fi 90 | fi 91 | ], [ 92 | -L$PQ_LIBDIR 93 | ]) 94 | LIBS="$save_LIBS" 95 | ]) 96 | 97 | PQ_CHECK_FUNC(PQregisterEventProc, true) 98 | PHP_ADD_LIBRARY_WITH_PATH(pq, $PQ_LIBDIR, PQ_SHARED_LIBADD) 99 | PHP_SUBST(PQ_SHARED_LIBADD) 100 | 101 | PQ_CHECK_FUNC(PQlibVersion) 102 | PQ_CHECK_FUNC(PQprotocolVersion) 103 | PQ_CHECK_FUNC(PQserverVersion) 104 | PQ_CHECK_FUNC(PQconninfo) 105 | PQ_CHECK_FUNC(PQsetSingleRowMode) 106 | 107 | dnl 108 | dnl PQ_HAVE_PHP_EXT(name[, code-if-yes[, code-if-not]]) 109 | dnl 110 | AC_DEFUN([PQ_HAVE_PHP_EXT], [ 111 | extname=$1 112 | haveext=$[PHP_]translit($1,a-z_-,A-Z__) 113 | AC_MSG_CHECKING([for ext/$extname support]) 114 | if test "$haveext" != "no" && test "x$haveext" != "x"; then 115 | [PHP_PQ_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1 116 | AC_MSG_RESULT([yes]) 117 | $2 118 | elif test -x "$PHP_EXECUTABLE"; then 119 | grepext=`$PHP_EXECUTABLE -m | $EGREP ^$extname\$` 120 | if test "$grepext" = "$extname"; then 121 | [PHP_PQ_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1 122 | AC_MSG_RESULT([yes]) 123 | $2 124 | else 125 | [PHP_PQ_HAVE_EXT_]translit($1,a-z_-,A-Z__)= 126 | AC_MSG_RESULT([no]) 127 | $3 128 | fi 129 | else 130 | [PHP_PQ_HAVE_EXT_]translit($1,a-z_-,A-Z__)= 131 | AC_MSG_RESULT([no]) 132 | $3 133 | fi 134 | ]) 135 | 136 | PQ_SRC="\ 137 | src/php_pq_module.c\ 138 | src/php_pq_misc.c\ 139 | src/php_pq_callback.c\ 140 | src/php_pq_object.c\ 141 | src/php_pq_params.c\ 142 | src/php_pqcancel.c\ 143 | src/php_pqconn.c\ 144 | src/php_pqconn_event.c\ 145 | src/php_pqcopy.c\ 146 | src/php_pqexc.c\ 147 | src/php_pqlob.c\ 148 | src/php_pqres.c\ 149 | src/php_pqstm.c\ 150 | src/php_pqtxn.c\ 151 | src/php_pqtypes.c\ 152 | src/php_pqcur.c\ 153 | " 154 | PHP_NEW_EXTENSION(pq, $PQ_SRC, $ext_shared) 155 | PHP_ADD_BUILD_DIR($ext_builddir/src) 156 | PHP_ADD_INCLUDE($ext_srcdir/src) 157 | 158 | PQ_HAVE_PHP_EXT([raphf], [ 159 | AC_MSG_CHECKING([for php_raphf.h]) 160 | PQ_EXT_RAPHF_INCDIR= 161 | for i in `echo $INCLUDES | $SED -e's/-I//g'` $abs_srcdir ../raphf; do 162 | if test -d $i; then 163 | if test -f $i/php_raphf.h; then 164 | PQ_EXT_RAPHF_INCDIR=$i 165 | break 166 | elif test -f $i/ext/raphf/php_raphf.h; then 167 | PQ_EXT_RAPHF_INCDIR=$i/ext/raphf 168 | break 169 | fi 170 | fi 171 | done 172 | if test "x$PQ_EXT_RAPHF_INCDIR" = "x"; then 173 | AC_MSG_ERROR([not found]) 174 | else 175 | AC_MSG_RESULT([$PQ_EXT_RAPHF_INCDIR]) 176 | AC_DEFINE([PHP_PQ_HAVE_PHP_RAPHF_H], [1], [Have ext/raphf support]) 177 | PHP_ADD_INCLUDE([$PQ_EXT_RAPHF_INCDIR]) 178 | fi 179 | ], [ 180 | AC_MSG_ERROR([Please install pecl/raphf and activate extension=raphf.$SHLIB_DL_SUFFIX_NAME in your php.ini]) 181 | ]) 182 | PHP_ADD_EXTENSION_DEP(pq, raphf, true) 183 | fi 184 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | pq 11 | pecl.php.net 12 | PostgreSQL client library (libpq) binding 13 | 14 | Documents: https://mdref.m6w6.name/pq 15 | 16 | Highlights: 17 | * Nearly complete support for asynchronous usage: 18 | https://mdref.m6w6.name/pq/Connection/%3A%20Asynchronous%20Usage 19 | * Extended type support by pg_type: 20 | https://mdref.m6w6.name/pq/Types/%3A%20Overview 21 | * Fetching simple multi-dimensional array maps: 22 | https://mdref.m6w6.name/pq/Result/map 23 | * Working Gateway implementation: 24 | https://mdref.m6w6.name/pq-gateway 25 | 26 | 27 | Michael Wallner 28 | mike 29 | mike@php.net 30 | yes 31 | 32 | 33 | Chris Wright 34 | daverandom 35 | daverandom@php.net 36 | yes 37 | 38 | 39 | Remi Collet 40 | remi 41 | remi@php.net 42 | yes 43 | 44 | 2024-02-05 45 | 46 | 2.2.3 47 | 2.1.0 48 | 49 | 50 | stable 51 | stable 52 | 53 | BSD-2-Clause 54 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 7.0.0 174 | 175 | 176 | 1.10.0 177 | 178 | 179 | raphf 180 | pecl.php.net 181 | 2.0.0 182 | raphf 183 | 184 | 185 | 186 | pq 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /php_pq.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQ_H 15 | #define PHP_PQ_H 16 | 17 | #define PHP_PQ_VERSION "2.2.3" 18 | 19 | #ifdef PHP_WIN32 20 | # define PHP_PQ_API __declspec(dllexport) 21 | #elif defined(__GNUC__) && __GNUC__ >= 4 22 | # define PHP_PQ_API extern __attribute__ ((visibility("default"))) 23 | #else 24 | # define PHP_PQ_API extern 25 | #endif 26 | 27 | extern int pq_module_number; 28 | extern zend_module_entry pq_module_entry; 29 | #define phpext_pq_ptr &pq_module_entry 30 | 31 | ZEND_BEGIN_MODULE_GLOBALS(php_pq) 32 | struct { 33 | /* for ext-raphf */ 34 | zend_string *name; 35 | } connection; 36 | ZEND_END_MODULE_GLOBALS(php_pq) 37 | 38 | ZEND_EXTERN_MODULE_GLOBALS(php_pq); 39 | 40 | #ifdef ZTS 41 | # include "TSRM/TSRM.h" 42 | # define PHP_PQ_G ((zend_php_pq_globals *) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(php_pq_globals_id)]) 43 | #else 44 | # define PHP_PQ_G (&php_pq_globals) 45 | #endif 46 | 47 | #endif /* PHP_PQ_H */ 48 | 49 | 50 | /* 51 | * Local variables: 52 | * tab-width: 4 53 | * c-basic-offset: 4 54 | * End: 55 | * vim600: noet sw=4 ts=4 fdm=marker 56 | * vim<600: noet sw=4 ts=4 57 | */ 58 | -------------------------------------------------------------------------------- /scripts/gen_github_workflow_ci.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 3 | 4 | name: ci 5 | on: 6 | workflow_dispatch: 7 | push: 8 | pull_request: 9 | 10 | jobs: 11 | github([ 16 | "old-matrix" => [ 17 | "PHP" => ["7.4", "8.0", "8.1", "8.2"], 18 | "enable_debug" => "yes", 19 | "enable_maintainer_zts" => "yes", 20 | "enable_json" => "yes", 21 | ], 22 | "next" => [ 23 | "PHP" => ["master"], 24 | "enable_debug" => "yes", 25 | "enable_zts" => "yes", 26 | ], 27 | "cur-dbg-zts" => [ 28 | "PHP" => $cur, 29 | "enable_debug", 30 | "enable_zts", 31 | ], 32 | "cur-cov" => [ 33 | "CFLAGS" => "-O0 -g --coverage", 34 | "CXXFLAGS" => "-O0 -g --coverage", 35 | "PHP" => $cur, 36 | ]]); 37 | foreach ($job as $id => $env) { 38 | printf(" %s:\n", $id); 39 | printf(" name: \"%s (%s)\"\n", $id, $env["PHP"]); 40 | if ($env["PHP"] == "master") { 41 | printf(" continue-on-error: true\n"); 42 | } 43 | printf(" env:\n"); 44 | foreach ($env as $key => $val) { 45 | printf(" %s: \"%s\"\n", $key, $val); 46 | } 47 | ?> 48 | PQ_DSN: "postgres:///runner" 49 | runs-on: ubuntu-22.04 50 | steps: 51 | - uses: actions/checkout@v2 52 | with: 53 | submodules: true 54 | - name: Install 55 | run: | 56 | sudo apt-get install -y \ 57 | php-cli \ 58 | php-pear \ 59 | libpq-dev \ 60 | re2c 61 | - name: Prepare 62 | run: | 63 | make -f scripts/ci/Makefile php || make -f scripts/ci/Makefile clean php 64 | make -f scripts/ci/Makefile pecl PECL=m6w6/ext-raphf.git:raphf:master 65 | - name: Build 66 | run: | 67 | make -f scripts/ci/Makefile ext PECL=pq 68 | - name: Prepare Test 69 | run: | 70 | sudo systemctl start postgresql 71 | sudo -u postgres createuser --login runner 72 | sudo -u postgres createdb -O runner runner 73 | - name: Test 74 | run: | 75 | make -f scripts/ci/Makefile test 76 | 77 | - name: Coverage 78 | if: success() 79 | run: | 80 | cd src/.libs 81 | bash <(curl -s https://codecov.io/bash) -X xcode -X coveragepy 82 | 83 | 84 | $CWD/../php_pq_type.h 4 | -------------------------------------------------------------------------------- /scripts/php_pq_type-pg11.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | \w+) => \'(?P(?:[^\']|(?<=\\\\)\')+)\'/', $set_data, $set_matches)) { 26 | fprintf(STDERR, "Failed matching key value pairs in set: '%s'\n", $set_data); 27 | continue; 28 | } 29 | $set = array_combine($set_matches["key"], $set_matches["val"]); 30 | $ucn = strtoupper($set["typname"]); 31 | $typ[$set["oid"]] = $ucn; 32 | 33 | if (isset($set["array_type_oid"])) { 34 | $arr[$set["array_type_oid"]] = $set["oid"]; 35 | $typ[$set["array_type_oid"]] = $ucn . "ARRAY"; 36 | } 37 | if (isset($set["typdelim"])) { 38 | $delims[$set["oid"]] = $delims[$set["array_type_oid"]] = $set["typdelim"]; 39 | } 40 | } 41 | 42 | ksort($typ, SORT_NUMERIC); 43 | ksort($arr, SORT_NUMERIC); 44 | ?> 45 | 46 | /* Generated file. See scripts/gen_pq_type-pq11.php */ 47 | 48 | #ifndef PHP_PQ_TYPE 49 | # define PHP_PQ_TYPE(t,o) 50 | #endif 51 | 52 | $ucn) : ?> 53 | #ifndef PHP_PQ_OID_ 54 | 55 | # define PHP_PQ_OID_ 56 | 57 | #endif 58 | PHP_PQ_TYPE("", ) 59 | 60 | 61 | #ifndef PHP_PQ_TYPE_IS_ARRAY 62 | # define PHP_PQ_TYPE_IS_ARRAY(oid) ( \ 63 | 0 \ 64 | $type) : ?> 65 | || ((oid) == ) \ 66 | 67 | ) 68 | #endif 69 | 70 | #ifndef PHP_PQ_TYPE_OF_ARRAY 71 | # define PHP_PQ_TYPE_OF_ARRAY(oid) ( \ 72 | $type) : ?> 73 | (oid) == ? : \ 74 | 75 | 0 \ 76 | ) 77 | #endif 78 | 79 | #ifndef PHP_PQ_DELIM_OF_ARRAY 80 | # define PHP_PQ_DELIM_OF_ARRAY(oid) ((char) ( \ 81 | $delim) : ?> 82 | (oid) == ? '' : \ 83 | 84 | ',' \ 85 | )) 86 | #endif 87 | -------------------------------------------------------------------------------- /scripts/php_pq_type.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { 4 | printf "#ifndef PHP_PQ_TYPE\n" 5 | printf "# define PHP_PQ_TYPE(t,o)\n" 6 | printf "#endif\n" 7 | } 8 | 9 | END { 10 | printf "#ifndef PHP_PQ_TYPE_IS_ARRAY\n" 11 | printf "# define PHP_PQ_TYPE_IS_ARRAY(oid) (\\\n\t\t0 \\\n" 12 | for (oid in arrays) { 13 | printf "\t||\t((oid) == %d) \\\n", oid 14 | } 15 | printf ")\n#endif\n" 16 | 17 | printf "#ifndef PHP_PQ_TYPE_OF_ARRAY\n" 18 | printf "# define PHP_PQ_TYPE_OF_ARRAY(oid) (" 19 | for (oid in arrays) { 20 | printf "\\\n\t(oid) == %d ? %s : ", oid, arrays[oid] 21 | } 22 | printf "0 \\\n)\n#endif\n" 23 | 24 | printf "#ifndef PHP_PQ_DELIM_OF_ARRAY\n" 25 | printf "# define PHP_PQ_DELIM_OF_ARRAY(oid) (" 26 | for (oid in delims) { 27 | printf "\\\n\t(oid) == %d ? '%s' : ", oid, delims[oid] 28 | } 29 | printf "\\\n\t0 \\\n)\n#endif\n" 30 | } 31 | 32 | /^DATA/ { 33 | oid = $4 34 | name = toupper($6) 35 | adelim = $15 36 | atypoid = $17 37 | if (sub("^_", "", name)) { 38 | arrays[oid] = atypoid 39 | name = name "ARRAY" 40 | } 41 | delims[oid] = adelim 42 | printf "#ifndef PHP_PQ_OID_%s\n", name 43 | printf "# define PHP_PQ_OID_%s %d\n", name, oid 44 | printf "#endif\n" 45 | printf "PHP_PQ_TYPE(\"%s\", %d)\n", name, oid 46 | } 47 | -------------------------------------------------------------------------------- /src/php_pq_callback.c: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | #include "php_pq_callback.h" 21 | 22 | void php_pq_callback_dtor(php_pq_callback_t *cb) 23 | { 24 | if (cb->recursion) { 25 | php_pq_callback_dtor(cb->recursion); 26 | efree(cb->recursion); 27 | cb->recursion = NULL; 28 | } 29 | if (cb->fci.size > 0) { 30 | zend_fcall_info_args_clear(&cb->fci, 1); 31 | zval_ptr_dtor(&cb->fci.function_name); 32 | if (cb->fci.object) { 33 | zval tmp; 34 | 35 | ZVAL_OBJ(&tmp, cb->fci.object); 36 | zval_ptr_dtor(&tmp); 37 | } 38 | cb->fci.size = 0; 39 | } 40 | } 41 | 42 | void php_pq_callback_addref(php_pq_callback_t *cb) 43 | { 44 | Z_TRY_ADDREF(cb->fci.function_name); 45 | if (cb->fci.object) { 46 | #ifdef GC_ADDREF 47 | GC_ADDREF(cb->fci.object); 48 | #else 49 | ++GC_REFCOUNT(cb->fci.object); 50 | #endif 51 | } 52 | } 53 | 54 | zval *php_pq_callback_to_zval(php_pq_callback_t *cb, zval *tmp) 55 | { 56 | php_pq_callback_addref(cb); 57 | 58 | if (cb->fci.object) { 59 | zval zo; 60 | 61 | array_init_size(tmp, 2); 62 | ZVAL_OBJ(&zo, cb->fci.object); 63 | add_next_index_zval(tmp, &zo); 64 | add_next_index_zval(tmp, &cb->fci.function_name); 65 | 66 | return tmp; 67 | } 68 | 69 | return &cb->fci.function_name; 70 | } 71 | 72 | zval *php_pq_callback_to_zval_no_addref(php_pq_callback_t *cb, zval *tmp) 73 | { 74 | if (cb->fci.object) { 75 | zval zo; 76 | 77 | array_init_size(tmp, 2); 78 | ZVAL_OBJ(&zo, cb->fci.object); 79 | add_next_index_zval(tmp, &zo); 80 | add_next_index_zval(tmp, &cb->fci.function_name); 81 | 82 | return tmp; 83 | } 84 | 85 | return &cb->fci.function_name; 86 | } 87 | 88 | zend_bool php_pq_callback_is_locked(php_pq_callback_t *cb) 89 | { 90 | /* TODO: fixed in php7? 91 | if (php_pq_callback_is_enabled(cb)) { 92 | const zend_function *closure; 93 | const zend_execute_data *ex; 94 | 95 | if (Z_TYPE_P(cb->fci.function_name) != IS_OBJECT) { 96 | return 0; 97 | } 98 | 99 | closure = zend_get_closure_method_def(cb->fci.function_name); 100 | if (closure->type != ZEND_USER_FUNCTION) { 101 | return 0; 102 | } 103 | 104 | for (ex = EG(current_execute_data); ex; ex = ex->prev_execute_data) { 105 | if (ex->op_array == &closure->op_array) { 106 | return 1; 107 | } 108 | } 109 | } 110 | if (!php_pq_callback_is_recurrent(cb)) { 111 | return 0; 112 | } 113 | return php_pq_callback_is_locked(cb->recursion); 114 | */ 115 | return 0; 116 | } 117 | 118 | void php_pq_callback_recurse(php_pq_callback_t *old, php_pq_callback_t *new) 119 | { 120 | if (php_pq_callback_is_locked(old)) { 121 | php_pq_callback_recurse_ex(old, new); 122 | } else { 123 | php_pq_callback_dtor(old); 124 | if (php_pq_callback_is_enabled(new)) { 125 | php_pq_callback_addref(new); 126 | memcpy(old, new, sizeof(*old)); 127 | new->fci.size = 0; 128 | } 129 | } 130 | } 131 | 132 | extern zend_bool php_pq_callback_is_enabled(php_pq_callback_t *cb) 133 | { 134 | return cb && cb->fci.size > 0; 135 | } 136 | 137 | extern zend_bool php_pq_callback_is_recurrent(php_pq_callback_t *cb) 138 | { 139 | return cb && cb->recursion != NULL; 140 | } 141 | 142 | extern void php_pq_callback_disable(php_pq_callback_t *cb) 143 | { 144 | if (php_pq_callback_is_enabled(cb)) { 145 | php_pq_callback_recurse_ex(cb, NULL); 146 | } 147 | } 148 | 149 | extern void php_pq_callback_recurse_ex(php_pq_callback_t *old, php_pq_callback_t *new) 150 | { 151 | php_pq_callback_t *tmp = emalloc(sizeof(*tmp)); 152 | 153 | if (new) { 154 | memcpy(tmp, old, sizeof(*tmp)); 155 | memcpy(old, new, sizeof(*old)); 156 | old->recursion = tmp; 157 | 158 | php_pq_callback_addref(old); 159 | php_pq_callback_disable(tmp); 160 | } else { 161 | memcpy(tmp, old, sizeof(*tmp)); 162 | memset(old, 0, sizeof(*old)); 163 | old->recursion = tmp; 164 | } 165 | } 166 | 167 | /* 168 | * Local variables: 169 | * tab-width: 4 170 | * c-basic-offset: 4 171 | * End: 172 | * vim600: noet sw=4 ts=4 fdm=marker 173 | * vim<600: noet sw=4 ts=4 174 | */ 175 | -------------------------------------------------------------------------------- /src/php_pq_callback.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifndef PHP_PQ_CALLBACK_H 14 | #define PHP_PQ_CALLBACK_H 15 | 16 | #include 17 | 18 | typedef struct php_pq_callback { 19 | zend_fcall_info fci; 20 | zend_fcall_info_cache fcc; 21 | struct php_pq_callback *recursion; 22 | } php_pq_callback_t; 23 | 24 | #define PHP_PQ_CALLBACK_INIT {{0},{0},NULL} 25 | 26 | extern void php_pq_callback_dtor(php_pq_callback_t *cb); 27 | extern void php_pq_callback_addref(php_pq_callback_t *cb); 28 | extern zval *php_pq_callback_to_zval(php_pq_callback_t *cb, zval *tmp); 29 | extern zval *php_pq_callback_to_zval_no_addref(php_pq_callback_t *cb, zval *tmp); 30 | extern zend_bool php_pq_callback_is_locked(php_pq_callback_t *cb); 31 | extern void php_pq_callback_recurse(php_pq_callback_t *old, php_pq_callback_t *new); 32 | extern zend_bool php_pq_callback_is_enabled(php_pq_callback_t *cb); 33 | extern void php_pq_callback_disable(php_pq_callback_t *cb); 34 | extern void php_pq_callback_recurse_ex(php_pq_callback_t *old, php_pq_callback_t *new); 35 | extern zend_bool php_pq_callback_is_recurrent(php_pq_callback_t *cb); 36 | 37 | #endif 38 | 39 | /* 40 | * Local variables: 41 | * tab-width: 4 42 | * c-basic-offset: 4 43 | * End: 44 | * vim600: noet sw=4 ts=4 fdm=marker 45 | * vim<600: noet sw=4 ts=4 46 | */ 47 | -------------------------------------------------------------------------------- /src/php_pq_misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQ_ERROR_H 15 | #define PHP_PQ_ERROR_H 16 | 17 | #include 18 | 19 | #include "php_pqres.h" 20 | 21 | #define z_is_true zend_is_true 22 | #define smart_str_s(ss) (ss)->s 23 | #define smart_str_v(ss) (smart_str_s(ss)?(ss)->s->val:NULL) 24 | #define smart_str_l(ss) (smart_str_s(ss)?(ss)->s->len:0) 25 | 26 | /* clear result object associated with a result handle */ 27 | extern void php_pqres_clear(PGresult *r); 28 | /* clear any asynchronous results */ 29 | extern void php_pqconn_clear(PGconn *conn); 30 | /* safe wrappers to clear any asynchronous wrappers before querying synchronously */ 31 | extern PGresult *php_pq_exec(PGconn *conn, const char *query); 32 | extern PGresult *php_pq_exec_params(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const * paramValues, const int *paramLengths, const int *paramFormats, int resultFormat); 33 | extern PGresult *php_pq_prepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes); 34 | extern PGresult *php_pq_exec_prepared(PGconn *conn, const char *stmtName, int nParams, const char *const * paramValues, const int *paramLengths, const int *paramFormats, int resultFormat); 35 | 36 | /* convert version to string */ 37 | extern void php_pq_version_to_string(int version, char *buffer, int len); 38 | 39 | /* trim LF from EOL */ 40 | extern char *php_pq_rtrim(char *e); 41 | 42 | /* R, W, RW */ 43 | extern const char *php_pq_strmode(long mode); 44 | 45 | /* free zval ptr values (as hash dtor) */ 46 | extern void php_pq_hash_ptr_dtor(zval *p); 47 | 48 | #define PHP_PQerrorMessage(c) php_pq_rtrim(PQerrorMessage((c))) 49 | #define PHP_PQresultErrorMessage(r) php_pq_rtrim(PQresultErrorMessage((r))) 50 | 51 | extern zend_class_entry *php_pqdt_class_entry; 52 | extern zval *php_pqdt_from_string(zval *zv, char *input_fmt, char *dt_str, size_t dt_len, const char *output_fmt, zval *ztimezone); 53 | extern zend_string *php_pqdt_to_string(zval *zdt, const char *format); 54 | 55 | extern zend_class_entry *php_pqconv_class_entry; 56 | 57 | extern HashTable *php_pq_parse_array(php_pqres_t *res, const char *val_str, size_t val_len, Oid typ); 58 | 59 | /* ZE compat */ 60 | #if PHP_VERSION_ID >= 80000 61 | extern int php_pq_compare_index(Bucket *lptr, Bucket *rptr); 62 | 63 | # define php_pq_call_method(objval_ptr, method_name, num_args, ...) \ 64 | zend_call_method_with_ ## num_args ## _params( \ 65 | Z_OBJ_P(objval_ptr), Z_OBJCE_P(objval_ptr), NULL, \ 66 | (method_name), __VA_ARGS__) 67 | # define php_pq_read_property(objval_ptr, prop_name, tmpval_ptr) \ 68 | zend_read_property(Z_OBJCE_P(objval_ptr), Z_OBJ_P(objval_ptr), \ 69 | (prop_name), strlen(prop_name), 0, (tmpval_ptr)) 70 | # define php_pq_update_property(objval_ptr, prop_name, newval_ptr) \ 71 | zend_update_property(Z_OBJCE_P(objval_ptr), Z_OBJ_P(objval_ptr), \ 72 | (prop_name), strlen(prop_name), (newval_ptr)) 73 | #define php_pq_cast_object(objval_ptr, cast_type, retval_ptr) \ 74 | (Z_OBJ_HT_P(objval_ptr)->cast_object && \ 75 | SUCCESS == Z_OBJ_HT_P(objval_ptr)->cast_object(Z_OBJ_P(objval_ptr), (retval_ptr), (cast_type))) 76 | #else 77 | 78 | extern int php_pq_compare_index(const void *lptr, const void *rptr); 79 | 80 | # define zend_ce_countable spl_ce_Countable 81 | 82 | # define php_pq_call_method(objval_ptr, method_name, num_args, ...) \ 83 | zend_call_method_with_ ## num_args ## _params( \ 84 | (objval_ptr), NULL, NULL, \ 85 | (method_name), __VA_ARGS__) 86 | # define php_pq_read_property(objval_ptr, prop_name, tmpval_ptr) \ 87 | zend_read_property(Z_OBJCE_P(objval_ptr), (objval_ptr), \ 88 | (prop_name), strlen(prop_name), 0, (tmpval_ptr)) 89 | # define php_pq_update_property(objval_ptr, prop_name, newval_ptr) \ 90 | zend_update_property(Z_OBJCE_P(objval_ptr), (objval_ptr), \ 91 | (prop_name), strlen(prop_name), (newval_ptr)) 92 | #define php_pq_cast_object(objval_ptr, cast_type, retval_ptr) \ 93 | (Z_OBJ_HT_P(objval_ptr)->cast_object && \ 94 | SUCCESS == Z_OBJ_HT_P(objval_ptr)->cast_object(objval_ptr, (retval_ptr), (cast_type))) 95 | #endif 96 | 97 | #if PHP_VERSION_ID < 80100 98 | # define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ 99 | ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args) 100 | # define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ 101 | ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args) 102 | #endif 103 | 104 | #ifndef ZEND_ACC_READONLY 105 | #define ZEND_ACC_READONLY 0 106 | #endif 107 | 108 | extern PHP_MINIT_FUNCTION(pq_misc); 109 | 110 | #endif 111 | 112 | /* 113 | * Local variables: 114 | * tab-width: 4 115 | * c-basic-offset: 4 116 | * End: 117 | * vim600: noet sw=4 ts=4 fdm=marker 118 | * vim<600: noet sw=4 ts=4 119 | */ 120 | -------------------------------------------------------------------------------- /src/php_pq_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "php_pq.h" 24 | #include "php_pq_misc.h" 25 | #include "php_pqcancel.h" 26 | #include "php_pqconn.h" 27 | #include "php_pqcopy.h" 28 | #include "php_pqcur.h" 29 | #include "php_pqexc.h" 30 | #include "php_pqlob.h" 31 | #include "php_pqres.h" 32 | #include "php_pqstm.h" 33 | #include "php_pqtxn.h" 34 | #include "php_pqtypes.h" 35 | 36 | ZEND_DECLARE_MODULE_GLOBALS(php_pq); 37 | 38 | static void php_pq_globals_init_once(zend_php_pq_globals *G) 39 | { 40 | memset(G, 0, sizeof(*G)); 41 | } 42 | 43 | #define PHP_MINIT_CALL(i) do { \ 44 | if (SUCCESS != PHP_MINIT(i)(type, module_number)) { \ 45 | return FAILURE; \ 46 | } \ 47 | } while(0) 48 | 49 | static PHP_MINIT_FUNCTION(pq) 50 | { 51 | ZEND_INIT_MODULE_GLOBALS(php_pq, php_pq_globals_init_once, NULL); 52 | 53 | PHP_MINIT_CALL(pq_misc); 54 | PHP_MINIT_CALL(pqexc); 55 | 56 | PHP_MINIT_CALL(pqconn); 57 | PHP_MINIT_CALL(pqcancel); 58 | PHP_MINIT_CALL(pqtypes); 59 | 60 | PHP_MINIT_CALL(pqres); 61 | PHP_MINIT_CALL(pqstm); 62 | PHP_MINIT_CALL(pqtxn); 63 | PHP_MINIT_CALL(pqcur); 64 | 65 | PHP_MINIT_CALL(pqcopy); 66 | PHP_MINIT_CALL(pqlob); 67 | 68 | return SUCCESS; 69 | } 70 | 71 | #define PHP_MSHUT_CALL(i) do { \ 72 | if (SUCCESS != PHP_MSHUTDOWN(i)(type, module_number)) { \ 73 | return FAILURE; \ 74 | } \ 75 | } while(0) 76 | 77 | static PHP_MSHUTDOWN_FUNCTION(pq) 78 | { 79 | PHP_MSHUT_CALL(pqlob); 80 | PHP_MSHUT_CALL(pqcopy); 81 | PHP_MSHUT_CALL(pqcur); 82 | PHP_MSHUT_CALL(pqtxn); 83 | PHP_MSHUT_CALL(pqstm); 84 | PHP_MSHUT_CALL(pqres); 85 | PHP_MSHUT_CALL(pqtypes); 86 | PHP_MSHUT_CALL(pqcancel); 87 | PHP_MSHUT_CALL(pqconn); 88 | 89 | return SUCCESS; 90 | } 91 | 92 | static PHP_MINFO_FUNCTION(pq) 93 | { 94 | #ifdef HAVE_PQLIBVERSION 95 | int libpq_v; 96 | #endif 97 | char libpq_version[10] = "pre-9.1"; 98 | 99 | php_info_print_table_start(); 100 | php_info_print_table_header(2, "PQ Support", "enabled"); 101 | php_info_print_table_row(2, "Extension Version", PHP_PQ_VERSION); 102 | php_info_print_table_end(); 103 | 104 | php_info_print_table_start(); 105 | php_info_print_table_header(3, "Used Library", "Compiled", "Linked"); 106 | #ifdef HAVE_PQLIBVERSION 107 | libpq_v = PQlibVersion(); 108 | php_pq_version_to_string(libpq_v, libpq_version, sizeof(libpq_version)); 109 | #endif 110 | php_info_print_table_row(3, "libpq", PHP_PQ_LIBVERSION, libpq_version); 111 | php_info_print_table_end(); 112 | } 113 | 114 | static const zend_function_entry pq_functions[] = { 115 | {0} 116 | }; 117 | 118 | static zend_module_dep pq_module_deps[] = { 119 | ZEND_MOD_REQUIRED("raphf") 120 | ZEND_MOD_REQUIRED("spl") 121 | ZEND_MOD_OPTIONAL("json") 122 | ZEND_MOD_END 123 | }; 124 | 125 | zend_module_entry pq_module_entry = { 126 | STANDARD_MODULE_HEADER_EX, 127 | NULL, 128 | pq_module_deps, 129 | "pq", 130 | pq_functions, 131 | PHP_MINIT(pq), 132 | PHP_MSHUTDOWN(pq), 133 | NULL,/*PHP_RINIT(pq),*/ 134 | NULL,/*PHP_RSHUTDOWN(pq),*/ 135 | PHP_MINFO(pq), 136 | PHP_PQ_VERSION, 137 | STANDARD_MODULE_PROPERTIES 138 | }; 139 | 140 | #ifdef COMPILE_DL_PQ 141 | ZEND_GET_MODULE(pq) 142 | #endif 143 | 144 | 145 | /* 146 | * Local variables: 147 | * tab-width: 4 148 | * c-basic-offset: 4 149 | * End: 150 | * vim600: noet sw=4 ts=4 fdm=marker 151 | * vim<600: noet sw=4 ts=4 152 | */ 153 | -------------------------------------------------------------------------------- /src/php_pq_object.c: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | 19 | #include "php_pq_object.h" 20 | #include "php_pq_misc.h" 21 | 22 | void *php_pq_object_create(zend_class_entry *ce, void *intern, size_t obj_size, zend_object_handlers *oh, HashTable *ph) 23 | { 24 | php_pq_object_t *o = ecalloc(1, obj_size + zend_object_properties_size(ce)); 25 | 26 | zend_object_std_init(&o->zo, ce); 27 | object_properties_init(&o->zo, ce); 28 | o->zo.handlers = oh; 29 | o->intern = intern; 30 | o->prophandler = ph; 31 | 32 | zend_hash_init(&o->gc, 0, NULL, NULL, 0); 33 | 34 | return o; 35 | } 36 | 37 | void php_pq_object_dtor(zend_object *o) 38 | { 39 | php_pq_object_t *obj = PHP_PQ_OBJ(NULL, o); 40 | 41 | zend_hash_destroy(&obj->gc); 42 | zend_object_std_dtor(o); 43 | } 44 | 45 | void php_pq_object_to_zval(void *o, zval *zv) 46 | { 47 | php_pq_object_t *obj = o; 48 | 49 | ZVAL_OBJ(zv, &obj->zo); 50 | Z_ADDREF_P(zv); 51 | } 52 | 53 | void php_pq_object_to_zval_no_addref(void *o, zval *zv) 54 | { 55 | php_pq_object_t *obj = o; 56 | 57 | ZVAL_OBJ(zv, &obj->zo); 58 | } 59 | 60 | void php_pq_object_addref(void *o) 61 | { 62 | php_pq_object_t *obj = o; 63 | #ifdef GC_ADDREF 64 | GC_ADDREF(&obj->zo); 65 | #else 66 | ++GC_REFCOUNT(&obj->zo); 67 | #endif 68 | } 69 | 70 | void php_pq_object_delref(void *o) 71 | { 72 | php_pq_object_t *obj = o; 73 | zval tmp; 74 | 75 | /* this should gc immediately */ 76 | ZVAL_OBJ(&tmp, &obj->zo); 77 | zval_ptr_dtor(&tmp); 78 | } 79 | 80 | struct apply_pi_to_ht_arg { 81 | HashTable *ht; 82 | php_pq_object_t *pq_obj; 83 | unsigned gc:1; 84 | }; 85 | 86 | static int apply_pi_to_ht(zval *p, void *a) 87 | { 88 | zend_property_info *pi = Z_PTR_P(p); 89 | struct apply_pi_to_ht_arg *arg = a; 90 | 91 | if (arg->gc) { 92 | php_pq_object_prophandler_t *handler; 93 | 94 | if ((handler = zend_hash_find_ptr(arg->pq_obj->prophandler, pi->name)) && handler->gc) { 95 | zval return_value; 96 | 97 | ZVAL_ARR(&return_value, arg->ht); 98 | handler->gc(arg->pq_obj, &return_value); 99 | } 100 | } else { 101 | zval tmp_prop, *property; 102 | #if PHP_VERSION_ID < 80000 103 | zval zobj; 104 | 105 | ZVAL_OBJ(&zobj, &arg->pq_obj->zo); 106 | # if PHP_VERSION_ID < 70100 107 | property = zend_read_property(arg->pq_obj->zo.ce, &zobj, pi->name->val, pi->name->len, 0, &tmp_prop); 108 | # else 109 | property = zend_read_property_ex(arg->pq_obj->zo.ce, &zobj, pi->name, 0, &tmp_prop); 110 | # endif 111 | #else 112 | property = zend_read_property_ex(arg->pq_obj->zo.ce, &arg->pq_obj->zo, pi->name, 0, &tmp_prop); 113 | #endif 114 | zend_hash_update(arg->ht, pi->name, property); 115 | } 116 | 117 | return ZEND_HASH_APPLY_KEEP; 118 | } 119 | 120 | static inline HashTable *php_pq_object_debug_info_ex(zend_object *object, int *temp) 121 | { 122 | struct apply_pi_to_ht_arg arg = {NULL}; 123 | 124 | *temp = 1; 125 | ALLOC_HASHTABLE(arg.ht); 126 | ZEND_INIT_SYMTABLE(arg.ht); 127 | 128 | arg.pq_obj = PHP_PQ_OBJ(NULL, object); 129 | arg.gc = 0; 130 | 131 | zend_hash_apply_with_argument(&arg.pq_obj->zo.ce->properties_info, apply_pi_to_ht, &arg); 132 | 133 | return arg.ht; 134 | } 135 | #if PHP_VERSION_ID >= 80000 136 | HashTable *php_pq_object_debug_info(zend_object *object, int *temp) 137 | { 138 | return php_pq_object_debug_info_ex(object, temp); 139 | } 140 | #else 141 | HashTable *php_pq_object_debug_info(zval *object, int *temp) 142 | { 143 | return php_pq_object_debug_info_ex(Z_OBJ_P(object), temp); 144 | } 145 | #endif 146 | 147 | static inline HashTable *php_pq_object_properties_ex(zend_object *object, HashTable *props) 148 | { 149 | struct apply_pi_to_ht_arg arg = {NULL}; 150 | 151 | arg.ht = props; 152 | arg.pq_obj = PHP_PQ_OBJ(NULL, object); 153 | arg.gc = 0; 154 | 155 | zend_hash_apply_with_argument(&arg.pq_obj->zo.ce->properties_info, apply_pi_to_ht, &arg); 156 | 157 | return arg.ht; 158 | } 159 | #if PHP_VERSION_ID >= 80000 160 | HashTable *php_pq_object_properties(zend_object *object) 161 | { 162 | return php_pq_object_properties_ex(object, zend_std_get_properties(object)); 163 | } 164 | #else 165 | HashTable *php_pq_object_properties(zval *object) 166 | { 167 | return php_pq_object_properties_ex(Z_OBJ_P(object), zend_std_get_properties(object)); 168 | } 169 | #endif 170 | 171 | static inline HashTable *php_pq_object_get_gc_ex(zend_object *object, HashTable *props, zval **table, int *n) 172 | { 173 | struct apply_pi_to_ht_arg arg = {NULL}; 174 | 175 | arg.pq_obj = PHP_PQ_OBJ(NULL, object); 176 | arg.ht = &arg.pq_obj->gc; 177 | arg.gc = 1; 178 | 179 | if (GC_REFCOUNT(arg.ht) == 1) { 180 | zend_hash_clean(arg.ht); 181 | zend_hash_copy(arg.ht, props, NULL); 182 | zend_hash_apply_with_argument(&arg.pq_obj->zo.ce->properties_info, apply_pi_to_ht, &arg); 183 | } 184 | 185 | *table = NULL; 186 | *n = 0; 187 | 188 | return arg.ht; 189 | } 190 | #if PHP_VERSION_ID >= 80000 191 | HashTable *php_pq_object_get_gc(zend_object *object, zval **table, int *n) 192 | { 193 | return php_pq_object_get_gc_ex(object, zend_std_get_properties(object), table, n); 194 | } 195 | #else 196 | HashTable *php_pq_object_get_gc(zval *object, zval **table, int *n) 197 | { 198 | return php_pq_object_get_gc_ex(Z_OBJ_P(object), zend_std_get_properties(object), table, n); 199 | } 200 | #endif 201 | 202 | zend_class_entry *ancestor(zend_class_entry *ce) 203 | { 204 | while (ce->parent) { 205 | ce = ce->parent; 206 | } 207 | return ce; 208 | } 209 | 210 | static inline int php_pq_object_read_prop_ex(zend_object *object, zend_string *member, int type, zval *return_value) 211 | { 212 | php_pq_object_t *obj = PHP_PQ_OBJ(NULL, object); 213 | php_pq_object_prophandler_t *handler; 214 | 215 | if (!obj->intern) { 216 | php_error(E_RECOVERABLE_ERROR, "%s not initialized", ancestor(obj->zo.ce)->name->val); 217 | } else if (!(handler = zend_hash_find_ptr(obj->prophandler, member)) || !handler->read) { 218 | /* default handler */ 219 | } else if (type != BP_VAR_R) { 220 | php_error(E_WARNING, "Cannot access %s properties by reference or array key/index", ancestor(obj->zo.ce)->name->val); 221 | } else { 222 | handler->read(obj, return_value); 223 | return SUCCESS; 224 | } 225 | 226 | return FAILURE; 227 | } 228 | #if PHP_VERSION_ID >= 80000 229 | zval *php_pq_object_read_prop(zend_object *object, zend_string *member, int type, void **cache_slot, zval *tmp) 230 | { 231 | if (SUCCESS != php_pq_object_read_prop_ex(object, member, type, tmp)) { 232 | return zend_std_read_property(object, member, type, cache_slot, tmp); 233 | } 234 | 235 | zend_std_write_property(object, member, tmp, cache_slot); 236 | 237 | if (cache_slot) { 238 | *cache_slot = NULL; 239 | } 240 | return tmp; 241 | } 242 | #else 243 | zval *php_pq_object_read_prop(zval *object, zval *member, int type, void **cache_slot, zval *tmp) 244 | { 245 | zend_string *member_str = zval_get_string(member); 246 | 247 | if (SUCCESS != php_pq_object_read_prop_ex(Z_OBJ_P(object), member_str, type, tmp)) { 248 | zend_string_release(member_str); 249 | return zend_get_std_object_handlers()->read_property(object, member, type, cache_slot, tmp); 250 | } 251 | zend_string_release(member_str); 252 | 253 | zend_std_write_property(object, member, tmp, cache_slot); 254 | 255 | if (cache_slot) { 256 | *cache_slot = NULL; 257 | } 258 | return tmp; 259 | } 260 | #endif 261 | 262 | static inline int php_pq_object_write_prop_ex(zend_object *object, zend_string *member, zval *value) 263 | { 264 | php_pq_object_t *obj = PHP_PQ_OBJ(NULL, object); 265 | php_pq_object_prophandler_t *handler; 266 | 267 | if (!obj->intern) { 268 | php_error(E_RECOVERABLE_ERROR, "%s not initialized", ancestor(obj->zo.ce)->name->val); 269 | } else if (!(handler = zend_hash_find_ptr(obj->prophandler, member))) { 270 | /* default handler */ 271 | } else { 272 | if (handler->write) { 273 | handler->write(obj, value); 274 | } 275 | return SUCCESS; 276 | } 277 | return FAILURE; 278 | } 279 | #if PHP_VERSION_ID >= 80000 280 | zval *php_pq_object_write_prop(zend_object *object, zend_string *member, zval *value, void **cache_slot) 281 | { 282 | if (SUCCESS != php_pq_object_write_prop_ex(object, member, value)) { 283 | return zend_std_write_property(object, member, value, cache_slot); 284 | } 285 | return value; 286 | } 287 | #elif PHP_VERSION_ID >= 70400 288 | zval *php_pq_object_write_prop(zval *object, zval *member, zval *value, void **cache_slot) 289 | { 290 | zend_string *member_str = zval_get_string(member); 291 | if (SUCCESS != php_pq_object_write_prop_ex(Z_OBJ_P(object), member_str, value)) { 292 | value = zend_std_write_property(object, member, value, cache_slot); 293 | } 294 | zend_string_release(member_str); 295 | return value; 296 | } 297 | #else 298 | void php_pq_object_write_prop(zval *object, zval *member, zval *value, void **cache_slot) 299 | { 300 | zend_string *member_str = zval_get_string(member); 301 | if (SUCCESS != php_pq_object_write_prop_ex(Z_OBJ_P(object), member_str, value)) { 302 | zend_std_write_property(object, member, value, cache_slot); 303 | } 304 | zend_string_release(member_str); 305 | } 306 | #endif 307 | 308 | #if PHP_VERSION_ID >= 80000 309 | zval *php_pq_object_get_prop_ptr_null(zend_object *object, zend_string *member, int type, void **cache_slot) 310 | { 311 | return NULL; 312 | } 313 | #else 314 | zval *php_pq_object_get_prop_ptr_null(zval *object, zval *member, int type, void **cache_slot) 315 | { 316 | return NULL; 317 | } 318 | #endif 319 | 320 | void php_pq_object_prophandler_dtor(zval *zv) { 321 | pefree(Z_PTR_P(zv), 1); 322 | } 323 | 324 | -------------------------------------------------------------------------------- /src/php_pq_object.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifndef PHP_PQ_OBJECT_H 14 | #define PHP_PQ_OBJECT_H 15 | 16 | #define PHP_PQ_OBJ_DECL(_intern_type) \ 17 | _intern_type intern; \ 18 | HashTable *prophandler; \ 19 | HashTable gc; \ 20 | zend_object zo; 21 | 22 | typedef struct php_pq_object { 23 | PHP_PQ_OBJ_DECL(void *) 24 | } php_pq_object_t; 25 | 26 | static inline void *PHP_PQ_OBJ(zval *zv, zend_object *zo) { 27 | if (zv) { 28 | zo = Z_OBJ_P(zv); 29 | } 30 | return (void *) (((char *) zo) - zo->handlers->offset); 31 | } 32 | 33 | extern zend_class_entry *ancestor(zend_class_entry *ce); 34 | 35 | typedef void (*php_pq_object_prophandler_func_t)(void *o, zval *return_value); 36 | 37 | typedef struct php_pq_object_prophandler { 38 | php_pq_object_prophandler_func_t read; 39 | php_pq_object_prophandler_func_t write; 40 | php_pq_object_prophandler_func_t gc; 41 | } php_pq_object_prophandler_t; 42 | 43 | extern void php_pq_object_prophandler_dtor(zval *zv); 44 | 45 | extern void *php_pq_object_create(zend_class_entry *ce, void *intern, size_t obj_size, zend_object_handlers *oh, HashTable *ph); 46 | extern void php_pq_object_dtor(zend_object *obj); 47 | extern void php_pq_object_to_zval(void *o, zval *zv); 48 | extern void php_pq_object_to_zval_no_addref(void *o, zval *zv); 49 | extern void php_pq_object_addref(void *o); 50 | extern void php_pq_object_delref(void *o); 51 | 52 | #if PHP_VERSION_ID >= 80000 53 | extern HashTable *php_pq_object_debug_info(zend_object *object, int *temp); 54 | extern HashTable *php_pq_object_properties(zend_object *object); 55 | extern HashTable *php_pq_object_get_gc(zend_object *object, zval **table, int *n); 56 | extern zval *php_pq_object_read_prop(zend_object *object, zend_string *member, int type, void **cache_slot, zval *tmp); 57 | extern zval *php_pq_object_write_prop(zend_object *object, zend_string *member, zval *value, void **cache_slot); 58 | extern zval *php_pq_object_get_prop_ptr_null(zend_object *object, zend_string *member, int type, void **cache_slot); 59 | #else 60 | extern HashTable *php_pq_object_debug_info(zval *object, int *temp); 61 | extern HashTable *php_pq_object_properties(zval *object); 62 | extern HashTable *php_pq_object_get_gc(zval *object, zval **table, int *n); 63 | extern zval *php_pq_object_read_prop(zval *object, zval *member, int type, void **cache_slot, zval *tmp); 64 | # if PHP_VERSION_ID >= 70400 65 | extern zval *php_pq_object_write_prop(zval *object, zval *member, zval *value, void **cache_slot); 66 | # else 67 | extern void php_pq_object_write_prop(zval *object, zval *member, zval *value, void **cache_slot); 68 | # endif 69 | extern zval *php_pq_object_get_prop_ptr_null(zval *object, zval *member, int type, void **cache_slot); 70 | #endif 71 | 72 | #endif 73 | 74 | /* 75 | * Local variables: 76 | * tab-width: 4 77 | * c-basic-offset: 4 78 | * End: 79 | * vim600: noet sw=4 ts=4 fdm=marker 80 | * vim<600: noet sw=4 ts=4 81 | */ 82 | -------------------------------------------------------------------------------- /src/php_pq_params.c: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "php_pq.h" 27 | #include "php_pq_params.h" 28 | #include "php_pq_misc.h" 29 | #undef PHP_PQ_TYPE 30 | #include "php_pq_type.h" 31 | 32 | void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv) 33 | { 34 | zend_hash_clean(&p->type.conv); 35 | zend_hash_copy(&p->type.conv, conv, (copy_ctor_func_t) zval_add_ref); 36 | } 37 | 38 | static int apply_to_oid(zval *ztype, void *arg) 39 | { 40 | Oid **types = arg; 41 | 42 | **types = zval_get_long(ztype); 43 | ++*types; 44 | 45 | return ZEND_HASH_APPLY_KEEP; 46 | } 47 | 48 | unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids) 49 | { 50 | p->type.count = oids ? zend_hash_num_elements(oids) : 0; 51 | 52 | if (p->type.oids) { 53 | efree(p->type.oids); 54 | p->type.oids = NULL; 55 | } 56 | if (p->type.count) { 57 | Oid *ptr = ecalloc(p->type.count + 1, sizeof(*p->type.oids)); 58 | /* +1 for when less types than params are specified */ 59 | p->type.oids = ptr; 60 | zend_hash_apply_with_argument(oids, apply_to_oid, &ptr); 61 | } 62 | return p->type.count; 63 | } 64 | 65 | unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type) 66 | { 67 | p->type.oids = safe_erealloc(p->type.oids, ++p->type.count, sizeof(*p->type.oids), sizeof(*p->type.oids)); 68 | p->type.oids[p->type.count] = 0; 69 | p->type.oids[p->type.count-1] = type; 70 | return p->type.count; 71 | } 72 | 73 | 74 | static zend_string *object_param_to_string(php_pq_params_t *p, zval *zobj, Oid type) 75 | { 76 | #ifdef PHP_PQ_OID_JSON 77 | smart_str str = {0}; 78 | #endif 79 | 80 | switch (type) { 81 | #ifdef PHP_PQ_OID_JSON 82 | # ifdef PHP_PQ_OID_JSONB 83 | case PHP_PQ_OID_JSONB: 84 | # endif 85 | case PHP_PQ_OID_JSON: 86 | # if PHP_VERSION_ID >= 70100 87 | JSON_G(encode_max_depth) = PHP_JSON_PARSER_DEFAULT_DEPTH; 88 | # endif 89 | php_json_encode(&str, zobj, PHP_JSON_UNESCAPED_UNICODE); 90 | smart_str_0(&str); 91 | return str.s; 92 | #endif 93 | 94 | case PHP_PQ_OID_DATE: 95 | return php_pqdt_to_string(zobj, "Y-m-d"); 96 | #ifdef PHP_PQ_OID_ABSTIME 97 | case PHP_PQ_OID_ABSTIME: 98 | return php_pqdt_to_string(zobj, "Y-m-d H:i:s"); 99 | #endif 100 | case PHP_PQ_OID_TIMESTAMP: 101 | return php_pqdt_to_string(zobj, "Y-m-d H:i:s.u"); 102 | 103 | case PHP_PQ_OID_TIMESTAMPTZ: 104 | return php_pqdt_to_string(zobj, "Y-m-d H:i:s.uO"); 105 | } 106 | 107 | return zval_get_string(zobj); 108 | } 109 | 110 | struct apply_to_param_from_array_arg { 111 | php_pq_params_t *params; 112 | unsigned index; 113 | smart_str *buffer; 114 | Oid type; 115 | char delim; 116 | zval *zconv; 117 | }; 118 | 119 | static int apply_to_param_from_array(zval *zparam, void *arg_ptr) 120 | { 121 | struct apply_to_param_from_array_arg subarg, *arg = arg_ptr; 122 | char *tmp; 123 | size_t len; 124 | zend_string *str, *tmpstr; 125 | 126 | if (arg->index++) { 127 | smart_str_appendc(arg->buffer, arg->delim); 128 | } 129 | 130 | if (arg->zconv) { 131 | zval ztype, rv; 132 | 133 | ZVAL_LONG(&ztype, arg->type); 134 | php_pq_call_method(arg->zconv, "converttostring", 2, &rv, zparam, &ztype); 135 | tmpstr = zval_get_string(&rv); 136 | zval_ptr_dtor(&rv); 137 | goto append_string; 138 | 139 | } else { 140 | again: 141 | switch (Z_TYPE_P(zparam)) { 142 | case IS_REFERENCE: 143 | ZVAL_DEREF(zparam); 144 | goto again; 145 | 146 | case IS_NULL: 147 | smart_str_appends(arg->buffer, "NULL"); 148 | break; 149 | 150 | case IS_TRUE: 151 | smart_str_appends(arg->buffer, "t"); 152 | break; 153 | 154 | case IS_FALSE: 155 | smart_str_appends(arg->buffer, "f"); 156 | break; 157 | 158 | case IS_LONG: 159 | smart_str_append_long(arg->buffer, Z_LVAL_P(zparam)); 160 | break; 161 | 162 | case IS_DOUBLE: 163 | len = spprintf(&tmp, 0, "%F", Z_DVAL_P(zparam)); 164 | smart_str_appendl(arg->buffer, tmp, len); 165 | efree(tmp); 166 | break; 167 | 168 | case IS_ARRAY: 169 | subarg = *arg; 170 | subarg.index = 0; 171 | smart_str_appendc(arg->buffer, '{'); 172 | zend_hash_apply_with_argument(Z_ARRVAL_P(zparam), apply_to_param_from_array, &subarg); 173 | smart_str_appendc(arg->buffer, '}'); 174 | break; 175 | 176 | case IS_OBJECT: 177 | if ((tmpstr = object_param_to_string(arg->params, zparam, arg->type))) { 178 | goto append_string; 179 | } 180 | /* no break */ 181 | default: 182 | tmpstr = zval_get_string(zparam); 183 | 184 | append_string: 185 | #if PHP_VERSION_ID < 70300 186 | str = php_addslashes(tmpstr, 1); 187 | #else 188 | str = php_addslashes(tmpstr); 189 | zend_string_release(tmpstr); 190 | #endif 191 | smart_str_appendc(arg->buffer, '"'); 192 | smart_str_appendl(arg->buffer, str->val, str->len); 193 | smart_str_appendc(arg->buffer, '"'); 194 | zend_string_release(str); 195 | break; 196 | } 197 | } 198 | ++arg->index; 199 | return ZEND_HASH_APPLY_KEEP; 200 | } 201 | 202 | static zend_string *array_param_to_string(php_pq_params_t *p, zval *zarr, Oid type) 203 | { 204 | smart_str s = {0}; 205 | struct apply_to_param_from_array_arg arg = {NULL}; 206 | 207 | switch (type) { 208 | #ifdef PHP_PQ_OID_JSON 209 | # ifdef PHP_PQ_OID_JSONB 210 | case PHP_PQ_OID_JSONB: 211 | # endif 212 | case PHP_PQ_OID_JSON: 213 | php_json_encode(&s, zarr, PHP_JSON_UNESCAPED_UNICODE); 214 | break; 215 | #endif 216 | 217 | default: 218 | arg.params = p; 219 | arg.buffer = &s; 220 | arg.type = PHP_PQ_TYPE_OF_ARRAY(type); 221 | arg.delim = PHP_PQ_DELIM_OF_ARRAY(type); 222 | arg.zconv = zend_hash_index_find(&p->type.conv, PHP_PQ_TYPE_OF_ARRAY(type)); 223 | smart_str_appendc(arg.buffer, '{'); 224 | SEPARATE_ZVAL(zarr); 225 | zend_hash_apply_with_argument(Z_ARRVAL_P(zarr), apply_to_param_from_array, &arg); 226 | smart_str_appendc(arg.buffer, '}'); 227 | break; 228 | } 229 | 230 | smart_str_0(&s); 231 | return s.s; 232 | } 233 | 234 | static void php_pq_params_set_param(php_pq_params_t *p, unsigned index, zval *zpp) 235 | { 236 | zval *zconv = NULL; 237 | Oid type = p->type.count > index ? p->type.oids[index] : 0; 238 | 239 | if (type && (zconv = zend_hash_index_find(&p->type.conv, type))) { 240 | zval ztype, rv; 241 | 242 | ZVAL_NULL(&rv); 243 | ZVAL_LONG(&ztype, type); 244 | php_pq_call_method(zconv, "converttostring", 2, &rv, zpp, &ztype); 245 | convert_to_string(&rv); 246 | p->param.strings[index] = Z_STRVAL_P(&rv); 247 | zend_hash_next_index_insert(&p->param.dtor, &rv); 248 | } else { 249 | zval tmp; 250 | zend_string *str = NULL; 251 | char tmp_str[64]; 252 | size_t tmp_len = 0; 253 | 254 | again: 255 | switch (Z_TYPE_P(zpp)) { 256 | case IS_REFERENCE: 257 | ZVAL_DEREF(zpp); 258 | goto again; 259 | 260 | case IS_NULL: 261 | p->param.strings[index] = NULL; 262 | return; 263 | 264 | case IS_TRUE: 265 | p->param.strings[index] = "t"; 266 | break; 267 | 268 | case IS_FALSE: 269 | p->param.strings[index] = "f"; 270 | return; 271 | 272 | case IS_DOUBLE: 273 | tmp_len = slprintf(tmp_str, sizeof(tmp_str), "%F", Z_DVAL_P(zpp)); 274 | str = zend_string_init(tmp_str, tmp_len, 0); 275 | break; 276 | 277 | case IS_ARRAY: 278 | str = array_param_to_string(p, zpp, type); 279 | break; 280 | 281 | case IS_OBJECT: 282 | if ((str = object_param_to_string(p, zpp, type))) { 283 | break; 284 | } 285 | /* no break */ 286 | default: 287 | str = zval_get_string(zpp); 288 | break; 289 | } 290 | 291 | if (str) { 292 | ZVAL_STR(&tmp, str); 293 | p->param.strings[index] = Z_STRVAL(tmp); 294 | zend_hash_next_index_insert(&p->param.dtor, &tmp); 295 | } 296 | } 297 | } 298 | 299 | struct apply_to_params_arg { 300 | php_pq_params_t *params; 301 | unsigned index; 302 | }; 303 | 304 | static int apply_to_params(zval *zp, void *arg_ptr) 305 | { 306 | struct apply_to_params_arg *arg = arg_ptr; 307 | 308 | ZVAL_DEREF(zp); 309 | SEPARATE_ZVAL(zp); 310 | php_pq_params_set_param(arg->params, arg->index++, zp); 311 | return ZEND_HASH_APPLY_KEEP; 312 | } 313 | 314 | unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param) 315 | { 316 | p->param.strings = safe_erealloc(p->param.strings, ++p->param.count, sizeof(*p->param.strings), 0); 317 | php_pq_params_set_param(p, p->param.count-1, param); 318 | return p->type.count; 319 | } 320 | 321 | unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params) 322 | { 323 | p->param.count = params ? zend_hash_num_elements(params) : 0; 324 | 325 | if (p->param.strings) { 326 | efree(p->param.strings); 327 | p->param.strings = NULL; 328 | } 329 | zend_hash_clean(&p->param.dtor); 330 | if (p->param.count) { 331 | struct apply_to_params_arg arg = {p, 0}; 332 | p->param.strings = ecalloc(p->param.count, sizeof(*p->param.strings)); 333 | zend_hash_apply_with_argument(params, apply_to_params, &arg); 334 | } 335 | return p->param.count; 336 | } 337 | 338 | void php_pq_params_free(php_pq_params_t **p) 339 | { 340 | if (*p) { 341 | php_pq_params_set_type_oids(*p, NULL); 342 | php_pq_params_set_params(*p, NULL); 343 | 344 | zend_hash_destroy(&(*p)->param.dtor); 345 | zend_hash_destroy(&(*p)->type.conv); 346 | 347 | efree(*p); 348 | *p = NULL; 349 | } 350 | } 351 | 352 | php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params) 353 | { 354 | php_pq_params_t *p = ecalloc(1, sizeof(*p)); 355 | 356 | zend_hash_init(&p->type.conv, 0, NULL, ZVAL_PTR_DTOR, 0); 357 | zend_hash_init(&p->param.dtor, 0, NULL, ZVAL_PTR_DTOR, 0); 358 | 359 | if (conv) { 360 | php_pq_params_set_type_conv(p, conv); 361 | } 362 | if (oids) { 363 | php_pq_params_set_type_oids(p, oids); 364 | } 365 | if (params) { 366 | php_pq_params_set_params(p, params); 367 | } 368 | 369 | return p; 370 | } 371 | 372 | /* 373 | * Local variables: 374 | * tab-width: 4 375 | * c-basic-offset: 4 376 | * End: 377 | * vim600: noet sw=4 ts=4 fdm=marker 378 | * vim<600: noet sw=4 ts=4 379 | */ 380 | -------------------------------------------------------------------------------- /src/php_pq_params.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifndef PHP_PQ_PARAMS_H 14 | #define PHP_PQ_PARAMS_H 15 | 16 | typedef struct php_pq_params { 17 | struct { 18 | HashTable conv; 19 | unsigned count; 20 | Oid *oids; 21 | } type; 22 | struct { 23 | HashTable dtor; 24 | unsigned count; 25 | char **strings; 26 | } param; 27 | } php_pq_params_t; 28 | 29 | extern php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params); 30 | extern void php_pq_params_free(php_pq_params_t **p); 31 | extern unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params); 32 | extern unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids); 33 | extern unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type); 34 | extern unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param); 35 | extern void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv); 36 | 37 | #endif 38 | 39 | /* 40 | * Local variables: 41 | * tab-width: 4 42 | * c-basic-offset: 4 43 | * End: 44 | * vim600: noet sw=4 ts=4 fdm=marker 45 | * vim<600: noet sw=4 ts=4 46 | */ 47 | -------------------------------------------------------------------------------- /src/php_pqcancel.c: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include "php_pq.h" 22 | #include "php_pq_misc.h" 23 | #include "php_pq_object.h" 24 | #include "php_pqexc.h" 25 | #include "php_pqcancel.h" 26 | 27 | zend_class_entry *php_pqcancel_class_entry; 28 | static zend_object_handlers php_pqcancel_object_handlers; 29 | static HashTable php_pqcancel_object_prophandlers; 30 | 31 | static void php_pqcancel_object_free(zend_object *o) 32 | { 33 | php_pqcancel_object_t *obj = PHP_PQ_OBJ(NULL, o); 34 | #if DBG_GC 35 | fprintf(stderr, "FREE cancel(#%d) %p (conn(#%d): %p)\n", obj->zo.handle, obj, obj->intern->conn->zo.handle, obj->intern->conn); 36 | #endif 37 | if (obj->intern) { 38 | PQfreeCancel(obj->intern->cancel); 39 | php_pq_object_delref(obj->intern->conn); 40 | efree(obj->intern); 41 | obj->intern = NULL; 42 | } 43 | php_pq_object_dtor(o); 44 | } 45 | 46 | php_pqcancel_object_t *php_pqcancel_create_object_ex(zend_class_entry *ce, php_pqcancel_t *intern) 47 | { 48 | return php_pq_object_create(ce, intern, sizeof(php_pqcancel_object_t), 49 | &php_pqcancel_object_handlers, &php_pqcancel_object_prophandlers); 50 | } 51 | 52 | static zend_object *php_pqcancel_create_object(zend_class_entry *class_type) 53 | { 54 | return &php_pqcancel_create_object_ex(class_type, NULL)->zo; 55 | } 56 | 57 | static void php_pqcancel_object_read_connection(void *o, zval *return_value) 58 | { 59 | php_pqcancel_object_t *obj = o; 60 | 61 | php_pq_object_to_zval(obj->intern->conn, return_value); 62 | } 63 | 64 | static void php_pqcancel_object_gc_connection(void *o, zval *return_value) 65 | { 66 | php_pqcancel_object_t *obj = o; 67 | zval zconn; 68 | 69 | php_pq_object_to_zval_no_addref(obj->intern->conn, &zconn); 70 | add_next_index_zval(return_value, &zconn); 71 | } 72 | 73 | ZEND_BEGIN_ARG_INFO_EX(ai_pqcancel_construct, 0, 0, 1) 74 | ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) 75 | ZEND_END_ARG_INFO(); 76 | static PHP_METHOD(pqcancel, __construct) { 77 | zend_error_handling zeh; 78 | zval *zconn; 79 | ZEND_RESULT_CODE rv; 80 | 81 | zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh); 82 | rv = zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zconn, php_pqconn_class_entry); 83 | zend_restore_error_handling(&zeh); 84 | 85 | if (SUCCESS == rv) { 86 | php_pqconn_object_t *conn_obj = PHP_PQ_OBJ(zconn, NULL); 87 | 88 | if (!conn_obj->intern) { 89 | throw_exce(EX_UNINITIALIZED, "pq\\Connection not initialized"); 90 | } else { 91 | PGcancel *cancel = PQgetCancel(conn_obj->intern->conn); 92 | 93 | if (!cancel) { 94 | throw_exce(EX_RUNTIME, "Failed to acquire cancel (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); 95 | } else { 96 | php_pqcancel_object_t *obj = PHP_PQ_OBJ(getThis(), NULL); 97 | 98 | obj->intern = ecalloc(1, sizeof(*obj->intern)); 99 | obj->intern->cancel = cancel; 100 | php_pq_object_addref(conn_obj); 101 | obj->intern->conn = conn_obj; 102 | } 103 | } 104 | } 105 | } 106 | 107 | ZEND_BEGIN_ARG_INFO_EX(ai_pqcancel_cancel, 0, 0, 0) 108 | ZEND_END_ARG_INFO(); 109 | static PHP_METHOD(pqcancel, cancel) { 110 | zend_error_handling zeh; 111 | ZEND_RESULT_CODE rv; 112 | 113 | zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh); 114 | rv = zend_parse_parameters_none(); 115 | zend_restore_error_handling(&zeh); 116 | 117 | if (SUCCESS == rv) { 118 | php_pqcancel_object_t *obj = PHP_PQ_OBJ(getThis(), NULL); 119 | 120 | if (!obj->intern) { 121 | throw_exce(EX_UNINITIALIZED, "pq\\Cancel not initialized"); 122 | } else { 123 | char err[256] = {0}; 124 | 125 | if (!PQcancel(obj->intern->cancel, err, sizeof(err))) { 126 | throw_exce(EX_RUNTIME, "Failed to request cancellation (%s)", err); 127 | } 128 | } 129 | } 130 | } 131 | 132 | static zend_function_entry php_pqcancel_methods[] = { 133 | PHP_ME(pqcancel, __construct, ai_pqcancel_construct, ZEND_ACC_PUBLIC) 134 | PHP_ME(pqcancel, cancel, ai_pqcancel_cancel, ZEND_ACC_PUBLIC) 135 | {0} 136 | }; 137 | 138 | PHP_MSHUTDOWN_FUNCTION(pqcancel) 139 | { 140 | zend_hash_destroy(&php_pqcancel_object_prophandlers); 141 | return SUCCESS; 142 | } 143 | 144 | PHP_MINIT_FUNCTION(pqcancel) 145 | { 146 | zend_class_entry ce = {0}; 147 | php_pq_object_prophandler_t ph = {0}; 148 | 149 | INIT_NS_CLASS_ENTRY(ce, "pq", "Cancel", php_pqcancel_methods); 150 | php_pqcancel_class_entry = zend_register_internal_class_ex(&ce, NULL); 151 | php_pqcancel_class_entry->create_object = php_pqcancel_create_object; 152 | 153 | memcpy(&php_pqcancel_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 154 | php_pqcancel_object_handlers.offset = XtOffsetOf(php_pqcancel_object_t, zo); 155 | php_pqcancel_object_handlers.free_obj = php_pqcancel_object_free; 156 | php_pqcancel_object_handlers.read_property = php_pq_object_read_prop; 157 | php_pqcancel_object_handlers.write_property = php_pq_object_write_prop; 158 | php_pqcancel_object_handlers.clone_obj = NULL; 159 | php_pqcancel_object_handlers.get_property_ptr_ptr = php_pq_object_get_prop_ptr_null; 160 | php_pqcancel_object_handlers.get_gc = php_pq_object_get_gc; 161 | php_pqcancel_object_handlers.get_properties = php_pq_object_properties; 162 | php_pqcancel_object_handlers.get_debug_info = php_pq_object_debug_info; 163 | 164 | zend_hash_init(&php_pqcancel_object_prophandlers, 1, NULL, php_pq_object_prophandler_dtor, 1); 165 | 166 | zend_declare_property_null(php_pqcancel_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC); 167 | ph.read = php_pqcancel_object_read_connection; 168 | ph.gc = php_pqcancel_object_gc_connection; 169 | zend_hash_str_add_mem(&php_pqcancel_object_prophandlers, ZEND_STRL("connection"), (void *) &ph, sizeof(ph)); 170 | 171 | return SUCCESS; 172 | } 173 | 174 | /* 175 | * Local variables: 176 | * tab-width: 4 177 | * c-basic-offset: 4 178 | * End: 179 | * vim600: noet sw=4 ts=4 fdm=marker 180 | * vim<600: noet sw=4 ts=4 181 | */ 182 | -------------------------------------------------------------------------------- /src/php_pqcancel.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQCANCEL_H 15 | #define PHP_PQCANCEL_H 16 | 17 | #include "php_pqconn.h" 18 | 19 | typedef struct php_pqcancel { 20 | PGcancel *cancel; 21 | php_pqconn_object_t *conn; 22 | } php_pqcancel_t; 23 | 24 | typedef struct php_pqcancel_object { 25 | PHP_PQ_OBJ_DECL(php_pqcancel_t *) 26 | } php_pqcancel_object_t; 27 | 28 | extern zend_class_entry *php_pqcancel_class_entry; 29 | extern php_pqcancel_object_t *php_pqcancel_create_object_ex(zend_class_entry *ce, php_pqcancel_t *intern); 30 | 31 | extern PHP_MINIT_FUNCTION(pqcancel); 32 | extern PHP_MSHUTDOWN_FUNCTION(pqcancel); 33 | 34 | #endif 35 | 36 | /* 37 | * Local variables: 38 | * tab-width: 4 39 | * c-basic-offset: 4 40 | * End: 41 | * vim600: noet sw=4 ts=4 fdm=marker 42 | * vim<600: noet sw=4 ts=4 43 | */ 44 | -------------------------------------------------------------------------------- /src/php_pqconn.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQCONN_H 15 | #define PHP_PQCONN_H 16 | 17 | #define PHP_PQCONN_ASYNC 0x01 18 | #define PHP_PQCONN_PERSISTENT 0x02 19 | 20 | #include 21 | #include "php_pq_object.h" 22 | #include "php_pq_callback.h" 23 | #include "php_pq_object.h" 24 | #include "php_pq_params.h" 25 | 26 | typedef struct php_pqconn { 27 | PGconn *conn; 28 | int (*poller)(PGconn *); 29 | php_resource_factory_t factory; 30 | HashTable listeners; 31 | HashTable statements; 32 | HashTable converters; 33 | HashTable eventhandlers; 34 | php_pq_callback_t onevent; 35 | unsigned unbuffered:1; 36 | unsigned default_fetch_type:2; 37 | unsigned default_txn_isolation:2; 38 | unsigned default_txn_readonly:1; 39 | unsigned default_txn_deferrable:1; 40 | unsigned default_auto_convert:16; 41 | } php_pqconn_t; 42 | 43 | typedef struct php_pqconn_object { 44 | PHP_PQ_OBJ_DECL(php_pqconn_t *) 45 | } php_pqconn_object_t; 46 | 47 | typedef struct php_pqconn_resource_factory_data { 48 | char *dsn; 49 | unsigned long flags; 50 | } php_pqconn_resource_factory_data_t; 51 | 52 | extern php_resource_factory_ops_t *php_pqconn_get_resource_factory_ops(void); 53 | 54 | extern zend_class_entry *php_pqconn_class_entry; 55 | extern php_pqconn_object_t *php_pqconn_create_object_ex(zend_class_entry *ce, php_pqconn_t *intern); 56 | extern void php_pqconn_notify_listeners(php_pqconn_object_t *obj); 57 | extern ZEND_RESULT_CODE php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params); 58 | extern ZEND_RESULT_CODE php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params); 59 | extern ZEND_RESULT_CODE php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable); 60 | extern ZEND_RESULT_CODE php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable); 61 | extern ZEND_RESULT_CODE php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl); 62 | extern ZEND_RESULT_CODE php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl); 63 | 64 | extern PHP_MINIT_FUNCTION(pqconn); 65 | extern PHP_MSHUTDOWN_FUNCTION(pqconn); 66 | 67 | #endif 68 | 69 | /* 70 | * Local variables: 71 | * tab-width: 4 72 | * c-basic-offset: 4 73 | * End: 74 | * vim600: noet sw=4 ts=4 fdm=marker 75 | * vim<600: noet sw=4 ts=4 76 | */ 77 | -------------------------------------------------------------------------------- /src/php_pqconn_event.c: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include "php_pq.h" 24 | #include "php_pq_misc.h" 25 | #include "php_pq_object.h" 26 | #include "php_pqconn_event.h" 27 | #include "php_pqstm.h" 28 | #include "php_pqres.h" 29 | 30 | static int apply_event(zval *p, void *a) 31 | { 32 | php_pq_callback_t *cb = Z_PTR_P(p); 33 | zval *args = a; 34 | zval rv; 35 | 36 | ZVAL_NULL(&rv); 37 | zend_fcall_info_args(&cb->fci, args); 38 | zend_fcall_info_call(&cb->fci, &cb->fcc, &rv, NULL); 39 | zend_fcall_info_args_clear(&cb->fci, 0); 40 | zval_ptr_dtor(&rv); 41 | 42 | return ZEND_HASH_APPLY_KEEP; 43 | } 44 | 45 | 46 | static inline PGresult *relisten(PGconn *conn, const char *channel_str, size_t channel_len) 47 | { 48 | char *quoted_channel = PQescapeIdentifier(conn, channel_str, channel_len); 49 | PGresult *res = NULL; 50 | 51 | if (quoted_channel) { 52 | smart_str cmd = {0}; 53 | 54 | smart_str_appends(&cmd, "LISTEN "); 55 | smart_str_appends(&cmd, quoted_channel); 56 | smart_str_0(&cmd); 57 | 58 | res = php_pq_exec(conn, smart_str_v(&cmd)); 59 | 60 | smart_str_free(&cmd); 61 | PQfreemem(quoted_channel); 62 | } 63 | 64 | return res; 65 | } 66 | 67 | static int apply_relisten(zval *p, int argc, va_list argv, zend_hash_key *key) 68 | { 69 | php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *); 70 | PGresult *res = relisten(obj->intern->conn, key->key->val, key->key->len); 71 | 72 | if (res) { 73 | php_pqres_clear(res); 74 | } 75 | 76 | return ZEND_HASH_APPLY_KEEP; 77 | } 78 | 79 | static int apply_reprepare(zval *p, int argc, va_list argv, zend_hash_key *key) 80 | { 81 | php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *); 82 | php_pqstm_t *stm = Z_PTR_P(p); 83 | 84 | php_pqconn_prepare(NULL, obj, stm->name, stm->query, stm->params); 85 | 86 | return ZEND_HASH_APPLY_KEEP; 87 | } 88 | 89 | static void php_pqconn_event_connreset(PGEventConnReset *event) 90 | { 91 | php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event); 92 | 93 | if (data) { 94 | zval *zevhs; 95 | 96 | /* restore listeners */ 97 | zend_hash_apply_with_arguments(&data->obj->intern->listeners, apply_relisten, 1, data->obj); 98 | 99 | /* restore statements */ 100 | zend_hash_apply_with_arguments(&data->obj->intern->statements, apply_reprepare, 1, data->obj); 101 | 102 | /* eventhandler */ 103 | if ((zevhs = zend_hash_str_find(&data->obj->intern->eventhandlers, ZEND_STRL("reset")))) { 104 | zval args, connection; 105 | 106 | array_init(&args); 107 | php_pq_object_to_zval(data->obj, &connection); 108 | add_next_index_zval(&args, &connection); 109 | zend_hash_apply_with_argument(Z_ARRVAL_P(zevhs), apply_event, &args); 110 | zval_ptr_dtor(&args); 111 | } 112 | } 113 | } 114 | 115 | static void php_pqconn_event_resultcreate(PGEventResultCreate *event) 116 | { 117 | php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event); 118 | 119 | if (data) { 120 | php_pqres_object_t *obj = php_pqres_init_instance_data(event->result, data->obj); 121 | zval *zevhs; 122 | 123 | /* event listener */ 124 | if ((zevhs = zend_hash_str_find(&data->obj->intern->eventhandlers, ZEND_STRL("result")))) { 125 | zval args, connection, res; 126 | 127 | array_init(&args); 128 | php_pq_object_to_zval(data->obj, &connection); 129 | add_next_index_zval(&args, &connection); 130 | php_pq_object_to_zval(obj, &res); 131 | add_next_index_zval(&args, &res); 132 | zend_hash_apply_with_argument(Z_ARRVAL_P(zevhs), apply_event, &args); 133 | zval_ptr_dtor(&args); 134 | } 135 | 136 | /* async callback */ 137 | if (php_pq_callback_is_enabled(&data->obj->intern->onevent)) { 138 | zval res; 139 | 140 | php_pq_object_to_zval(obj, &res); 141 | zend_fcall_info_argn(&data->obj->intern->onevent.fci, 1, &res); 142 | zend_fcall_info_call(&data->obj->intern->onevent.fci, &data->obj->intern->onevent.fcc, NULL, NULL); 143 | zval_ptr_dtor(&res); 144 | } 145 | 146 | } 147 | } 148 | 149 | static void php_pqconn_event_resultdestroy(PGEventResultDestroy *event) 150 | { 151 | php_pqres_object_t *obj = PQresultInstanceData(event->result, php_pqconn_event); 152 | 153 | if (obj) { 154 | obj->intern->res = NULL; 155 | assert(GC_REFCOUNT(&obj->zo)); 156 | php_pq_object_delref(obj); 157 | } 158 | } 159 | 160 | int php_pqconn_event(PGEventId id, void *e, void *data) 161 | { 162 | switch (id) { 163 | case PGEVT_CONNRESET: 164 | php_pqconn_event_connreset(e); 165 | break; 166 | case PGEVT_RESULTCREATE: 167 | php_pqconn_event_resultcreate(e); 168 | break; 169 | case PGEVT_RESULTDESTROY: 170 | php_pqconn_event_resultdestroy(e); 171 | break; 172 | default: 173 | break; 174 | } 175 | 176 | return 1; 177 | } 178 | 179 | php_pqconn_event_data_t *php_pqconn_event_data_init(php_pqconn_object_t *obj) 180 | { 181 | php_pqconn_event_data_t *data = emalloc(sizeof(*data)); 182 | 183 | data->obj = obj; 184 | data->res = NULL; 185 | 186 | return data; 187 | } 188 | 189 | void php_pqconn_notice_recv(void *p, const PGresult *res) 190 | { 191 | php_pqconn_event_data_t *data = p; 192 | 193 | if (data) { 194 | zval *zevhs; 195 | 196 | if ((zevhs = zend_hash_str_find(&data->obj->intern->eventhandlers, ZEND_STRL("notice")))) { 197 | zval args, connection; 198 | 199 | array_init(&args); 200 | php_pq_object_to_zval(data->obj, &connection); 201 | add_next_index_zval(&args, &connection); 202 | add_next_index_string(&args, PHP_PQresultErrorMessage(res)); 203 | zend_hash_apply_with_argument(Z_ARRVAL_P(zevhs), apply_event, &args); 204 | zval_ptr_dtor(&args); 205 | } 206 | } 207 | } 208 | 209 | void php_pqconn_notice_ignore(void *p, const PGresult *res) 210 | { 211 | } 212 | 213 | /* 214 | * Local variables: 215 | * tab-width: 4 216 | * c-basic-offset: 4 217 | * End: 218 | * vim600: noet sw=4 ts=4 fdm=marker 219 | * vim<600: noet sw=4 ts=4 220 | */ 221 | -------------------------------------------------------------------------------- /src/php_pqconn_event.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQCONN_EVENT_H 15 | #define PHP_PQCONN_EVENT_H 16 | 17 | #include 18 | 19 | #include "php_pqconn.h" 20 | #include "php_pqres.h" 21 | 22 | typedef struct php_pqconn_event_data { 23 | php_pqconn_object_t *obj; 24 | php_pqres_object_t *res; 25 | } php_pqconn_event_data_t; 26 | 27 | extern php_pqconn_event_data_t *php_pqconn_event_data_init(php_pqconn_object_t *obj); 28 | extern void php_pqconn_notice_recv(void *p, const PGresult *res); 29 | extern void php_pqconn_notice_ignore(void *p, const PGresult *res); 30 | extern int php_pqconn_event(PGEventId id, void *e, void *data); 31 | 32 | #endif 33 | 34 | /* 35 | * Local variables: 36 | * tab-width: 4 37 | * c-basic-offset: 4 38 | * End: 39 | * vim600: noet sw=4 ts=4 fdm=marker 40 | * vim<600: noet sw=4 ts=4 41 | */ 42 | -------------------------------------------------------------------------------- /src/php_pqcopy.c: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include "php_pq.h" 23 | #include "php_pq_misc.h" 24 | #include "php_pq_object.h" 25 | #include "php_pqexc.h" 26 | #include "php_pqres.h" 27 | #include "php_pqconn.h" 28 | #include "php_pqcopy.h" 29 | 30 | zend_class_entry *php_pqcopy_class_entry; 31 | static zend_object_handlers php_pqcopy_object_handlers; 32 | static HashTable php_pqcopy_object_prophandlers; 33 | 34 | static void php_pqcopy_object_free(zend_object *o) 35 | { 36 | php_pqcopy_object_t *obj = PHP_PQ_OBJ(NULL, o); 37 | #if DBG_GC 38 | fprintf(stderr, "FREE copy(#%d) %p (conn(#%d): %p)\n", obj->zo.handle, obj, obj->intern->conn->zo.handle, obj->intern->conn); 39 | #endif 40 | if (obj->intern) { 41 | efree(obj->intern->expression); 42 | efree(obj->intern->options); 43 | php_pq_object_delref(obj->intern->conn); 44 | efree(obj->intern); 45 | obj->intern = NULL; 46 | } 47 | php_pq_object_dtor(o); 48 | } 49 | 50 | php_pqcopy_object_t *php_pqcopy_create_object_ex(zend_class_entry *ce, php_pqcopy_t *intern) 51 | { 52 | return php_pq_object_create(ce, intern, sizeof(php_pqcopy_object_t), 53 | &php_pqcopy_object_handlers, &php_pqcopy_object_prophandlers); 54 | } 55 | 56 | static zend_object *php_pqcopy_create_object(zend_class_entry *class_type) 57 | { 58 | return &php_pqcopy_create_object_ex(class_type, NULL)->zo; 59 | } 60 | 61 | static void php_pqcopy_object_read_connection(void *o, zval *return_value) 62 | { 63 | php_pqcopy_object_t *obj = o; 64 | 65 | php_pq_object_to_zval(obj->intern->conn, return_value); 66 | } 67 | 68 | static void php_pqcopy_object_gc_connection(void *o, zval *return_value) 69 | { 70 | php_pqcopy_object_t *obj = o; 71 | zval zconn; 72 | 73 | php_pq_object_to_zval_no_addref(obj->intern->conn, &zconn); 74 | add_next_index_zval(return_value, &zconn); 75 | } 76 | 77 | static void php_pqcopy_object_read_direction(void *o, zval *return_value) 78 | { 79 | php_pqcopy_object_t *obj = o; 80 | 81 | RETVAL_LONG(obj->intern->direction); 82 | } 83 | 84 | static void php_pqcopy_object_read_expression(void *o, zval *return_value) 85 | { 86 | php_pqcopy_object_t *obj = o; 87 | 88 | RETURN_STRING(obj->intern->expression); 89 | } 90 | 91 | static void php_pqcopy_object_read_options(void *o, zval *return_value) 92 | { 93 | php_pqcopy_object_t *obj = o; 94 | 95 | RETURN_STRING(obj->intern->options); 96 | } 97 | 98 | ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_construct, 0, 0, 3) 99 | ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) 100 | ZEND_ARG_INFO(0, expression) 101 | ZEND_ARG_INFO(0, direction) 102 | ZEND_ARG_INFO(0, options) 103 | ZEND_END_ARG_INFO(); 104 | static PHP_METHOD(pqcopy, __construct) { 105 | zend_error_handling zeh; 106 | zval *zconn; 107 | char *expr_str, *opt_str = ""; 108 | size_t expr_len, opt_len = 0; 109 | zend_long direction; 110 | ZEND_RESULT_CODE rv; 111 | 112 | zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh); 113 | rv = zend_parse_parameters(ZEND_NUM_ARGS(), "Osl|s", &zconn, php_pqconn_class_entry, &expr_str, &expr_len, &direction, &opt_str, &opt_len); 114 | zend_restore_error_handling(&zeh); 115 | 116 | if (SUCCESS == rv) { 117 | php_pqconn_object_t *conn_obj = PHP_PQ_OBJ(zconn, NULL); 118 | 119 | if (!conn_obj->intern) { 120 | throw_exce(EX_UNINITIALIZED, "pq\\Connection not initialized"); 121 | } else { 122 | php_pqcopy_object_t *obj = PHP_PQ_OBJ(getThis(), NULL); 123 | smart_str cmd = {0}; 124 | PGresult *res; 125 | 126 | smart_str_appends(&cmd, "COPY "); 127 | smart_str_appendl(&cmd, expr_str, expr_len); 128 | 129 | switch (direction) { 130 | case PHP_PQCOPY_FROM_STDIN: 131 | smart_str_appends(&cmd, " FROM STDIN "); 132 | break; 133 | case PHP_PQCOPY_TO_STDOUT: 134 | smart_str_appends(&cmd, " TO STDOUT "); 135 | break; 136 | default: 137 | throw_exce(EX_RUNTIME, "Invalid COPY direction, expected one of FROM_STDIN (%d) TO_STDOUT (%d), got %ld", PHP_PQCOPY_FROM_STDIN, PHP_PQCOPY_TO_STDOUT, direction); 138 | smart_str_free(&cmd); 139 | return; 140 | } 141 | smart_str_appendl(&cmd, opt_str, opt_len); 142 | smart_str_0(&cmd); 143 | 144 | res = php_pq_exec(conn_obj->intern->conn, smart_str_v(&cmd)); 145 | 146 | if (!res) { 147 | throw_exce(EX_RUNTIME, "Failed to start %s (%s)", smart_str_v(&cmd), PHP_PQerrorMessage(obj->intern->conn->intern->conn)); 148 | } else { 149 | if (SUCCESS == php_pqres_success(res)) { 150 | obj->intern = ecalloc(1, sizeof(*obj->intern)); 151 | obj->intern->direction = direction; 152 | obj->intern->expression = estrdup(expr_str); 153 | obj->intern->options = estrdup(opt_str); 154 | obj->intern->conn = conn_obj; 155 | php_pq_object_addref(conn_obj); 156 | } 157 | 158 | php_pqres_clear(res); 159 | } 160 | 161 | smart_str_free(&cmd); 162 | php_pqconn_notify_listeners(obj->intern->conn); 163 | } 164 | } 165 | } 166 | 167 | ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_put, 0, 0, 1) 168 | ZEND_ARG_INFO(0, data) 169 | ZEND_END_ARG_INFO(); 170 | static PHP_METHOD(pqcopy, put) { 171 | zend_error_handling zeh; 172 | char *data_str; 173 | size_t data_len; 174 | ZEND_RESULT_CODE rv; 175 | 176 | zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh); 177 | rv = zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data_str, &data_len); 178 | zend_restore_error_handling(&zeh); 179 | 180 | if (SUCCESS == rv) { 181 | php_pqcopy_object_t *obj = PHP_PQ_OBJ(getThis(), NULL); 182 | 183 | if (!obj->intern) { 184 | throw_exce(EX_UNINITIALIZED, "pq\\COPY not initialized"); 185 | } else if (obj->intern->direction != PHP_PQCOPY_FROM_STDIN) { 186 | throw_exce(EX_BAD_METHODCALL, "pq\\COPY was not initialized with FROM_STDIN"); 187 | } else { 188 | if (1 != PQputCopyData(obj->intern->conn->intern->conn, data_str, data_len)) { 189 | throw_exce(EX_RUNTIME, "Failed to put COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); 190 | } 191 | php_pqconn_notify_listeners(obj->intern->conn); 192 | } 193 | } 194 | } 195 | 196 | ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_end, 0, 0, 0) 197 | ZEND_ARG_INFO(0, error) 198 | ZEND_END_ARG_INFO(); 199 | static PHP_METHOD(pqcopy, end) { 200 | zend_error_handling zeh; 201 | char *error_str = NULL; 202 | size_t error_len = 0; 203 | ZEND_RESULT_CODE rv; 204 | 205 | zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh); 206 | rv = zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &error_str, &error_len); 207 | zend_restore_error_handling(&zeh); 208 | 209 | if (SUCCESS == rv) { 210 | php_pqcopy_object_t *obj = PHP_PQ_OBJ(getThis(), NULL); 211 | 212 | if (!obj->intern) { 213 | throw_exce(EX_UNINITIALIZED, "pq\\COPY not intitialized"); 214 | } else if (obj->intern->direction != PHP_PQCOPY_FROM_STDIN) { 215 | throw_exce(EX_BAD_METHODCALL, "pq\\COPY was not intitialized with FROM_STDIN"); 216 | } else { 217 | if (1 != PQputCopyEnd(obj->intern->conn->intern->conn, error_str)) { 218 | throw_exce(EX_RUNTIME, "Failed to end COPY (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); 219 | } else { 220 | PGresult *res = PQgetResult(obj->intern->conn->intern->conn); 221 | 222 | if (!res) { 223 | throw_exce(EX_RUNTIME, "Failed to fetch COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); 224 | } else { 225 | php_pqres_success(res); 226 | php_pqres_clear(res); 227 | } 228 | } 229 | 230 | php_pqconn_notify_listeners(obj->intern->conn); 231 | } 232 | } 233 | } 234 | 235 | ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_get, 0, 0, 1) 236 | ZEND_ARG_INFO(1, data) 237 | ZEND_END_ARG_INFO(); 238 | static PHP_METHOD(pqcopy, get) { 239 | zend_error_handling zeh; 240 | zval *zdata; 241 | ZEND_RESULT_CODE rv; 242 | 243 | zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh); 244 | rv = zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zdata); 245 | zend_restore_error_handling(&zeh); 246 | 247 | if (SUCCESS == rv) { 248 | php_pqcopy_object_t *obj = PHP_PQ_OBJ(getThis(), NULL); 249 | 250 | if (!obj->intern) { 251 | throw_exce(EX_UNINITIALIZED, "pq\\COPY not initialized"); 252 | } else if (obj->intern->direction != PHP_PQCOPY_TO_STDOUT) { 253 | throw_exce(EX_RUNTIME, "pq\\COPY was not intialized with TO_STDOUT"); 254 | } else { 255 | PGresult *res; 256 | char *buffer = NULL; 257 | int bytes = PQgetCopyData(obj->intern->conn->intern->conn, &buffer, 0); 258 | 259 | switch (bytes) { 260 | case -2: 261 | throw_exce(EX_RUNTIME, "Failed to fetch COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); 262 | break; 263 | 264 | case -1: 265 | res = PQgetResult(obj->intern->conn->intern->conn); 266 | 267 | if (!res) { 268 | throw_exce(EX_RUNTIME, "Failed to fetch COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); 269 | } else { 270 | php_pqres_success(res); 271 | php_pqres_clear(res); 272 | RETVAL_FALSE; 273 | } 274 | break; 275 | 276 | default: 277 | ZVAL_DEREF(zdata); 278 | zval_dtor(zdata); 279 | if (buffer) { 280 | ZVAL_STRINGL(zdata, buffer, bytes); 281 | } else { 282 | ZVAL_EMPTY_STRING(zdata); 283 | } 284 | RETVAL_TRUE; 285 | break; 286 | } 287 | 288 | if (buffer) { 289 | PQfreemem(buffer); 290 | } 291 | } 292 | } 293 | } 294 | 295 | static zend_function_entry php_pqcopy_methods[] = { 296 | PHP_ME(pqcopy, __construct, ai_pqcopy_construct, ZEND_ACC_PUBLIC) 297 | PHP_ME(pqcopy, put, ai_pqcopy_put, ZEND_ACC_PUBLIC) 298 | PHP_ME(pqcopy, end, ai_pqcopy_end, ZEND_ACC_PUBLIC) 299 | PHP_ME(pqcopy, get, ai_pqcopy_get, ZEND_ACC_PUBLIC) 300 | {0} 301 | }; 302 | 303 | PHP_MSHUTDOWN_FUNCTION(pqcopy) 304 | { 305 | zend_hash_destroy(&php_pqcopy_object_prophandlers); 306 | return SUCCESS; 307 | } 308 | 309 | PHP_MINIT_FUNCTION(pqcopy) 310 | { 311 | zend_class_entry ce = {0}; 312 | php_pq_object_prophandler_t ph = {0}; 313 | 314 | INIT_NS_CLASS_ENTRY(ce, "pq", "COPY", php_pqcopy_methods); 315 | php_pqcopy_class_entry = zend_register_internal_class_ex(&ce, NULL); 316 | php_pqcopy_class_entry->create_object = php_pqcopy_create_object; 317 | 318 | memcpy(&php_pqcopy_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 319 | php_pqcopy_object_handlers.offset = XtOffsetOf(php_pqcopy_object_t, zo); 320 | php_pqcopy_object_handlers.free_obj = php_pqcopy_object_free; 321 | php_pqcopy_object_handlers.read_property = php_pq_object_read_prop; 322 | php_pqcopy_object_handlers.write_property = php_pq_object_write_prop; 323 | php_pqcopy_object_handlers.clone_obj = NULL; 324 | php_pqcopy_object_handlers.get_property_ptr_ptr = php_pq_object_get_prop_ptr_null; 325 | php_pqcopy_object_handlers.get_gc = php_pq_object_get_gc; 326 | php_pqcopy_object_handlers.get_properties = php_pq_object_properties; 327 | php_pqcopy_object_handlers.get_debug_info = php_pq_object_debug_info; 328 | 329 | zend_hash_init(&php_pqcopy_object_prophandlers, 4, NULL, php_pq_object_prophandler_dtor, 1); 330 | 331 | zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC); 332 | ph.read = php_pqcopy_object_read_connection; 333 | ph.gc = php_pqcopy_object_gc_connection; 334 | zend_hash_str_add_mem(&php_pqcopy_object_prophandlers, "connection", sizeof("connection")-1, (void *) &ph, sizeof(ph)); 335 | ph.gc = NULL; 336 | 337 | zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("expression"), ZEND_ACC_PUBLIC); 338 | ph.read = php_pqcopy_object_read_expression; 339 | zend_hash_str_add_mem(&php_pqcopy_object_prophandlers, "expression", sizeof("expression")-1, (void *) &ph, sizeof(ph)); 340 | 341 | zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("direction"), ZEND_ACC_PUBLIC); 342 | ph.read = php_pqcopy_object_read_direction; 343 | zend_hash_str_add_mem(&php_pqcopy_object_prophandlers, "direction", sizeof("direction")-1, (void *) &ph, sizeof(ph)); 344 | 345 | zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("options"), ZEND_ACC_PUBLIC); 346 | ph.read = php_pqcopy_object_read_options; 347 | zend_hash_str_add_mem(&php_pqcopy_object_prophandlers, "options", sizeof("options")-1, (void *) &ph, sizeof(ph)); 348 | 349 | zend_declare_class_constant_long(php_pqcopy_class_entry, ZEND_STRL("FROM_STDIN"), PHP_PQCOPY_FROM_STDIN); 350 | zend_declare_class_constant_long(php_pqcopy_class_entry, ZEND_STRL("TO_STDOUT"), PHP_PQCOPY_TO_STDOUT); 351 | 352 | return SUCCESS; 353 | } 354 | /* 355 | * Local variables: 356 | * tab-width: 4 357 | * c-basic-offset: 4 358 | * End: 359 | * vim600: noet sw=4 ts=4 fdm=marker 360 | * vim<600: noet sw=4 ts=4 361 | */ 362 | -------------------------------------------------------------------------------- /src/php_pqcopy.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQCOPY_H 15 | #define PHP_PQCOPY_H 16 | 17 | #include "php_pqconn.h" 18 | 19 | typedef enum php_pqcopy_direction { 20 | PHP_PQCOPY_FROM_STDIN, 21 | PHP_PQCOPY_TO_STDOUT 22 | } php_pqcopy_direction_t; 23 | 24 | typedef enum php_pqcopy_status { 25 | PHP_PQCOPY_FAIL, 26 | PHP_PQCOPY_CONT, 27 | PHP_PQCOPY_DONE 28 | } php_pqcopy_status_t; 29 | 30 | typedef struct php_pqcopy { 31 | php_pqcopy_direction_t direction; 32 | char *expression; 33 | char *options; 34 | php_pqconn_object_t *conn; 35 | } php_pqcopy_t; 36 | 37 | typedef struct php_pqcopy_object { 38 | PHP_PQ_OBJ_DECL(php_pqcopy_t *) 39 | } php_pqcopy_object_t; 40 | 41 | extern zend_class_entry *php_pqcopy_class_entry; 42 | extern php_pqcopy_object_t *php_pqcopy_create_object_ex(zend_class_entry *ce, php_pqcopy_t *intern); 43 | 44 | extern PHP_MINIT_FUNCTION(pqcopy); 45 | extern PHP_MSHUTDOWN_FUNCTION(pqcopy); 46 | 47 | #endif 48 | 49 | /* 50 | * Local variables: 51 | * tab-width: 4 52 | * c-basic-offset: 4 53 | * End: 54 | * vim600: noet sw=4 ts=4 fdm=marker 55 | * vim<600: noet sw=4 ts=4 56 | */ 57 | -------------------------------------------------------------------------------- /src/php_pqcur.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifndef PHP_PQCUR_H 14 | #define PHP_PQCUR_H 15 | 16 | #include "php_pqconn.h" 17 | 18 | #define PHP_PQ_DECLARE_BINARY 0x01 19 | #define PHP_PQ_DECLARE_INSENSITIVE 0x02 20 | #define PHP_PQ_DECLARE_WITH_HOLD 0x04 21 | 22 | #define PHP_PQ_DECLARE_SCROLL 0x10 23 | #define PHP_PQ_DECLARE_NO_SCROLL 0x20 24 | 25 | typedef struct php_pqcur { 26 | php_pqconn_object_t *conn; 27 | char *name; 28 | char *decl; 29 | unsigned open:1; 30 | int query_offset; 31 | long flags; 32 | } php_pqcur_t; 33 | 34 | typedef struct php_pqcur_object { 35 | PHP_PQ_OBJ_DECL(php_pqcur_t *) 36 | } php_pqcur_object_t; 37 | 38 | extern zend_class_entry *php_pqcur_class_entry; 39 | extern php_pqcur_object_t *php_pqcur_create_object_ex(zend_class_entry *ce, php_pqcur_t *intern); 40 | 41 | extern char *php_pqcur_declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len, int *query_offset); 42 | extern php_pqcur_t *php_pqcur_init(php_pqconn_object_t *conn, const char *name, char *decl, int query_offset, long flags); 43 | 44 | extern PHP_MINIT_FUNCTION(pqcur); 45 | extern PHP_MSHUTDOWN_FUNCTION(pqcur); 46 | 47 | #endif 48 | 49 | /* 50 | * Local variables: 51 | * tab-width: 4 52 | * c-basic-offset: 4 53 | * End: 54 | * vim600: noet sw=4 ts=4 fdm=marker 55 | * vim<600: noet sw=4 ts=4 56 | */ 57 | -------------------------------------------------------------------------------- /src/php_pqexc.c: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "php_pq.h" 23 | #include "php_pq_object.h" 24 | #include "php_pqexc.h" 25 | 26 | static zend_class_entry *php_pqexc_interface_class_entry; 27 | static zend_class_entry *php_pqexc_invalid_argument_class_entry; 28 | static zend_class_entry *php_pqexc_runtime_class_entry; 29 | static zend_class_entry *php_pqexc_bad_methodcall_class_entry; 30 | static zend_class_entry *php_pqexc_domain_class_entry; 31 | 32 | static zend_function_entry php_pqexc_methods[] = { 33 | {0} 34 | }; 35 | 36 | zend_class_entry *exce(php_pqexc_type_t type) 37 | { 38 | switch (type) { 39 | default: 40 | case EX_INVALID_ARGUMENT: 41 | return php_pqexc_invalid_argument_class_entry; 42 | case EX_RUNTIME: 43 | case EX_CONNECTION_FAILED: 44 | case EX_IO: 45 | case EX_ESCAPE: 46 | return php_pqexc_runtime_class_entry; 47 | case EX_UNINITIALIZED: 48 | case EX_BAD_METHODCALL: 49 | return php_pqexc_bad_methodcall_class_entry; 50 | case EX_DOMAIN: 51 | case EX_SQL: 52 | return php_pqexc_domain_class_entry; 53 | } 54 | } 55 | 56 | zend_object *throw_exce(php_pqexc_type_t type, const char *fmt, ...) 57 | { 58 | char *msg; 59 | zend_object *zexc; 60 | va_list argv; 61 | 62 | va_start(argv, fmt); 63 | vspprintf(&msg, 0, fmt, argv); 64 | va_end(argv); 65 | 66 | zexc = zend_throw_exception(exce(type), msg, type); 67 | efree(msg); 68 | 69 | return zexc; 70 | } 71 | 72 | PHP_MINIT_FUNCTION(pqexc) 73 | { 74 | zend_class_entry ce = {0}; 75 | 76 | INIT_NS_CLASS_ENTRY(ce, "pq", "Exception", php_pqexc_methods); 77 | php_pqexc_interface_class_entry = zend_register_internal_interface(&ce); 78 | zend_class_implements(php_pqexc_interface_class_entry, 1, zend_ce_throwable); 79 | 80 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("INVALID_ARGUMENT"), EX_INVALID_ARGUMENT); 81 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("RUNTIME"), EX_RUNTIME); 82 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("CONNECTION_FAILED"), EX_CONNECTION_FAILED); 83 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("IO"), EX_IO); 84 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("ESCAPE"), EX_ESCAPE); 85 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("BAD_METHODCALL"), EX_BAD_METHODCALL); 86 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("UNINITIALIZED"), EX_UNINITIALIZED); 87 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("DOMAIN"), EX_DOMAIN); 88 | zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("SQL"), EX_SQL); 89 | 90 | memset(&ce, 0, sizeof(ce)); 91 | INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "InvalidArgumentException", php_pqexc_methods); 92 | php_pqexc_invalid_argument_class_entry = zend_register_internal_class_ex(&ce, spl_ce_InvalidArgumentException); 93 | zend_class_implements(php_pqexc_invalid_argument_class_entry, 1, php_pqexc_interface_class_entry); 94 | 95 | memset(&ce, 0, sizeof(ce)); 96 | INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "RuntimeException", php_pqexc_methods); 97 | php_pqexc_runtime_class_entry = zend_register_internal_class_ex(&ce, spl_ce_RuntimeException); 98 | zend_class_implements(php_pqexc_runtime_class_entry, 1, php_pqexc_interface_class_entry); 99 | 100 | memset(&ce, 0, sizeof(ce)); 101 | INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "BadMethodCallException", php_pqexc_methods); 102 | php_pqexc_bad_methodcall_class_entry = zend_register_internal_class_ex(&ce, spl_ce_BadMethodCallException); 103 | zend_class_implements(php_pqexc_bad_methodcall_class_entry, 1, php_pqexc_interface_class_entry); 104 | 105 | memset(&ce, 0, sizeof(ce)); 106 | INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "DomainException", php_pqexc_methods); 107 | php_pqexc_domain_class_entry = zend_register_internal_class_ex(&ce, spl_ce_DomainException); 108 | zend_class_implements(php_pqexc_domain_class_entry, 1, php_pqexc_interface_class_entry); 109 | zend_declare_property_null(php_pqexc_domain_class_entry, ZEND_STRL("sqlstate"), ZEND_ACC_PUBLIC); 110 | 111 | return SUCCESS; 112 | } 113 | 114 | /* 115 | * Local variables: 116 | * tab-width: 4 117 | * c-basic-offset: 4 118 | * End: 119 | * vim600: noet sw=4 ts=4 fdm=marker 120 | * vim<600: noet sw=4 ts=4 121 | */ 122 | -------------------------------------------------------------------------------- /src/php_pqexc.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQEXC_H 15 | #define PHP_PQEXC_H 16 | 17 | typedef enum php_pqexc_type { 18 | EX_INVALID_ARGUMENT, 19 | EX_RUNTIME, 20 | EX_CONNECTION_FAILED, 21 | EX_IO, 22 | EX_ESCAPE, 23 | EX_BAD_METHODCALL, 24 | EX_UNINITIALIZED, 25 | EX_DOMAIN, 26 | EX_SQL 27 | } php_pqexc_type_t; 28 | 29 | extern zend_class_entry *exce(php_pqexc_type_t type); 30 | extern zend_object *throw_exce(php_pqexc_type_t type, const char *fmt, ...); 31 | 32 | extern PHP_MINIT_FUNCTION(pqexc); 33 | 34 | #endif 35 | 36 | /* 37 | * Local variables: 38 | * tab-width: 4 39 | * c-basic-offset: 4 40 | * End: 41 | * vim600: noet sw=4 ts=4 fdm=marker 42 | * vim<600: noet sw=4 ts=4 43 | */ 44 | -------------------------------------------------------------------------------- /src/php_pqlob.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQLOB_H 15 | #define PHP_PQLOB_H 16 | 17 | #include "php_pqtxn.h" 18 | 19 | typedef struct php_pqlob { 20 | int lofd; 21 | Oid loid; 22 | php_stream *stream; 23 | php_pqtxn_object_t *txn; 24 | } php_pqlob_t; 25 | 26 | typedef struct php_pqlob_object { 27 | PHP_PQ_OBJ_DECL(php_pqlob_t *) 28 | } php_pqlob_object_t; 29 | 30 | extern zend_class_entry *php_pqlob_class_entry; 31 | extern php_pqlob_object_t *php_pqlob_create_object_ex(zend_class_entry *ce, php_pqlob_t *intern); 32 | 33 | extern PHP_MINIT_FUNCTION(pqlob); 34 | extern PHP_MSHUTDOWN_FUNCTION(pqlob); 35 | 36 | #endif 37 | 38 | /* 39 | * Local variables: 40 | * tab-width: 4 41 | * c-basic-offset: 4 42 | * End: 43 | * vim600: noet sw=4 ts=4 fdm=marker 44 | * vim<600: noet sw=4 ts=4 45 | */ 46 | -------------------------------------------------------------------------------- /src/php_pqres.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQRES_H 15 | #define PHP_PQRES_H 16 | 17 | #include "php_pqconn.h" 18 | 19 | typedef enum php_pqres_fetch { 20 | PHP_PQRES_FETCH_ARRAY, 21 | PHP_PQRES_FETCH_ASSOC, 22 | PHP_PQRES_FETCH_OBJECT 23 | } php_pqres_fetch_t; 24 | 25 | #define PHP_PQRES_CONV_BOOL 0x0001 26 | #define PHP_PQRES_CONV_INT 0x0002 27 | #define PHP_PQRES_CONV_FLOAT 0x0004 28 | #define PHP_PQRES_CONV_BYTEA 0x0008 29 | #define PHP_PQRES_CONV_SCALAR 0x000f 30 | #define PHP_PQRES_CONV_ARRAY 0x0010 31 | #define PHP_PQRES_CONV_DATETIME 0x0020 32 | #define PHP_PQRES_CONV_JSON 0x0100 33 | #define PHP_PQRES_CONV_ALL 0xffff 34 | 35 | typedef struct php_pqres_iterator { 36 | zend_object_iterator zi; 37 | zval current_val; 38 | unsigned index; 39 | php_pqres_fetch_t fetch_type; 40 | } php_pqres_iterator_t; 41 | 42 | typedef struct php_pqres { 43 | PGresult *res; 44 | php_pqres_iterator_t *iter; 45 | HashTable bound; 46 | HashTable converters; 47 | unsigned auto_convert; 48 | php_pqres_fetch_t default_fetch_type; 49 | } php_pqres_t; 50 | 51 | typedef struct php_pqres_object { 52 | PHP_PQ_OBJ_DECL(php_pqres_t *) 53 | } php_pqres_object_t; 54 | 55 | extern ZEND_RESULT_CODE php_pqres_success(PGresult *res); 56 | extern php_pqres_object_t *php_pqres_init_instance_data(PGresult *res, php_pqconn_object_t *obj); 57 | extern zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch_type, zval *data); 58 | extern zval *php_pqres_typed_zval(php_pqres_t *res, Oid typ, zval *zv); 59 | extern php_pqres_fetch_t php_pqres_fetch_type(php_pqres_t *res); 60 | 61 | #include "php_pq_object.h" 62 | #include "php_pqconn_event.h" 63 | 64 | extern zend_class_entry *php_pqres_class_entry; 65 | extern php_pqres_object_t *php_pqres_create_object_ex(zend_class_entry *ce, php_pqres_t *intern); 66 | 67 | extern PHP_MINIT_FUNCTION(pqres); 68 | extern PHP_MSHUTDOWN_FUNCTION(pqres); 69 | 70 | #endif 71 | 72 | /* 73 | * Local variables: 74 | * tab-width: 4 75 | * c-basic-offset: 4 76 | * End: 77 | * vim600: noet sw=4 ts=4 fdm=marker 78 | * vim<600: noet sw=4 ts=4 79 | */ 80 | -------------------------------------------------------------------------------- /src/php_pqstm.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQSTM_H 15 | #define PHP_PQSTM_H 16 | 17 | #include "php_pqconn.h" 18 | 19 | typedef struct php_pqstm { 20 | php_pqconn_object_t *conn; 21 | char *name; 22 | HashTable bound; 23 | php_pq_params_t *params; 24 | char *query; 25 | unsigned allocated:1; 26 | } php_pqstm_t; 27 | 28 | typedef struct php_pqstm_object { 29 | PHP_PQ_OBJ_DECL(php_pqstm_t *) 30 | } php_pqstm_object_t; 31 | 32 | extern zend_class_entry *php_pqstm_class_entry; 33 | extern php_pqstm_object_t *php_pqstm_create_object_ex(zend_class_entry *ce, php_pqstm_t *intern); 34 | extern php_pqstm_t *php_pqstm_init(php_pqconn_object_t *conn, const char *name, const char *query, php_pq_params_t *params); 35 | 36 | extern PHP_MINIT_FUNCTION(pqstm); 37 | extern PHP_MSHUTDOWN_FUNCTION(pqstm); 38 | 39 | #endif 40 | 41 | /* 42 | * Local variables: 43 | * tab-width: 4 44 | * c-basic-offset: 4 45 | * End: 46 | * vim600: noet sw=4 ts=4 fdm=marker 47 | * vim<600: noet sw=4 ts=4 48 | */ 49 | -------------------------------------------------------------------------------- /src/php_pqtxn.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQTXN_H 15 | #define PHP_PQTXN_H 16 | 17 | #include "php_pqconn.h" 18 | 19 | typedef enum php_pqtxn_isolation { 20 | PHP_PQTXN_READ_COMMITTED, 21 | PHP_PQTXN_REPEATABLE_READ, 22 | PHP_PQTXN_SERIALIZABLE, 23 | } php_pqtxn_isolation_t; 24 | 25 | typedef struct php_pqtxn { 26 | php_pqconn_object_t *conn; 27 | php_pqtxn_isolation_t isolation; 28 | unsigned savepoint; 29 | unsigned open:1; 30 | unsigned readonly:1; 31 | unsigned deferrable:1; 32 | } php_pqtxn_t; 33 | 34 | typedef struct php_pqtxn_object { 35 | PHP_PQ_OBJ_DECL(php_pqtxn_t *) 36 | } php_pqtxn_object_t; 37 | 38 | extern const char *php_pq_isolation_level(long *isolation); 39 | 40 | extern zend_class_entry *php_pqtxn_class_entry; 41 | extern php_pqtxn_object_t *php_pqtxn_create_object_ex(zend_class_entry *ce, php_pqtxn_t *intern); 42 | 43 | extern PHP_MINIT_FUNCTION(pqtxn); 44 | extern PHP_MSHUTDOWN_FUNCTION(pqtxn); 45 | 46 | #endif 47 | 48 | /* 49 | * Local variables: 50 | * tab-width: 4 51 | * c-basic-offset: 4 52 | * End: 53 | * vim600: noet sw=4 ts=4 fdm=marker 54 | * vim<600: noet sw=4 ts=4 55 | */ 56 | -------------------------------------------------------------------------------- /src/php_pqtypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | +--------------------------------------------------------------------+ 3 | | PECL :: pq | 4 | +--------------------------------------------------------------------+ 5 | | Redistribution and use in source and binary forms, with or without | 6 | | modification, are permitted provided that the conditions mentioned | 7 | | in the accompanying LICENSE file are met. | 8 | +--------------------------------------------------------------------+ 9 | | Copyright (c) 2013, Michael Wallner | 10 | +--------------------------------------------------------------------+ 11 | */ 12 | 13 | 14 | #ifndef PHP_PQTYPES_H 15 | #define PHP_PQTYPES_H 16 | 17 | #include "php_pqconn.h" 18 | 19 | typedef struct php_pqtypes { 20 | HashTable types; 21 | php_pqconn_object_t *conn; 22 | } php_pqtypes_t; 23 | 24 | typedef struct php_pqtypes_object { 25 | PHP_PQ_OBJ_DECL(php_pqtypes_t *) 26 | } php_pqtypes_object_t; 27 | 28 | extern zend_class_entry *php_pqtypes_class_entry; 29 | extern php_pqtypes_object_t *php_pqtypes_create_object_ex(zend_class_entry *ce, php_pqtypes_t *intern); 30 | 31 | extern PHP_MINIT_FUNCTION(pqtypes); 32 | extern PHP_MSHUTDOWN_FUNCTION(pqtypes); 33 | 34 | #endif 35 | 36 | /* 37 | * Local variables: 38 | * tab-width: 4 39 | * c-basic-offset: 4 40 | * End: 41 | * vim600: noet sw=4 ts=4 fdm=marker 42 | * vim<600: noet sw=4 ts=4 43 | */ 44 | -------------------------------------------------------------------------------- /tests/_setup.inc: -------------------------------------------------------------------------------- 1 | serverVersion) > 0) { 11 | die("skip server {$c->serverVersion} is too old, needed " . SERVER_MIN); 12 | } 13 | } catch (pq\Exception $e) { 14 | die("skip could not connect to PQ_DSN ".$e->getMessage()); 15 | } 16 | -------------------------------------------------------------------------------- /tests/async001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | async connect 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | status); 13 | echo "W"; 14 | $w = array($c->socket); 15 | $r = $e = null; 16 | stream_select($r, $w, $e, null); 17 | while (true) { 18 | $s[] = $c->status; 19 | echo "P"; 20 | switch ($c->poll()) { 21 | case pq\Connection::POLLING_READING: 22 | echo "R"; 23 | $w = $e = null; 24 | $r = array($c->socket); 25 | stream_select($r, $w, $e, NULL); 26 | break; 27 | case pq\Connection::POLLING_WRITING: 28 | echo "W"; 29 | $w = array($c->socket); 30 | $r = $e = null; 31 | stream_select($r, $w, $e, null); 32 | break; 33 | case pq\Connection::POLLING_FAILED: 34 | echo "F"; 35 | break 2; 36 | case pq\Connection::POLLING_OK: 37 | echo "S"; 38 | break 2; 39 | } 40 | } 41 | $s[] = $c->status; 42 | printf("\n%s\n", implode(",", $s)); 43 | ?> 44 | DONE 45 | --EXPECTREGEX-- 46 | Test 47 | (WP(RP)*)+S 48 | [23](,\d*)*,0 49 | DONE 50 | -------------------------------------------------------------------------------- /tests/async002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | async reset 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | status); 15 | echo "W"; 16 | $w = array($c->socket); 17 | $r = $e = null; 18 | stream_select($r, $w, $e, null); 19 | while (true) { 20 | $s[] = $c->status; 21 | echo "P"; 22 | switch ($c->poll()) { 23 | case pq\Connection::POLLING_READING: 24 | echo "R"; 25 | $w = $e = null; 26 | $r = array($c->socket); 27 | stream_select($r, $w, $e, NULL); 28 | break; 29 | case pq\Connection::POLLING_WRITING: 30 | echo "W"; 31 | $w = array($c->socket); 32 | $r = $e = null; 33 | stream_select($r, $w, $e, null); 34 | break; 35 | case pq\Connection::POLLING_FAILED: 36 | echo "F"; 37 | break 2; 38 | case pq\Connection::POLLING_OK: 39 | echo "S"; 40 | break 2; 41 | } 42 | } 43 | $s[] = $c->status; 44 | printf("\n%s\n", implode(",", $s)); 45 | } 46 | 47 | complete($c); 48 | 49 | if ($c->status == pq\Connection::OK) { 50 | $c->resetAsync(); 51 | complete($c); 52 | } 53 | ?> 54 | DONE 55 | --EXPECTREGEX-- 56 | Test 57 | (WP(RP)*)+S 58 | [23](,\d*)*,0 59 | (WP(RP)*)+S 60 | [23](,\d*)*,0 61 | DONE 62 | -------------------------------------------------------------------------------- /tests/async003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | async exec 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | execAsync("SELECT 1+2+3; SELECT 2,3,4", function ($res) { 13 | var_dump($res); 14 | }); 15 | do { 16 | while ($c->busy) { 17 | $r = array($c->socket); 18 | $w = $e = null; 19 | if (stream_select($r, $w, $e, 1, 1000)) { 20 | $c->poll(); 21 | } 22 | } 23 | } while ($c->getResult()); 24 | 25 | ?> 26 | DONE 27 | --EXPECTF-- 28 | Test 29 | object(pq\Result)#%d (9) { 30 | ["status"]=> 31 | int(2) 32 | ["statusMessage"]=> 33 | string(9) "TUPLES_OK" 34 | ["errorMessage"]=> 35 | string(0) "" 36 | ["diag"]=> 37 | array(17) { 38 | ["severity"]=> 39 | NULL 40 | ["sqlstate"]=> 41 | NULL 42 | ["message_primary"]=> 43 | NULL 44 | ["message_detail"]=> 45 | NULL 46 | ["message_hint"]=> 47 | NULL 48 | ["statement_position"]=> 49 | NULL 50 | ["internal_position"]=> 51 | NULL 52 | ["internal_query"]=> 53 | NULL 54 | ["context"]=> 55 | NULL 56 | ["schema_name"]=> 57 | NULL 58 | ["table_name"]=> 59 | NULL 60 | ["column_name"]=> 61 | NULL 62 | ["datatype_name"]=> 63 | NULL 64 | ["constraint_name"]=> 65 | NULL 66 | ["source_file"]=> 67 | NULL 68 | ["source_line"]=> 69 | NULL 70 | ["source_function"]=> 71 | NULL 72 | } 73 | ["numRows"]=> 74 | int(1) 75 | ["numCols"]=> 76 | int(1) 77 | ["affectedRows"]=> 78 | int(%d) 79 | ["fetchType"]=> 80 | int(0) 81 | ["autoConvert"]=> 82 | int(65535) 83 | } 84 | object(pq\Result)#%d (9) { 85 | ["status"]=> 86 | int(2) 87 | ["statusMessage"]=> 88 | string(9) "TUPLES_OK" 89 | ["errorMessage"]=> 90 | string(0) "" 91 | ["diag"]=> 92 | array(17) { 93 | ["severity"]=> 94 | NULL 95 | ["sqlstate"]=> 96 | NULL 97 | ["message_primary"]=> 98 | NULL 99 | ["message_detail"]=> 100 | NULL 101 | ["message_hint"]=> 102 | NULL 103 | ["statement_position"]=> 104 | NULL 105 | ["internal_position"]=> 106 | NULL 107 | ["internal_query"]=> 108 | NULL 109 | ["context"]=> 110 | NULL 111 | ["schema_name"]=> 112 | NULL 113 | ["table_name"]=> 114 | NULL 115 | ["column_name"]=> 116 | NULL 117 | ["datatype_name"]=> 118 | NULL 119 | ["constraint_name"]=> 120 | NULL 121 | ["source_file"]=> 122 | NULL 123 | ["source_line"]=> 124 | NULL 125 | ["source_function"]=> 126 | NULL 127 | } 128 | ["numRows"]=> 129 | int(1) 130 | ["numCols"]=> 131 | int(3) 132 | ["affectedRows"]=> 133 | int(%d) 134 | ["fetchType"]=> 135 | int(0) 136 | ["autoConvert"]=> 137 | int(65535) 138 | } 139 | DONE 140 | -------------------------------------------------------------------------------- /tests/async004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | async exec params 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | execParamsAsync("SELECT \$1,\$2::int4", array(1,2), array($t["int4"]->oid), function ($res) { 14 | var_dump($res); 15 | }); 16 | do { 17 | while ($c->busy) { 18 | $r = array($c->socket); 19 | $w = $e = null; 20 | if (stream_select($r, $w, $e, null)) { 21 | $c->poll(); 22 | } 23 | } 24 | } while ($c->getResult()); 25 | 26 | ?> 27 | DONE 28 | --EXPECTF-- 29 | Test 30 | object(pq\Result)#%d (9) { 31 | ["status"]=> 32 | int(2) 33 | ["statusMessage"]=> 34 | string(9) "TUPLES_OK" 35 | ["errorMessage"]=> 36 | string(0) "" 37 | ["diag"]=> 38 | array(17) { 39 | ["severity"]=> 40 | NULL 41 | ["sqlstate"]=> 42 | NULL 43 | ["message_primary"]=> 44 | NULL 45 | ["message_detail"]=> 46 | NULL 47 | ["message_hint"]=> 48 | NULL 49 | ["statement_position"]=> 50 | NULL 51 | ["internal_position"]=> 52 | NULL 53 | ["internal_query"]=> 54 | NULL 55 | ["context"]=> 56 | NULL 57 | ["schema_name"]=> 58 | NULL 59 | ["table_name"]=> 60 | NULL 61 | ["column_name"]=> 62 | NULL 63 | ["datatype_name"]=> 64 | NULL 65 | ["constraint_name"]=> 66 | NULL 67 | ["source_file"]=> 68 | NULL 69 | ["source_line"]=> 70 | NULL 71 | ["source_function"]=> 72 | NULL 73 | } 74 | ["numRows"]=> 75 | int(1) 76 | ["numCols"]=> 77 | int(2) 78 | ["affectedRows"]=> 79 | int(%d) 80 | ["fetchType"]=> 81 | int(0) 82 | ["autoConvert"]=> 83 | int(65535) 84 | } 85 | DONE 86 | -------------------------------------------------------------------------------- /tests/async005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | async prepared statement 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | connection->busy) { 14 | $r = array($s->connection->socket); 15 | $w = $e = null; 16 | if (stream_select($r, $w, $e, null)) { 17 | $s->connection->poll(); 18 | } 19 | } 20 | } while ($s->connection->getResult()); 21 | } 22 | 23 | $c = new pq\Connection(PQ_DSN); 24 | $t = new pq\Types($c); 25 | $s = $c->prepareAsync("test", "SELECT \$1,\$2::int4", array($t["int4"]->oid)); 26 | 27 | complete($s); 28 | 29 | $s->execAsync(array(1,2), function ($res) { 30 | var_dump($res); 31 | }); 32 | 33 | complete($s); 34 | 35 | ?> 36 | DONE 37 | --EXPECTF-- 38 | Test 39 | object(pq\Result)#%d (9) { 40 | ["status"]=> 41 | int(2) 42 | ["statusMessage"]=> 43 | string(9) "TUPLES_OK" 44 | ["errorMessage"]=> 45 | string(0) "" 46 | ["diag"]=> 47 | array(17) { 48 | ["severity"]=> 49 | NULL 50 | ["sqlstate"]=> 51 | NULL 52 | ["message_primary"]=> 53 | NULL 54 | ["message_detail"]=> 55 | NULL 56 | ["message_hint"]=> 57 | NULL 58 | ["statement_position"]=> 59 | NULL 60 | ["internal_position"]=> 61 | NULL 62 | ["internal_query"]=> 63 | NULL 64 | ["context"]=> 65 | NULL 66 | ["schema_name"]=> 67 | NULL 68 | ["table_name"]=> 69 | NULL 70 | ["column_name"]=> 71 | NULL 72 | ["datatype_name"]=> 73 | NULL 74 | ["constraint_name"]=> 75 | NULL 76 | ["source_file"]=> 77 | NULL 78 | ["source_line"]=> 79 | NULL 80 | ["source_function"]=> 81 | NULL 82 | } 83 | ["numRows"]=> 84 | int(1) 85 | ["numCols"]=> 86 | int(2) 87 | ["affectedRows"]=> 88 | int(%d) 89 | ["fetchType"]=> 90 | int(0) 91 | ["autoConvert"]=> 92 | int(65535) 93 | } 94 | DONE 95 | -------------------------------------------------------------------------------- /tests/async006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | async unbuffered exec 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | unbuffered = true; 16 | $c->execAsync("SELECT a FROM generate_series(1,3) a", function ($res) { 17 | var_dump($res); 18 | }); 19 | do { 20 | while ($c->busy) { 21 | $r = array($c->socket); 22 | $w = $e = null; 23 | if (stream_select($r, $w, $e, null)) { 24 | $c->poll(); 25 | } 26 | } 27 | } while ($c->getResult()); 28 | 29 | ?> 30 | DONE 31 | --EXPECTF-- 32 | Test 33 | object(pq\Result)#%d (9) { 34 | ["status"]=> 35 | int(9) 36 | ["statusMessage"]=> 37 | string(12) "SINGLE_TUPLE" 38 | ["errorMessage"]=> 39 | string(0) "" 40 | ["diag"]=> 41 | array(17) { 42 | ["severity"]=> 43 | NULL 44 | ["sqlstate"]=> 45 | NULL 46 | ["message_primary"]=> 47 | NULL 48 | ["message_detail"]=> 49 | NULL 50 | ["message_hint"]=> 51 | NULL 52 | ["statement_position"]=> 53 | NULL 54 | ["internal_position"]=> 55 | NULL 56 | ["internal_query"]=> 57 | NULL 58 | ["context"]=> 59 | NULL 60 | ["schema_name"]=> 61 | NULL 62 | ["table_name"]=> 63 | NULL 64 | ["column_name"]=> 65 | NULL 66 | ["datatype_name"]=> 67 | NULL 68 | ["constraint_name"]=> 69 | NULL 70 | ["source_file"]=> 71 | NULL 72 | ["source_line"]=> 73 | NULL 74 | ["source_function"]=> 75 | NULL 76 | } 77 | ["numRows"]=> 78 | int(1) 79 | ["numCols"]=> 80 | int(1) 81 | ["affectedRows"]=> 82 | int(0) 83 | ["fetchType"]=> 84 | int(0) 85 | ["autoConvert"]=> 86 | int(65535) 87 | } 88 | object(pq\Result)#%d (9) { 89 | ["status"]=> 90 | int(9) 91 | ["statusMessage"]=> 92 | string(12) "SINGLE_TUPLE" 93 | ["errorMessage"]=> 94 | string(0) "" 95 | ["diag"]=> 96 | array(17) { 97 | ["severity"]=> 98 | NULL 99 | ["sqlstate"]=> 100 | NULL 101 | ["message_primary"]=> 102 | NULL 103 | ["message_detail"]=> 104 | NULL 105 | ["message_hint"]=> 106 | NULL 107 | ["statement_position"]=> 108 | NULL 109 | ["internal_position"]=> 110 | NULL 111 | ["internal_query"]=> 112 | NULL 113 | ["context"]=> 114 | NULL 115 | ["schema_name"]=> 116 | NULL 117 | ["table_name"]=> 118 | NULL 119 | ["column_name"]=> 120 | NULL 121 | ["datatype_name"]=> 122 | NULL 123 | ["constraint_name"]=> 124 | NULL 125 | ["source_file"]=> 126 | NULL 127 | ["source_line"]=> 128 | NULL 129 | ["source_function"]=> 130 | NULL 131 | } 132 | ["numRows"]=> 133 | int(1) 134 | ["numCols"]=> 135 | int(1) 136 | ["affectedRows"]=> 137 | int(0) 138 | ["fetchType"]=> 139 | int(0) 140 | ["autoConvert"]=> 141 | int(65535) 142 | } 143 | object(pq\Result)#%d (9) { 144 | ["status"]=> 145 | int(9) 146 | ["statusMessage"]=> 147 | string(12) "SINGLE_TUPLE" 148 | ["errorMessage"]=> 149 | string(0) "" 150 | ["diag"]=> 151 | array(17) { 152 | ["severity"]=> 153 | NULL 154 | ["sqlstate"]=> 155 | NULL 156 | ["message_primary"]=> 157 | NULL 158 | ["message_detail"]=> 159 | NULL 160 | ["message_hint"]=> 161 | NULL 162 | ["statement_position"]=> 163 | NULL 164 | ["internal_position"]=> 165 | NULL 166 | ["internal_query"]=> 167 | NULL 168 | ["context"]=> 169 | NULL 170 | ["schema_name"]=> 171 | NULL 172 | ["table_name"]=> 173 | NULL 174 | ["column_name"]=> 175 | NULL 176 | ["datatype_name"]=> 177 | NULL 178 | ["constraint_name"]=> 179 | NULL 180 | ["source_file"]=> 181 | NULL 182 | ["source_line"]=> 183 | NULL 184 | ["source_function"]=> 185 | NULL 186 | } 187 | ["numRows"]=> 188 | int(1) 189 | ["numCols"]=> 190 | int(1) 191 | ["affectedRows"]=> 192 | int(0) 193 | ["fetchType"]=> 194 | int(0) 195 | ["autoConvert"]=> 196 | int(65535) 197 | } 198 | object(pq\Result)#%d (9) { 199 | ["status"]=> 200 | int(2) 201 | ["statusMessage"]=> 202 | string(9) "TUPLES_OK" 203 | ["errorMessage"]=> 204 | string(0) "" 205 | ["diag"]=> 206 | array(17) { 207 | ["severity"]=> 208 | NULL 209 | ["sqlstate"]=> 210 | NULL 211 | ["message_primary"]=> 212 | NULL 213 | ["message_detail"]=> 214 | NULL 215 | ["message_hint"]=> 216 | NULL 217 | ["statement_position"]=> 218 | NULL 219 | ["internal_position"]=> 220 | NULL 221 | ["internal_query"]=> 222 | NULL 223 | ["context"]=> 224 | NULL 225 | ["schema_name"]=> 226 | NULL 227 | ["table_name"]=> 228 | NULL 229 | ["column_name"]=> 230 | NULL 231 | ["datatype_name"]=> 232 | NULL 233 | ["constraint_name"]=> 234 | NULL 235 | ["source_file"]=> 236 | NULL 237 | ["source_line"]=> 238 | NULL 239 | ["source_function"]=> 240 | NULL 241 | } 242 | ["numRows"]=> 243 | int(0) 244 | ["numCols"]=> 245 | int(1) 246 | ["affectedRows"]=> 247 | int(3) 248 | ["fetchType"]=> 249 | int(0) 250 | ["autoConvert"]=> 251 | int(65535) 252 | } 253 | DONE 254 | -------------------------------------------------------------------------------- /tests/async007.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | async statement 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | busy) { 13 | $r = array($c->socket); 14 | $w = $e = null; 15 | if (stream_select($r, $w, $e, null)) { 16 | $c->poll(); 17 | } 18 | } 19 | } while ($c->getResult()); 20 | } 21 | 22 | $c = new pq\Connection(PQ_DSN); 23 | $t = new pq\Types($c); 24 | $s = new pq\Statement($c, "test1", "SELECT NOW() - \$1", null, true); 25 | complete($s->connection); 26 | 27 | $s->execAsync(array("2012-12-12 12:12:12")); 28 | complete($s->connection); 29 | 30 | $s->descAsync(function($r) use ($t) { 31 | list($typeOid) = $r->desc(); 32 | printf("%s\n", $t[$typeOid]->typname); 33 | }); 34 | complete($s->connection); 35 | 36 | ?> 37 | DONE 38 | --EXPECT-- 39 | Test 40 | timestamptz 41 | DONE 42 | -------------------------------------------------------------------------------- /tests/async008.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | async cursor 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | busy) { 13 | $r = array($c->socket); 14 | $w = $e = null; 15 | if (stream_select($r, $w, $e, null)) { 16 | $c->poll(); 17 | } 18 | } 19 | } while ($c->getResult()); 20 | } 21 | 22 | $c = new pq\Connection(PQ_DSN); 23 | $p = $c->declareAsync("mycursor", pq\Cursor::WITH_HOLD, 24 | "SELECT * FROM generate_series(0,29) s WHERE (s%2)=0"); 25 | complete($c); 26 | 27 | do { 28 | $p->fetchAsync(2, function ($r) { 29 | foreach ($r as $row) { 30 | foreach ($row as $col) { 31 | echo " $col"; 32 | } 33 | echo "\n"; 34 | } 35 | }); 36 | complete($p->connection); 37 | $p->moveAsync(1, function ($r) use(&$keep_going) { 38 | $keep_going = $r->affectedRows; 39 | }); 40 | complete($p->connection); 41 | } while ($keep_going); 42 | 43 | ?> 44 | ===DONE=== 45 | --EXPECT-- 46 | Test 47 | 0 48 | 2 49 | 6 50 | 8 51 | 12 52 | 14 53 | 18 54 | 20 55 | 24 56 | 26 57 | ===DONE=== 58 | -------------------------------------------------------------------------------- /tests/async009.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Deallocate and prepare statement async 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | busy) { 13 | $r = array($c->socket); 14 | $w = $e = null; 15 | if (stream_select($r, $w, $e, null)) { 16 | $c->poll(); 17 | } 18 | } 19 | } while ($c->getResult()); 20 | } 21 | 22 | $c = new pq\Connection(PQ_DSN); 23 | $s = $c->prepareAsync("test1", "SELECT 'test' || \$1"); 24 | complete($c); 25 | 26 | $r = $s->exec(array("ing")); 27 | $r->fetchCol($d); 28 | var_dump($d); 29 | 30 | $s->deallocateAsync(); 31 | complete($c); 32 | 33 | try { 34 | $s->exec(array("ing")); 35 | } catch (pq\Exception\BadMethodCallException $e) { 36 | echo "Caught exception\n"; 37 | } 38 | 39 | $s->prepareAsync(); 40 | complete($c); 41 | 42 | $r = $s->exec(array("ing")); 43 | $r->fetchCol($d); 44 | var_dump($d); 45 | 46 | ?> 47 | DONE 48 | --EXPECT-- 49 | Test 50 | string(7) "testing" 51 | Caught exception 52 | string(7) "testing" 53 | DONE 54 | -------------------------------------------------------------------------------- /tests/async010.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | asnyc query not cleaned before sync exec 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | $c->execAsync("select clock_timestamp(), pg_sleep(0.1), clock_timestamp()", function($r) { 14 | var_dump([ 15 | "cb" => $r->fetchRow() 16 | ]); 17 | }) 18 | ]); 19 | 20 | var_dump([ 21 | "execParams" => $c->execParams("select \$1::int4", [123])->fetchRow() 22 | ]); 23 | ?> 24 | DONE 25 | --EXPECTF-- 26 | Test 27 | array(1) { 28 | ["async"]=> 29 | NULL 30 | } 31 | array(1) { 32 | ["cb"]=> 33 | array(3) { 34 | [0]=> 35 | object(pq\DateTime)#%d (4) { 36 | ["format"]=> 37 | string(14) "Y-m-d H:i:s.uO" 38 | ["date"]=> 39 | string(26) "%s" 40 | ["timezone_type"]=> 41 | int(1) 42 | ["timezone"]=> 43 | string(6) "%s" 44 | } 45 | [1]=> 46 | string(0) "" 47 | [2]=> 48 | object(pq\DateTime)#%d (4) { 49 | ["format"]=> 50 | string(14) "Y-m-d H:i:s.uO" 51 | ["date"]=> 52 | string(26) "%s" 53 | ["timezone_type"]=> 54 | int(1) 55 | ["timezone"]=> 56 | string(6) "%s" 57 | } 58 | } 59 | } 60 | array(1) { 61 | ["execParams"]=> 62 | array(1) { 63 | [0]=> 64 | int(123) 65 | } 66 | } 67 | DONE 68 | -------------------------------------------------------------------------------- /tests/basic001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | basic functionality 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exec("SELECT 1 as one, 2 as two from generate_series(1,2)"); 13 | 14 | var_dump($res->status == pq\Result::TUPLES_OK); 15 | var_dump($res->numRows); 16 | var_dump($res->numCols); 17 | var_dump(count($res) == $res->count(), $res->numRows == count($res)); 18 | 19 | foreach ($res as $rowNum => $rowData) { 20 | printf("%d.0 => %d\n", $rowNum, $rowData[0]); 21 | printf("%d.1 => %d\n", $rowNum, $rowData[1]); 22 | } 23 | $res->fetchType = pq\Result::FETCH_ASSOC; 24 | foreach ($res as $rowNum => $rowData) { 25 | printf("%d.0 => %d\n", $rowNum, $rowData["one"]); 26 | printf("%d.1 => %d\n", $rowNum, $rowData["two"]); 27 | } 28 | $res->fetchType = pq\Result::FETCH_OBJECT; 29 | foreach ($res as $rowNum => $rowData) { 30 | printf("%d.0 => %d\n", $rowNum, $rowData->one); 31 | printf("%d.1 => %d\n", $rowNum, $rowData->two); 32 | } 33 | ?> 34 | DONE 35 | --EXPECT-- 36 | Test 37 | bool(true) 38 | int(2) 39 | int(2) 40 | bool(true) 41 | bool(true) 42 | 0.0 => 1 43 | 0.1 => 2 44 | 1.0 => 1 45 | 1.1 => 2 46 | 0.0 => 1 47 | 0.1 => 2 48 | 1.0 => 1 49 | 1.1 => 2 50 | 0.0 => 1 51 | 0.1 => 2 52 | 1.0 => 1 53 | 1.1 => 2 54 | DONE 55 | -------------------------------------------------------------------------------- /tests/basic002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | basic functionality 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | prepare("test1", "SELECT \$1",array($t["text"]->oid)); 13 | $r = $s->exec(array("fooo")); 14 | 15 | printf("%s\n", $r->errorMessage); 16 | var_dump($r->fetchCol($val)); 17 | var_dump($val); 18 | ?> 19 | DONE 20 | --EXPECT-- 21 | Test 22 | 23 | bool(true) 24 | string(4) "fooo" 25 | DONE 26 | -------------------------------------------------------------------------------- /tests/basic003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | basic functionality 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | libraryVersion); 13 | var_dump($c->protocolVersion); 14 | var_dump($c->serverVersion); 15 | ?> 16 | DONE 17 | --EXPECTF-- 18 | Test 19 | string(%d) "%s" 20 | int(%d) 21 | string(%d) "%s" 22 | DONE 23 | -------------------------------------------------------------------------------- /tests/bound002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | fetch bound 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | exec("select 1*a,2*a,3*a from generate_series(2,3) a"); 15 | $r->bind(0, $a); 16 | $r->bind(1, $b); 17 | $r->bind(2, $c); 18 | while ($s = $r->fetchBound()) { 19 | var_dump($s,$a,$b,$c); 20 | } 21 | ?> 22 | DONE 23 | --EXPECT-- 24 | Test 25 | array(3) { 26 | [0]=> 27 | int(2) 28 | [1]=> 29 | int(4) 30 | [2]=> 31 | int(6) 32 | } 33 | int(2) 34 | int(4) 35 | int(6) 36 | array(3) { 37 | [0]=> 38 | int(3) 39 | [1]=> 40 | int(6) 41 | [2]=> 42 | int(9) 43 | } 44 | int(3) 45 | int(6) 46 | int(9) 47 | DONE 48 | -------------------------------------------------------------------------------- /tests/callback001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | callback sanity 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | execAsync("select 1; select 2", function($r) { 13 | print_r($r->fetchAll()); 14 | }); 15 | $c->exec("select 3"); 16 | 17 | ?> 18 | ===DONE=== 19 | --EXPECT-- 20 | Test 21 | Array 22 | ( 23 | [0] => Array 24 | ( 25 | [0] => 1 26 | ) 27 | 28 | ) 29 | Array 30 | ( 31 | [0] => Array 32 | ( 33 | [0] => 2 34 | ) 35 | 36 | ) 37 | ===DONE=== 38 | -------------------------------------------------------------------------------- /tests/callback002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | callback sanity 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | execAsync("select 1; select 2", function($r) { 13 | print_r($r->fetchAll()); 14 | }); 15 | try { 16 | $c->execAsync("select 3; select 4", function($r) { 17 | 18 | }); 19 | } catch (Exception $e) { 20 | printf("%s\n", $e->getMessage()); 21 | } 22 | $c->exec(""); 23 | ?> 24 | ===DONE=== 25 | --EXPECT-- 26 | Test 27 | Failed to execute query (another command is already in progress) 28 | Array 29 | ( 30 | [0] => Array 31 | ( 32 | [0] => 1 33 | ) 34 | 35 | ) 36 | Array 37 | ( 38 | [0] => Array 39 | ( 40 | [0] => 2 41 | ) 42 | 43 | ) 44 | ===DONE=== 45 | -------------------------------------------------------------------------------- /tests/callback003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | callback sanity 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | execAsync("select 1; select 2", function($r) use($c) { 13 | echo "CALLBACK 1\n"; 14 | print_r($r->fetchAll()); 15 | $c->exec("select 'bug'"); 16 | try { 17 | $c->execAsync("select 3; select 4", function($r) { 18 | echo "CALLBACK 2\n"; 19 | print_r($r->fetchAll()); 20 | }); 21 | } catch (Exception $e) { 22 | printf("%s\n", $e->getMessage()); 23 | } 24 | }); 25 | $c->exec("select 'end'"); 26 | ?> 27 | ===DONE=== 28 | --EXPECT-- 29 | Test 30 | CALLBACK 1 31 | Array 32 | ( 33 | [0] => Array 34 | ( 35 | [0] => 1 36 | ) 37 | 38 | ) 39 | CALLBACK 1 40 | Array 41 | ( 42 | [0] => Array 43 | ( 44 | [0] => 2 45 | ) 46 | 47 | ) 48 | CALLBACK 2 49 | Array 50 | ( 51 | [0] => Array 52 | ( 53 | [0] => 3 54 | ) 55 | 56 | ) 57 | CALLBACK 2 58 | Array 59 | ( 60 | [0] => Array 61 | ( 62 | [0] => 4 63 | ) 64 | 65 | ) 66 | CALLBACK 2 67 | Array 68 | ( 69 | [0] => Array 70 | ( 71 | [0] => 3 72 | ) 73 | 74 | ) 75 | CALLBACK 2 76 | Array 77 | ( 78 | [0] => Array 79 | ( 80 | [0] => 4 81 | ) 82 | 83 | ) 84 | ===DONE=== -------------------------------------------------------------------------------- /tests/cancel001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | cancel 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exec("SET lc_messages TO 'C'"); 14 | } catch (pq\Exception $e) { 15 | // no not fail if we are not superuser 16 | } 17 | 18 | $x = new pq\Cancel($c); 19 | 20 | $c->execAsync("SELECT pg_sleep(10)"); 21 | 22 | $x->cancel(); 23 | 24 | var_dump($c === $x->connection); 25 | var_dump($c->getResult()); 26 | printf("%s\n", $c->errorMessage); 27 | ?> 28 | DONE 29 | --EXPECTF-- 30 | Test 31 | bool(true) 32 | object(pq\Result)#%d (9) { 33 | ["status"]=> 34 | int(7) 35 | ["statusMessage"]=> 36 | string(11) "FATAL_ERROR" 37 | ["errorMessage"]=> 38 | string(47) "ERROR: canceling statement due to user request" 39 | ["diag"]=> 40 | array(17) { 41 | ["severity"]=> 42 | string(5) "ERROR" 43 | ["sqlstate"]=> 44 | string(5) "57014" 45 | ["message_primary"]=> 46 | string(39) "canceling statement due to user request" 47 | ["message_detail"]=> 48 | NULL 49 | ["message_hint"]=> 50 | NULL 51 | ["statement_position"]=> 52 | NULL 53 | ["internal_position"]=> 54 | NULL 55 | ["internal_query"]=> 56 | NULL 57 | ["context"]=> 58 | NULL 59 | ["schema_name"]=> 60 | NULL 61 | ["table_name"]=> 62 | NULL 63 | ["column_name"]=> 64 | NULL 65 | ["datatype_name"]=> 66 | NULL 67 | ["constraint_name"]=> 68 | NULL 69 | ["source_file"]=> 70 | string(10) "postgres.c" 71 | ["source_line"]=> 72 | string(4) "%d" 73 | ["source_function"]=> 74 | string(17) "ProcessInterrupts" 75 | } 76 | ["numRows"]=> 77 | int(0) 78 | ["numCols"]=> 79 | int(0) 80 | ["affectedRows"]=> 81 | int(0) 82 | ["fetchType"]=> 83 | int(0) 84 | ["autoConvert"]=> 85 | int(65535) 86 | } 87 | ERROR: canceling statement due to user request 88 | DONE 89 | -------------------------------------------------------------------------------- /tests/conv001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | converter 3 | --SKIPIF-- 4 | 8 | --INI-- 9 | date.timezone=UTC 10 | --FILE-- 11 | types = $types; 22 | } 23 | } 24 | 25 | class HStoreConverter extends Converter 26 | { 27 | function convertTypes() { 28 | return [ $this->types["hstore"]->oid ]; 29 | } 30 | 31 | function convertFromString($string, $type) { 32 | return eval("return [$string];"); 33 | } 34 | 35 | function convertToString($data, $type) { 36 | $string = ""; 37 | foreach ($data as $k => $v) { 38 | if (isset($v)) { 39 | $string .= sprintf("\"%s\"=>\"%s\",", addslashes($k), addslashes($v)); 40 | } else { 41 | $string .= sprintf("\"%s\"=>NULL,", addslashes($k)); 42 | } 43 | } 44 | return $string; 45 | } 46 | } 47 | 48 | class IntVectorConverter extends Converter 49 | { 50 | function convertTypes() { 51 | return [ 52 | $this->types["int2vector"]->oid, 53 | $this->types["oidvector"]->oid 54 | ]; 55 | } 56 | 57 | function convertFromString($string, $type) { 58 | return array_map("intval", explode(" ", $string)); 59 | } 60 | 61 | function convertToString($data, $type) { 62 | return implode(" ", $data); 63 | } 64 | } 65 | 66 | class JSONConverter extends Converter 67 | { 68 | function convertTypes() { 69 | return [ $this->types["json"]->oid ]; 70 | } 71 | 72 | function convertFromString($string, $type) { 73 | return json_decode($string, true); 74 | } 75 | 76 | function convertToString($data, $type) { 77 | return json_encode($data); 78 | } 79 | } 80 | 81 | class Point { 82 | public $x; 83 | public $y; 84 | function __construct($x, $y) { 85 | $this->x = $x; 86 | $this->y = $y; 87 | } 88 | } 89 | 90 | class Box { 91 | public $p1; 92 | public $p2; 93 | function __construct(Point $p1, Point $p2) { 94 | $this->p1 = $p1; 95 | $this->p2 = $p2; 96 | } 97 | } 98 | 99 | class BoxConverter extends Converter 100 | { 101 | function convertTypes() { 102 | return [ $this->types["box"]->oid ]; 103 | } 104 | 105 | function convertToString($box, $type) { 106 | return sprintf("(%F,%F),(%F,%F)", 107 | $box->p1->x, $box->p1->y, 108 | $box->p2->x, $box->p2->y 109 | ); 110 | } 111 | 112 | function convertFromString($data, $type) { 113 | list($p1x, $p1y, $p2x, $p2y) = sscanf($data, "(%f,%f),(%f,%f)"); 114 | return new Box(new Point($p1x, $p1y), new Point($p2x, $p2y)); 115 | } 116 | } 117 | 118 | class Text { 119 | private $data; 120 | function __construct($data) { 121 | $this->data = $data; 122 | } 123 | function __toString() { 124 | return (string) $this->data; 125 | } 126 | } 127 | 128 | $c = new pq\Connection(PQ_DSN); 129 | $c->exec("CREATE EXTENSION IF NOT EXISTS hstore"); 130 | $t = new pq\Types($c); 131 | 132 | $c->setConverter(new HStoreConverter($t)); 133 | $c->setConverter(new IntVectorConverter($t)); 134 | if (!(defined("pq\\Types::JSON") && defined("pq\\Result::CONV_JSON"))) { 135 | $c->setConverter(new JSONConverter($t)); 136 | } 137 | $c->setConverter(new BoxConverter($t)); 138 | 139 | $r = $c->execParams("SELECT \$1 as hs, \$2 as iv, \$3 as oids, \$4 as js, \$5 as ia, \$6 as ta, \$7 as ba, \$8 as da, \$9 as dbl, \$10 as bln, ". 140 | "\$11 as dt1, \$12 as dt3, \$13 as dt4, \$14 as dt5, \$15 as dt7, \$16 as dt8, \$17 as txta, \$18 as boxa", 141 | array( 142 | // hstore 143 | array( 144 | "k1" => "v1", 145 | "k2" => "v2", 146 | "k3" => null 147 | ), 148 | // vectors 149 | array( 150 | 1, 3, 5, 7, 9, 11 151 | ), 152 | array( 153 | 2345124, 1431341, 1343423 154 | ), 155 | // JSON 156 | (object) array( 157 | "int" => 123, 158 | "obj" => (object) array( 159 | "a" => 1, 160 | "b" => 2, 161 | "c" => 3, 162 | ), 163 | "str" => "äüö" 164 | ), 165 | // arrays 166 | array(array(array(1,2,3))), 167 | array(array("a\"","b}",null)), 168 | array(true,false), 169 | array(1.1,2.2), 170 | // double 171 | 123.456, 172 | // bool 173 | true, 174 | // datetimes 175 | new pq\Datetime, 176 | new pq\Datetime, 177 | new pq\Datetime, 178 | new pq\Datetime, 179 | new pq\Datetime, 180 | new pq\Datetime, 181 | // text array 182 | [new Text(0), new Text(" or "), new Text(true)], 183 | // box array 184 | [new Box(new Point(1,2), new Point(2,3)), new Box(new Point(3,4), new Point(4,5))], 185 | ), 186 | array( 187 | $t["hstore"]->oid, 188 | $t["int2vector"]->oid, 189 | $t["oidvector"]->oid, 190 | $t["json"]->oid, 191 | $t["_int4"]->oid, 192 | $t["_text"]->oid, 193 | $t["_bool"]->oid, 194 | $t["_float8"]->oid, 195 | $t["float4"]->oid, 196 | $t["bool"]->oid, 197 | $t["date"]->oid, 198 | $t["timestamp"]->oid, 199 | $t["timestamptz"]->oid, 200 | $t["date"]->oid, 201 | $t["timestamp"]->oid, 202 | $t["timestamptz"]->oid, 203 | $t["_text"]->oid, 204 | $t["_box"]->oid 205 | ) 206 | ); 207 | 208 | var_dump($r->fetchAll()); 209 | 210 | ?> 211 | Done 212 | --EXPECTF-- 213 | Test 214 | array(1) { 215 | [0]=> 216 | array(%d) { 217 | [0]=> 218 | array(3) { 219 | ["k1"]=> 220 | string(2) "v1" 221 | ["k2"]=> 222 | string(2) "v2" 223 | ["k3"]=> 224 | NULL 225 | } 226 | [1]=> 227 | array(6) { 228 | [0]=> 229 | int(1) 230 | [1]=> 231 | int(3) 232 | [2]=> 233 | int(5) 234 | [3]=> 235 | int(7) 236 | [4]=> 237 | int(9) 238 | [5]=> 239 | int(11) 240 | } 241 | [2]=> 242 | array(3) { 243 | [0]=> 244 | int(2345124) 245 | [1]=> 246 | int(1431341) 247 | [2]=> 248 | int(1343423) 249 | } 250 | [3]=> 251 | array(3) { 252 | ["int"]=> 253 | int(123) 254 | ["obj"]=> 255 | array(3) { 256 | ["a"]=> 257 | int(1) 258 | ["b"]=> 259 | int(2) 260 | ["c"]=> 261 | int(3) 262 | } 263 | ["str"]=> 264 | string(6) "äüö" 265 | } 266 | [4]=> 267 | array(1) { 268 | [0]=> 269 | array(1) { 270 | [0]=> 271 | array(3) { 272 | [0]=> 273 | int(1) 274 | [1]=> 275 | int(2) 276 | [2]=> 277 | int(3) 278 | } 279 | } 280 | } 281 | [5]=> 282 | array(1) { 283 | [0]=> 284 | array(3) { 285 | [0]=> 286 | string(2) "a"" 287 | [1]=> 288 | string(2) "b}" 289 | [2]=> 290 | NULL 291 | } 292 | } 293 | [6]=> 294 | array(2) { 295 | [0]=> 296 | bool(true) 297 | [1]=> 298 | bool(false) 299 | } 300 | [7]=> 301 | array(2) { 302 | [0]=> 303 | float(1.1) 304 | [1]=> 305 | float(2.2) 306 | } 307 | [8]=> 308 | float(123.456) 309 | [9]=> 310 | bool(true) 311 | [10]=> 312 | object(pq\DateTime)#%d (4) { 313 | ["format"]=> 314 | string(5) "Y-m-d" 315 | ["date"]=> 316 | string(26) "%d-%d-%d 00:00:00.000000" 317 | ["timezone_type"]=> 318 | int(3) 319 | ["timezone"]=> 320 | string(3) "UTC" 321 | } 322 | [11]=> 323 | object(pq\DateTime)#%d (4) { 324 | ["format"]=> 325 | string(13) "Y-m-d H:i:s.u" 326 | ["date"]=> 327 | string(26) "%d-%d-%d %d:%d:%d.%d" 328 | ["timezone_type"]=> 329 | int(3) 330 | ["timezone"]=> 331 | string(3) "UTC" 332 | } 333 | [12]=> 334 | object(pq\DateTime)#%d (4) { 335 | ["format"]=> 336 | string(14) "Y-m-d H:i:s.uO" 337 | ["date"]=> 338 | string(26) "%d-%d-%d %d:%d:%d.%d" 339 | ["timezone_type"]=> 340 | int(1) 341 | ["timezone"]=> 342 | string(%d) "%s" 343 | } 344 | [13]=> 345 | object(pq\DateTime)#%d (4) { 346 | ["format"]=> 347 | string(5) "Y-m-d" 348 | ["date"]=> 349 | string(26) "%d-%d-%d 00:00:00.000000" 350 | ["timezone_type"]=> 351 | int(3) 352 | ["timezone"]=> 353 | string(3) "UTC" 354 | } 355 | [14]=> 356 | object(pq\DateTime)#%d (4) { 357 | ["format"]=> 358 | string(13) "Y-m-d H:i:s.u" 359 | ["date"]=> 360 | string(26) "%d-%d-%d %d:%d:%d.%d" 361 | ["timezone_type"]=> 362 | int(3) 363 | ["timezone"]=> 364 | string(3) "UTC" 365 | } 366 | [15]=> 367 | object(pq\DateTime)#%d (4) { 368 | ["format"]=> 369 | string(14) "Y-m-d H:i:s.uO" 370 | ["date"]=> 371 | string(26) "%d-%d-%d %d:%d:%d.%d" 372 | ["timezone_type"]=> 373 | int(1) 374 | ["timezone"]=> 375 | string(%d) "%s" 376 | } 377 | [16]=> 378 | array(3) { 379 | [0]=> 380 | string(1) "0" 381 | [1]=> 382 | string(4) " or " 383 | [2]=> 384 | string(1) "1" 385 | } 386 | [17]=> 387 | array(2) { 388 | [0]=> 389 | object(Box)#%d (2) { 390 | ["p1"]=> 391 | object(Point)#%d (2) { 392 | ["x"]=> 393 | float(2) 394 | ["y"]=> 395 | float(3) 396 | } 397 | ["p2"]=> 398 | object(Point)#%d (2) { 399 | ["x"]=> 400 | float(1) 401 | ["y"]=> 402 | float(2) 403 | } 404 | } 405 | [1]=> 406 | object(Box)#%d (2) { 407 | ["p1"]=> 408 | object(Point)#%d (2) { 409 | ["x"]=> 410 | float(4) 411 | ["y"]=> 412 | float(5) 413 | } 414 | ["p2"]=> 415 | object(Point)#%d (2) { 416 | ["x"]=> 417 | float(3) 418 | ["y"]=> 419 | float(4) 420 | } 421 | } 422 | } 423 | } 424 | } 425 | Done 426 | -------------------------------------------------------------------------------- /tests/copy001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | copy 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exec("DROP TABLE IF EXISTS copy_test; CREATE TABLE copy_test (id serial, line text);"); 13 | 14 | $file = file(__FILE__); 15 | 16 | $in = new pq\COPY($c, "copy_test (line)", pq\COPY::FROM_STDIN, "DELIMITER '\t'"); 17 | 18 | var_dump( 19 | $c === $in->connection, 20 | "copy_test (line)" === $in->expression, 21 | pq\COPY::FROM_STDIN === $in->direction, 22 | "DELIMITER '\t'" === $in->options 23 | ); 24 | 25 | foreach ($file as $i => $line) { 26 | $in->put(addcslashes($line, "\\\t")); 27 | } 28 | $in->end(); 29 | 30 | $out = new pq\COPY($c, "copy_test (line)", pq\COPY::TO_STDOUT, "DELIMITER '\t'"); 31 | 32 | var_dump( 33 | $c === $out->connection, 34 | "copy_test (line)" === $out->expression, 35 | pq\COPY::TO_STDOUT === $out->direction, 36 | "DELIMITER '\t'" === $out->options 37 | ); 38 | 39 | while ($out->get($line)) { 40 | $lines[] = stripcslashes($line); 41 | } 42 | 43 | var_dump($file == $lines); 44 | 45 | if ($file != $lines) { 46 | foreach (array_keys(array_diff($file, $lines)) as $idx) { 47 | var_dump($idx, $file[$idx], $lines[$idx], "##############"); 48 | } 49 | } 50 | 51 | $c->exec("DROP TABLE copy_test"); 52 | 53 | ?> 54 | DONE 55 | --EXPECT-- 56 | Test 57 | bool(true) 58 | bool(true) 59 | bool(true) 60 | bool(true) 61 | bool(true) 62 | bool(true) 63 | bool(true) 64 | bool(true) 65 | bool(true) 66 | DONE 67 | 68 | -------------------------------------------------------------------------------- /tests/crash_cur_reverse_dep.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | crash txn reverse dependency from connection 3 | --SKIPIF-- 4 | =")) 7 | echo "skip PHP_VERSION>=8.2 (dynamic properties deprecated)\n"; 8 | ?> 9 | --FILE-- 10 | t = $c->startTransaction(); 17 | 18 | ?> 19 | ===DONE=== 20 | --EXPECT-- 21 | Test 22 | ===DONE=== 23 | -------------------------------------------------------------------------------- /tests/crash_result_iterator.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | crash result iterator 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | exec($sql) as $row) { 23 | var_dump($row); 24 | } 25 | ?> 26 | ===DONE=== 27 | --EXPECT-- 28 | Test 29 | array(1) { 30 | [0]=> 31 | int(1) 32 | } 33 | array(1) { 34 | [0]=> 35 | int(2) 36 | } 37 | array(1) { 38 | [0]=> 39 | int(3) 40 | } 41 | ===DONE=== 42 | -------------------------------------------------------------------------------- /tests/crash_stm_reverse_dep.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | crash stm reverse dependency from connection 3 | --SKIPIF-- 4 | =")) 7 | echo "skip PHP_VERSION>=8.2 (dynamic properties deprecated)\n"; 8 | ?> 9 | --FILE-- 10 | s = $c->prepare("test", "SELECT 1"); 17 | 18 | ?> 19 | ===DONE=== 20 | --EXPECT-- 21 | Test 22 | ===DONE=== 23 | -------------------------------------------------------------------------------- /tests/crash_txn_reverse_dep.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | crash txn reverse dependency from connection 3 | --SKIPIF-- 4 | =")) 7 | echo "skip PHP_VERSION>=8.2 (dynamic properties deprecated)\n"; 8 | ?> 9 | --FILE-- 10 | c = $c->declare("test", pq\Cursor::WITH_HOLD, "SELECT 1"); 17 | 18 | ?> 19 | ===DONE=== 20 | --EXPECT-- 21 | Test 22 | ===DONE=== 23 | -------------------------------------------------------------------------------- /tests/crash_unbuffered_async_prepare.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | crash unbuffered async prepare 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | busy) { 16 | $r = array($c->socket); 17 | $w = $e = null; 18 | if (stream_select($r, $w, $e, null)) { 19 | $c->poll(); 20 | } 21 | } 22 | } while ($c->getResult()); 23 | } 24 | 25 | try { 26 | $c = new pq\Connection(PQ_DSN); 27 | $c->unbuffered = true; 28 | 29 | $s = $c->prepareAsync("test", "SELECT * from generate_series(1,2)"); 30 | complete($c); 31 | 32 | $r = $s->execAsync(); 33 | complete($c); 34 | } catch (Exception $e) { 35 | echo $e; 36 | } 37 | unset($c); 38 | 39 | ?> 40 | ===DONE=== 41 | --EXPECT-- 42 | Test 43 | ===DONE=== -------------------------------------------------------------------------------- /tests/cursor001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | cursor 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | declare("mycursor", pq\Cursor::WITH_HOLD, 13 | "SELECT * FROM generate_series(0,29) s WHERE (s%2)=0"); 14 | for ($r = $p->fetch(2); $r->numRows; $p->move(1), $r = $p->fetch(2)) { 15 | foreach ($r as $row) { 16 | foreach ($row as $col) { 17 | echo " $col"; 18 | } 19 | echo "\n"; 20 | } 21 | } 22 | try { 23 | $p = new pq\Cursor($c, "mycursor", pq\Cursor::WITH_HOLD, 24 | "SELECT * FROM generate_series(0,29) s WHERE (s%2)=0"); 25 | } catch (Exception $ex) { 26 | $p->close(); 27 | } 28 | $p = new pq\Cursor($c, "mycursor", pq\Cursor::WITH_HOLD, 29 | "SELECT * FROM generate_series(0,29) s WHERE (s%2)=0"); 30 | for ($r = $p->fetch(2); $r->numRows; $p->move(1), $r = $p->fetch(2)) { 31 | foreach ($r as $row) { 32 | foreach ($row as $col) { 33 | echo " $col"; 34 | } 35 | echo "\n"; 36 | } 37 | } 38 | ?> 39 | ===DONE=== 40 | --EXPECT-- 41 | Test 42 | 0 43 | 2 44 | 6 45 | 8 46 | 12 47 | 14 48 | 18 49 | 20 50 | 24 51 | 26 52 | 0 53 | 2 54 | 6 55 | 8 56 | 12 57 | 14 58 | 18 59 | 20 60 | 24 61 | 26 62 | ===DONE=== 63 | -------------------------------------------------------------------------------- /tests/encoding001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | encoding 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | encoding); 12 | $c->encoding = "utf8"; 13 | var_dump($c->encoding); 14 | $c->exec("SELECT 'ßüpä…'")->fetchCol($val); 15 | var_dump($val); 16 | $tmp = 12345; 17 | $c->encoding = $tmp; 18 | var_dump($c->encoding); 19 | ?> 20 | DONE 21 | --EXPECTF-- 22 | Test 23 | string(%d) "%s" 24 | string(4) "UTF8" 25 | string(10) "ßüpä…" 26 | 27 | Notice: Unrecognized encoding '12345' in %s on line %d 28 | string(4) "UTF8" 29 | DONE 30 | -------------------------------------------------------------------------------- /tests/exceptions001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | exceptions 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | getCode() == pq\Exception::INVALID_ARGUMENT, $e->getCode()."!=".pq\Exception::INVALID_ARGUMENT); 18 | } 19 | 20 | class c extends pq\Connection { 21 | function __construct() { 22 | } 23 | function open($dsn) { 24 | parent::__construct($dsn); 25 | } 26 | } 27 | $c = new c; 28 | try { 29 | $c->reset(); 30 | foo(); 31 | } catch (pq\Exception\BadMethodCallException $e) { 32 | assert($e->getCode() == pq\Exception::UNINITIALIZED, $e->getCode()."!=".pq\Exception::UNINITIALIZED); 33 | } 34 | 35 | $c->open(PQ_DSN); 36 | try { 37 | $c->open(PQ_DSN); 38 | foo(); 39 | } catch (pq\Exception\BadMethodCallException $e) { 40 | assert($e->getCode() == pq\Exception::BAD_METHODCALL, $e->getCode()."!=".pq\Exception::BAD_METHODCALL); 41 | } 42 | 43 | ?> 44 | DONE 45 | --EXPECT-- 46 | Test 47 | DONE 48 | -------------------------------------------------------------------------------- /tests/exceptions002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | sql exception 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exec("SELECT 1 FROM probably_non_existent_table"); 14 | } catch (pq\Exception $e) { 15 | var_dump($e instanceof pq\Exception\DomainException); 16 | var_dump($e->getCode() == pq\Exception::SQL); 17 | var_dump($e->sqlstate); 18 | } 19 | ?> 20 | DONE 21 | --EXPECT-- 22 | Test 23 | bool(true) 24 | bool(true) 25 | string(5) "42P01" 26 | DONE 27 | -------------------------------------------------------------------------------- /tests/fetch001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | fetch type 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exec("SELECT a,b, NULL as c from generate_series(1,2) a, generate_series(2,4) b"); 13 | 14 | $r->fetchType = pq\Result::FETCH_ARRAY; 15 | foreach ($r as $k => $v) { 16 | printf("%s => %s,%s,%s\n", $k, $v[0], $v[1], $v[2]); 17 | $r->fetchType = (string) $r->fetchType; 18 | } 19 | 20 | $r->fetchType = pq\Result::FETCH_ASSOC; 21 | foreach ($r as $k => $v) { 22 | printf("%s => %s,%s,%s\n", $k, $v["a"], $v["b"], $v["c"]); 23 | $r->fetchType = (string) $r->fetchType; 24 | } 25 | 26 | $r->fetchType = pq\Result::FETCH_OBJECT; 27 | foreach ($r as $k => $v) { 28 | printf("%s => %s,%s,%s\n", $k, $v->a, $v->b, $v->c); 29 | $r->fetchType = (string) $r->fetchType; 30 | } 31 | 32 | ?> 33 | DONE 34 | --EXPECT-- 35 | Test 36 | 0 => 1,2, 37 | 1 => 1,3, 38 | 2 => 1,4, 39 | 3 => 2,2, 40 | 4 => 2,3, 41 | 5 => 2,4, 42 | 0 => 1,2, 43 | 1 => 1,3, 44 | 2 => 1,4, 45 | 3 => 2,2, 46 | 4 => 2,3, 47 | 5 => 2,4, 48 | 0 => 1,2, 49 | 1 => 1,3, 50 | 2 => 1,4, 51 | 3 => 2,2, 52 | 4 => 2,3, 53 | 5 => 2,4, 54 | DONE 55 | -------------------------------------------------------------------------------- /tests/flush001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | flush 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | nonblocking = true; 13 | var_dump($c->nonblocking); 14 | $c->execAsync("SELECT '".str_repeat("a", 6e7)."'", function($r) { 15 | $r->fetchCol($s); 16 | var_dump(strlen($s)); 17 | }); 18 | var_dump($flushed = $c->flush()); 19 | do { 20 | while (!$flushed || $c->busy) { 21 | $r = $c->busy ? [$c->socket] : null; 22 | $w = !$flushed ?[$c->socket] : null; 23 | 24 | if (stream_select($r, $w, $e, null)) { 25 | if ($r) { 26 | printf("P%d", $c->poll()); 27 | } 28 | if ($w) { 29 | printf("F%d", $flushed = $c->flush()); 30 | } 31 | } 32 | } 33 | echo "\n"; 34 | } while ($c->getResult()); 35 | ?> 36 | ===DONE=== 37 | --EXPECTF-- 38 | Test 39 | bool(true) 40 | bool(%s) 41 | %r(F0)*(F1)*(P3)+%r 42 | int(60000000) 43 | 44 | ===DONE=== 45 | -------------------------------------------------------------------------------- /tests/gh-issue015_listeners.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | restore listeners on reset 3 | --SKIPIF-- 4 | 7 | --INI-- 8 | date.timezone=UTC 9 | --FILE-- 10 | listen("notify", function($channel, $message) { 18 | printf("%s: %s\n", $channel, $message); 19 | }); 20 | $c->on(pq\Connection::EVENT_RESET, function($conn) { 21 | printf("Connection was reset\n"); 22 | }); 23 | $c->notify("notify", "Gotcha!"); 24 | $c->resetAsync(); 25 | 26 | // wait until the stream becomes writable 27 | $w = array($c->socket); 28 | $r = $e = null; 29 | 30 | if (stream_select($r, $w, $e, null)) { 31 | 32 | // loop until the connection is established 33 | while (true) { 34 | 35 | switch ($c->poll()) { 36 | 37 | case pq\Connection::POLLING_READING: 38 | // we should wait for the stream to be read-ready 39 | $r = array($c->socket); 40 | stream_select($r, $w, $e, NULL); 41 | break; 42 | 43 | case pq\Connection::POLLING_WRITING: 44 | // we should wait for the stream to be write-ready 45 | $w = array($c->socket); 46 | $r = $e = null; 47 | stream_select($r, $w, $e, null); 48 | break; 49 | 50 | case pq\Connection::POLLING_FAILED: 51 | printf("Connection failed: %s\n", $c->errorMessage); 52 | break 2; 53 | 54 | case pq\Connection::POLLING_OK: 55 | printf("Connection completed\n"); 56 | break 2; 57 | } 58 | } 59 | } 60 | $c->notify("notify", "Do you miss me?"); 61 | $c->exec(""); 62 | ?> 63 | ===DONE=== 64 | --EXPECT-- 65 | Test 66 | notify: Gotcha! 67 | Connection was reset 68 | Connection completed 69 | notify: Do you miss me? 70 | ===DONE=== -------------------------------------------------------------------------------- /tests/gh-issue015_statements.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | restore statements on reset 3 | --SKIPIF-- 4 | 7 | --INI-- 8 | date.timezone=UTC 9 | --FILE-- 10 | prepare("test", "SELECT 1"); 18 | $c->on(pq\Connection::EVENT_RESET, function($conn) { 19 | printf("Connection was reset\n"); 20 | }); 21 | 22 | var_dump($s->exec()->fetchRow()); 23 | 24 | $c->reset(); 25 | 26 | // Fatal error: Uncaught exception 'pq\Exception\DomainException' with message 'ERROR: prepared statement "test" does not exist' 27 | var_dump($s->exec()->fetchRow()); 28 | 29 | ?> 30 | ===DONE=== 31 | --EXPECT-- 32 | Test 33 | array(1) { 34 | [0]=> 35 | int(1) 36 | } 37 | Connection was reset 38 | array(1) { 39 | [0]=> 40 | int(1) 41 | } 42 | ===DONE=== -------------------------------------------------------------------------------- /tests/gh-issue047_jsonb.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | json conv broken since 2.2.1 3 | --SKIPIF-- 4 | 8 | --INI-- 9 | date.timezone=UTC 10 | --FILE-- 11 | defaultFetchType = \pq\Result::FETCH_ASSOC; 18 | 19 | $q = <<exec($q); 23 | 24 | var_dump($r->fetchAll()); 25 | ?> 26 | ===DONE=== 27 | --EXPECT-- 28 | Test 29 | array(2) { 30 | [0]=> 31 | array(1) { 32 | ["jsonb"]=> 33 | string(4) "text" 34 | } 35 | [1]=> 36 | array(1) { 37 | ["jsonb"]=> 38 | int(0) 39 | } 40 | } 41 | ===DONE=== 42 | -------------------------------------------------------------------------------- /tests/info001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | connection info 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | db, 12 | $c->user, 13 | $c->pass, 14 | $c->host, 15 | $c->port, 16 | $c->options 17 | ); 18 | ?> 19 | DONE 20 | --EXPECTF-- 21 | Test 22 | %s 23 | DONE 24 | -------------------------------------------------------------------------------- /tests/info002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ext info 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | info(); 12 | ?> 13 | Done 14 | --EXPECTF-- 15 | Test 16 | 17 | pq 18 | 19 | PQ Support => enabled 20 | Extension Version => %s 21 | 22 | Used Library => Compiled => Linked 23 | libpq => %s => %s 24 | Done 25 | -------------------------------------------------------------------------------- /tests/lob001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | large objects 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | startTransaction(); 13 | 14 | $lob = $t->createLOB(); 15 | 16 | var_dump($lob->transaction === $t); 17 | 18 | $lob->write(file_get_contents(__FILE__)); 19 | var_dump($lob->tell()); 20 | 21 | $lob->seek(0, SEEK_SET); 22 | $dat = $lob->read(filesize(__FILE__)); 23 | var_dump(md5($dat)===md5_file(__FILE__)); 24 | 25 | $lob->truncate(5); 26 | 27 | $lob = new pq\Lob($t, $lob->oid); 28 | var_dump($lob->read(123)); 29 | 30 | $t->commit(); 31 | $t->unlinkLOB($lob->oid); 32 | 33 | ?> 34 | DONE 35 | --EXPECTF-- 36 | Test 37 | bool(true) 38 | int(474) 39 | bool(true) 40 | string(5) "%c?php" 41 | DONE 42 | 43 | -------------------------------------------------------------------------------- /tests/lob002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | large object stream 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | startTransaction(); 13 | 14 | $lob = $t->createLOB(); 15 | fwrite($lob->stream, file_get_contents(__FILE__)); 16 | var_dump(ftell($lob->stream)); 17 | 18 | fseek($lob->stream, 0, SEEK_SET); 19 | $dat = fread($lob->stream, filesize(__FILE__)); 20 | var_dump(md5($dat)===md5_file(__FILE__)); 21 | 22 | ftruncate($lob->stream, 5); 23 | 24 | $lob = new pq\Lob($t, $lob->oid); 25 | var_dump(fread($lob->stream, 123)); 26 | 27 | $t->commit(); 28 | $t->unlinkLOB($lob->oid); 29 | 30 | ?> 31 | DONE 32 | --EXPECTF-- 33 | Test 34 | int(488) 35 | bool(true) 36 | 37 | Warning: ftruncate(): Can't truncate this stream! in %s on line %d 38 | string(123) "%c?php 39 | echo "Test\n"; 40 | 41 | include "_setup.inc"; 42 | 43 | $c = new pq\Connection(PQ_DSN); 44 | $t = $c->startTransaction(); 45 | 46 | $lob = $t->creat" 47 | DONE 48 | 49 | -------------------------------------------------------------------------------- /tests/lob003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | large object closing stream 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | startTransaction(); 13 | 14 | $lob = $t->createLOB(); 15 | var_dump($lob->stream); 16 | var_dump($lob->stream); 17 | fclose($lob->stream); // bad boy! 18 | var_dump($lob->stream); 19 | var_dump(fread($lob->stream, 5)); 20 | $lob = null; 21 | ?> 22 | DONE 23 | --EXPECTF-- 24 | Test 25 | resource(%d) of type (stream) 26 | resource(%d) of type (stream) 27 | 28 | Warning: fclose(): %d is not a valid stream resource in %s on line %d 29 | resource(%d) of type (stream) 30 | string(0) "" 31 | DONE 32 | 33 | -------------------------------------------------------------------------------- /tests/lob004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | large object import/export 3 | --SKIPIF-- 4 | 5 | --CLEAN-- 6 | 7 | --FILE-- 8 | importLOB(__FILE__); 17 | var_dump($oid); 18 | $t->exportLOB($oid, "lob004.tmp"); 19 | 20 | var_dump(md5_file(__FILE__)===md5_file("lob004.tmp")); 21 | 22 | ?> 23 | DONE 24 | --EXPECTF-- 25 | Test 26 | int(%d) 27 | bool(true) 28 | DONE 29 | 30 | -------------------------------------------------------------------------------- /tests/map001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | map result 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exec("select (ARRAY['one','two','three','four','five','six','seven','eight','nine','ten'])[a] num, ". 13 | "round(log(a)::numeric,3) log, round(exp(a)::numeric,3) exp from generate_series(1,10) a"); 14 | $r->fetchType = pq\Result::FETCH_OBJECT; 15 | 16 | var_dump($r->map()); 17 | var_dump($r->map() == $r->map(0)); 18 | var_dump($r->map() == $r->map(0, array(0,1,2))); 19 | 20 | $r = $c->exec("select * from generate_series(0,1) a, generate_series(0,1) b, generate_series(0,1) c, generate_series(0,1) d ". 21 | "order by a,b,c,d"); 22 | $r->fetchType = pq\Result::FETCH_ARRAY; 23 | var_dump($r->map(array(0,"b",2), "d")); 24 | 25 | ?> 26 | DONE 27 | --EXPECTF-- 28 | Test 29 | object(stdClass)#%d (10) { 30 | ["one"]=> 31 | object(stdClass)#%d (3) { 32 | ["num"]=> 33 | string(3) "one" 34 | ["log"]=> 35 | string(5) "0.000" 36 | ["exp"]=> 37 | string(5) "2.718" 38 | } 39 | ["two"]=> 40 | object(stdClass)#%d (3) { 41 | ["num"]=> 42 | string(3) "two" 43 | ["log"]=> 44 | string(5) "0.301" 45 | ["exp"]=> 46 | string(5) "7.389" 47 | } 48 | ["three"]=> 49 | object(stdClass)#%d (3) { 50 | ["num"]=> 51 | string(5) "three" 52 | ["log"]=> 53 | string(5) "0.477" 54 | ["exp"]=> 55 | string(6) "20.086" 56 | } 57 | ["four"]=> 58 | object(stdClass)#%d (3) { 59 | ["num"]=> 60 | string(4) "four" 61 | ["log"]=> 62 | string(5) "0.602" 63 | ["exp"]=> 64 | string(6) "54.598" 65 | } 66 | ["five"]=> 67 | object(stdClass)#%d (3) { 68 | ["num"]=> 69 | string(4) "five" 70 | ["log"]=> 71 | string(5) "0.699" 72 | ["exp"]=> 73 | string(7) "148.413" 74 | } 75 | ["six"]=> 76 | object(stdClass)#%d (3) { 77 | ["num"]=> 78 | string(3) "six" 79 | ["log"]=> 80 | string(5) "0.778" 81 | ["exp"]=> 82 | string(7) "403.429" 83 | } 84 | ["seven"]=> 85 | object(stdClass)#%d (3) { 86 | ["num"]=> 87 | string(5) "seven" 88 | ["log"]=> 89 | string(5) "0.845" 90 | ["exp"]=> 91 | string(8) "1096.633" 92 | } 93 | ["eight"]=> 94 | object(stdClass)#%d (3) { 95 | ["num"]=> 96 | string(5) "eight" 97 | ["log"]=> 98 | string(5) "0.903" 99 | ["exp"]=> 100 | string(8) "2980.958" 101 | } 102 | ["nine"]=> 103 | object(stdClass)#%d (3) { 104 | ["num"]=> 105 | string(4) "nine" 106 | ["log"]=> 107 | string(5) "0.954" 108 | ["exp"]=> 109 | string(8) "8103.084" 110 | } 111 | ["ten"]=> 112 | object(stdClass)#%d (3) { 113 | ["num"]=> 114 | string(3) "ten" 115 | ["log"]=> 116 | string(5) "1.000" 117 | ["exp"]=> 118 | string(9) "22026.466" 119 | } 120 | } 121 | bool(true) 122 | bool(true) 123 | array(2) { 124 | [0]=> 125 | array(2) { 126 | [0]=> 127 | array(2) { 128 | [0]=> 129 | array(1) { 130 | [3]=> 131 | string(1) "1" 132 | } 133 | [1]=> 134 | array(1) { 135 | [3]=> 136 | string(1) "1" 137 | } 138 | } 139 | [1]=> 140 | array(2) { 141 | [0]=> 142 | array(1) { 143 | [3]=> 144 | string(1) "1" 145 | } 146 | [1]=> 147 | array(1) { 148 | [3]=> 149 | string(1) "1" 150 | } 151 | } 152 | } 153 | [1]=> 154 | array(2) { 155 | [0]=> 156 | array(2) { 157 | [0]=> 158 | array(1) { 159 | [3]=> 160 | string(1) "1" 161 | } 162 | [1]=> 163 | array(1) { 164 | [3]=> 165 | string(1) "1" 166 | } 167 | } 168 | [1]=> 169 | array(2) { 170 | [0]=> 171 | array(1) { 172 | [3]=> 173 | string(1) "1" 174 | } 175 | [1]=> 176 | array(1) { 177 | [3]=> 178 | string(1) "1" 179 | } 180 | } 181 | } 182 | } 183 | DONE 184 | 185 | -------------------------------------------------------------------------------- /tests/notify001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | notify 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | listen("test", function($channel, $message, $pid) { 13 | printf("%s(%d): %s\n", $channel, $pid, $message); 14 | }); 15 | 16 | $producer = new pq\Connection(PQ_DSN); 17 | $producer->notify("test", "this is a test"); 18 | 19 | $consumer->exec("select 1"); 20 | 21 | $producer->notify("test", "this is an async test"); 22 | 23 | $r = array($consumer->socket); 24 | $w = null; $e = null; 25 | stream_select($r, $w, $e, NULL); 26 | $consumer->poll(); 27 | 28 | $producer->notify("other", "this should not show up"); 29 | 30 | stream_select($r, $w, $e, 0,1000); 31 | $consumer->poll(); 32 | 33 | $producer->notify("test", "just to be sure"); 34 | 35 | $r = array($consumer->socket); 36 | $w = null; $e = null; 37 | stream_select($r, $w, $e, 0,1000); 38 | $consumer->poll(); 39 | 40 | $consumer->unlisten("test"); 41 | 42 | $producer->notify("test", "this shouldn't show up either"); 43 | 44 | $r = array($consumer->socket); 45 | $w = null; $e = null; 46 | stream_select($r, $w, $e, 0,1000); 47 | $consumer->poll(); 48 | 49 | ?> 50 | DONE 51 | --EXPECTF-- 52 | Test 53 | test(%d): this is a test 54 | test(%d): this is an async test 55 | test(%d): just to be sure 56 | DONE 57 | -------------------------------------------------------------------------------- /tests/persistent001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | persistent handles 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | listen("chan", function($chan, $msg) { 17 | // dummy 18 | }); 19 | $c->on(pq\Connection::EVENT_RESULT, function($c, $res) { 20 | }); 21 | } 22 | 23 | if (!($i%10)) gc_collect_cycles(); 24 | 25 | $c->exec(""); 26 | } 27 | var_dump(raphf\stat_persistent_handles()->{"pq\\Connection"}); 28 | ?> 29 | DONE 30 | --EXPECTF-- 31 | Test 32 | array(1) { 33 | ["%S"]=> 34 | array(2) { 35 | ["used"]=> 36 | int(1) 37 | ["free"]=> 38 | int(2) 39 | } 40 | } 41 | DONE 42 | -------------------------------------------------------------------------------- /tests/res001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | empty result 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 30 | Done 31 | --EXPECTF-- 32 | Test 33 | 34 | Warning: pq\Result not initialized in %s on line %d 35 | 36 | Warning: pq\Result not initialized in %s on line %d 37 | 38 | Warning: pq\Result not initialized in %s on line %d 39 | 40 | Warning: pq\Result not initialized in %s on line %d 41 | 42 | Warning: pq\Result not initialized in %s on line %d 43 | 44 | Warning: pq\Result not initialized in %s on line %d 45 | 46 | Warning: pq\Result not initialized in %s on line %d 47 | 48 | Warning: pq\Result not initialized in %s on line %d 49 | 50 | Warning: pq\Result not initialized in %s on line %d 51 | object(pq\Result)#%d (9) { 52 | ["status"]=> 53 | NULL 54 | ["statusMessage"]=> 55 | NULL 56 | ["errorMessage"]=> 57 | NULL 58 | ["diag"]=> 59 | NULL 60 | ["numRows"]=> 61 | int(0) 62 | ["numCols"]=> 63 | int(0) 64 | ["affectedRows"]=> 65 | int(0) 66 | ["fetchType"]=> 67 | int(0) 68 | ["autoConvert"]=> 69 | int(65535) 70 | } 71 | Test 72 | 73 | Warning: pq\Result not initialized in %s on line %d 74 | 75 | Warning: pq\Result not initialized in %s on line %d 76 | 77 | Warning: pq\Result not initialized in %s on line %d 78 | 79 | Warning: pq\Result not initialized in %s on line %d 80 | 81 | Warning: pq\Result not initialized in %s on line %d 82 | 83 | Warning: pq\Result not initialized in %s on line %d 84 | 85 | Warning: pq\Result not initialized in %s on line %d 86 | 87 | Warning: pq\Result not initialized in %s on line %d 88 | 89 | Warning: pq\Result not initialized in %s on line %d 90 | 91 | Warning: pq\Result not initialized in %s on line %d 92 | array(10) { 93 | ["affectedRows"]=> 94 | int(0) 95 | ["autoConvert"]=> 96 | int(65535) 97 | ["diag"]=> 98 | NULL 99 | ["dummy"]=> 100 | int(2) 101 | ["errorMessage"]=> 102 | NULL 103 | ["fetchType"]=> 104 | int(0) 105 | ["numCols"]=> 106 | int(0) 107 | ["numRows"]=> 108 | int(0) 109 | ["status"]=> 110 | NULL 111 | ["statusMessage"]=> 112 | NULL 113 | } 114 | Done 115 | -------------------------------------------------------------------------------- /tests/reset001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | connection reset 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | reset(); 13 | var_dump($c->status); 14 | $c->on(pq\Connection::EVENT_RESET, function ($c) { print "RESET!\n"; }); 15 | $c->reset(); 16 | var_dump($c->status); 17 | 18 | ?> 19 | DONE 20 | --EXPECT-- 21 | Test 22 | int(0) 23 | RESET! 24 | int(0) 25 | DONE 26 | -------------------------------------------------------------------------------- /tests/savepoint001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | savepoints 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | startTransaction(); 13 | $t->savepoint(); 14 | $t->savepoint(); 15 | $t->rollback(); 16 | $t->commit(); 17 | $t->rollback(); 18 | 19 | ?> 20 | DONE 21 | --EXPECT-- 22 | Test 23 | DONE 24 | 25 | -------------------------------------------------------------------------------- /tests/stm_bound001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | statement w/ bound vars 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | bind(0, $_1); 15 | $s->bind(1, $_2); 16 | $s->bind(2, $_3); 17 | $r = $s->exec(); 18 | var_dump($r->fetchAll()); 19 | $_1 = "\$1"; 20 | $_2 = "\$2"; 21 | $_3 = "\$3"; 22 | $r = $s->exec(); 23 | var_dump($r->fetchAll()); 24 | ?> 25 | Done 26 | --EXPECT-- 27 | Test 28 | array(1) { 29 | [0]=> 30 | array(3) { 31 | [0]=> 32 | NULL 33 | [1]=> 34 | NULL 35 | [2]=> 36 | NULL 37 | } 38 | } 39 | array(1) { 40 | [0]=> 41 | array(3) { 42 | [0]=> 43 | string(2) "$1" 44 | [1]=> 45 | string(2) "$2" 46 | [2]=> 47 | string(2) "$3" 48 | } 49 | } 50 | Done 51 | -------------------------------------------------------------------------------- /tests/stm_deallocate_prepare001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Deallocated and prepare statement 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | prepare("test1", "SELECT 'test' || \$1"); 12 | 13 | $r = $s->exec(array("ing")); 14 | $r->fetchCol($d); 15 | var_dump($d); 16 | 17 | $s->deallocate(); 18 | 19 | try { 20 | $s->exec(array("ing")); 21 | } catch (pq\Exception\BadMethodCallException $e) { 22 | echo "Caught exception\n"; 23 | } 24 | 25 | $s->prepare(); 26 | 27 | $r = $s->exec(array("ing")); 28 | $r->fetchCol($d); 29 | var_dump($d); 30 | 31 | ?> 32 | DONE 33 | --EXPECT-- 34 | Test 35 | string(7) "testing" 36 | Caught exception 37 | string(7) "testing" 38 | DONE 39 | -------------------------------------------------------------------------------- /tests/stm_desc001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | desc statement 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | prepare("test1", "SELECT NOW() - \$1"); 12 | $r = $s->exec(array("2012-12-12 12:12:12")); 13 | $d = $s->desc(); 14 | 15 | printf("%s\n", (new pq\Types($c))[$d[0]]->typname); 16 | 17 | ?> 18 | DONE 19 | --EXPECT-- 20 | Test 21 | timestamptz 22 | DONE 23 | -------------------------------------------------------------------------------- /tests/stm_desc002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | desc statement 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exec(array("2012-12-12 12:12:12")); 13 | $d = $s->desc(); 14 | 15 | printf("%s\n", (new pq\Types($c))[$d[0]]->typname); 16 | 17 | ?> 18 | DONE 19 | --EXPECT-- 20 | Test 21 | timestamptz 22 | DONE 23 | -------------------------------------------------------------------------------- /tests/stm_props001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Statement properties 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | connection); 21 | var_dump($n === $s->name); 22 | var_dump($q === $s->query); 23 | var_dump($t === $s->types); 24 | 25 | $n = 'props2'; 26 | $s = $c->prepare($n, $q, $t); 27 | 28 | var_dump($c === $s->connection); 29 | var_dump($n === $s->name); 30 | var_dump($q === $s->query); 31 | var_dump($t === $s->types); 32 | 33 | ?> 34 | Done 35 | --EXPECT-- 36 | Test 37 | bool(true) 38 | bool(true) 39 | bool(true) 40 | bool(true) 41 | bool(true) 42 | bool(true) 43 | bool(true) 44 | bool(true) 45 | Done 46 | -------------------------------------------------------------------------------- /tests/trans001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | transaction 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exec("DROP TABLE IF EXISTS test CASCADE"); 13 | $c->exec("SET client_min_messages TO NOTICE"); 14 | try { 15 | $c->exec("SET lc_messages TO 'C'"); 16 | } catch (pq\Exception $e) { 17 | // do not die if we are not superuser 18 | } 19 | $c->on(pq\Connection::EVENT_NOTICE, function($c, $notice) { 20 | echo "Got notice: $notice\n"; 21 | }); 22 | var_dump($c->transactionStatus == pq\Connection::TRANS_IDLE); 23 | $t = new pq\Transaction($c); 24 | var_dump($t->connection->transactionStatus == pq\Connection::TRANS_INTRANS); 25 | $c->exec("DROP TABLE IF EXISTS test"); 26 | $c->off(pq\Connection::EVENT_NOTICE); 27 | $c->exec("CREATE TABLE test (id serial, data text)"); 28 | $s = $c->prepare("test_insert", "INSERT INTO test (data) VALUES (\$1)", array((new pq\Types($c))["text"]->oid)); 29 | $s->exec(array("a")); 30 | $s->exec(array("b")); 31 | $s->exec(array("c")); 32 | $r = $c->exec("SELECT * FROM test"); 33 | while ($row = $r->fetchRow(pq\Result::FETCH_OBJECT)) { 34 | printf("%d => %s\n", $row->id, $row->data); 35 | } 36 | $t->rollback(); 37 | var_dump($c->transactionStatus == pq\Connection::TRANS_IDLE); 38 | ?> 39 | DONE 40 | --EXPECT-- 41 | Test 42 | bool(true) 43 | bool(true) 44 | Got notice: NOTICE: table "test" does not exist, skipping 45 | 1 => a 46 | 2 => b 47 | 3 => c 48 | bool(true) 49 | DONE 50 | -------------------------------------------------------------------------------- /tests/trans002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | txn properties 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | isolation, 14 | $t->readonly, 15 | $t->deferrable 16 | ); 17 | 18 | $t->isolation = pq\Transaction::SERIALIZABLE; 19 | $t->readonly = true; 20 | $t->deferrable = true; 21 | var_dump( 22 | $t->isolation, 23 | $t->readonly, 24 | $t->deferrable 25 | ); 26 | ?> 27 | DONE 28 | --EXPECTF-- 29 | Test 30 | int(0) 31 | bool(false) 32 | bool(false) 33 | int(2) 34 | bool(true) 35 | bool(true) 36 | DONE 37 | -------------------------------------------------------------------------------- /tests/types001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | types functionality 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | connection === $c); 13 | var_dump(isset($t["int4"]), empty($t["int4"])); 14 | var_dump(isset($t["whatthahell"]), empty($t["whatthahell"])); 15 | 16 | var_dump(isset($t[25]), empty($t[25])); 17 | var_dump(isset($t[0]), empty($t[0])); 18 | ?> 19 | DONE 20 | --EXPECT-- 21 | Test 22 | bool(true) 23 | bool(true) 24 | bool(false) 25 | bool(false) 26 | bool(true) 27 | bool(true) 28 | bool(false) 29 | bool(false) 30 | bool(true) 31 | DONE 32 | -------------------------------------------------------------------------------- /tests/types002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | extended type support 3 | --SKIPIF-- 4 | 7 | --INI-- 8 | date.timezone=UTC 9 | --FILE-- 10 | exec("SET timezone TO UTC; SELECT 17 | NULL as null, 18 | true as bool, 19 | 1::int2 as int2, 20 | 2::int4 as int4, 21 | 3::int8 as int8, 22 | 1.1::float4 as float4, 23 | 2.2::float8 as float8, 24 | '2013-01-01'::date as date, 25 | '2013-01-01 01:01:01'::timestamp as timestamp, 26 | '2013-01-01 01:01:01 UTC'::timestamptz as timestamptz, 27 | array[array[1,2,3],array[4,5,6],array[NULL::int,NULL::int,NULL::int]] as intarray, 28 | array[box(point(1,2),point(2,3)),box(point(4,5),point(5,6))] as boxarray, 29 | array[]::text[] as emptyarray, 30 | 'foo\n'::bytea as bytea, 31 | 'foo\n'::bytea::text as bytea_text 32 | "); 33 | var_dump($r->fetchRow(pq\Result::FETCH_ASSOC)); 34 | ?> 35 | DONE 36 | --EXPECTF-- 37 | Test 38 | array(15) { 39 | ["null"]=> 40 | NULL 41 | ["bool"]=> 42 | bool(true) 43 | ["int2"]=> 44 | int(1) 45 | ["int4"]=> 46 | int(2) 47 | ["int8"]=> 48 | int(3) 49 | ["float4"]=> 50 | float(1.1) 51 | ["float8"]=> 52 | float(2.2) 53 | ["date"]=> 54 | object(pq\DateTime)#%d (4) { 55 | ["format"]=> 56 | string(5) "Y-m-d" 57 | ["date"]=> 58 | string(%d) "2013-01-01 00:00:00%r(\.000000)?%r" 59 | ["timezone_type"]=> 60 | int(3) 61 | ["timezone"]=> 62 | string(3) "UTC" 63 | } 64 | ["timestamp"]=> 65 | object(pq\DateTime)#%d (4) { 66 | ["format"]=> 67 | string(13) "Y-m-d H:i:s.u" 68 | ["date"]=> 69 | string(%d) "2013-01-01 01:01:01%r(\.000000)?%r" 70 | ["timezone_type"]=> 71 | int(3) 72 | ["timezone"]=> 73 | string(3) "UTC" 74 | } 75 | ["timestamptz"]=> 76 | object(pq\DateTime)#%d (4) { 77 | ["format"]=> 78 | string(14) "Y-m-d H:i:s.uO" 79 | ["date"]=> 80 | string(%d) "2013-01-01 01:01:01%r(\.000000)?%r" 81 | ["timezone_type"]=> 82 | int(1) 83 | ["timezone"]=> 84 | string(6) "+00:00" 85 | } 86 | ["intarray"]=> 87 | array(3) { 88 | [0]=> 89 | array(3) { 90 | [0]=> 91 | int(1) 92 | [1]=> 93 | int(2) 94 | [2]=> 95 | int(3) 96 | } 97 | [1]=> 98 | array(3) { 99 | [0]=> 100 | int(4) 101 | [1]=> 102 | int(5) 103 | [2]=> 104 | int(6) 105 | } 106 | [2]=> 107 | array(3) { 108 | [0]=> 109 | NULL 110 | [1]=> 111 | NULL 112 | [2]=> 113 | NULL 114 | } 115 | } 116 | ["boxarray"]=> 117 | array(2) { 118 | [0]=> 119 | string(11) "(2,3),(1,2)" 120 | [1]=> 121 | string(11) "(5,6),(4,5)" 122 | } 123 | ["emptyarray"]=> 124 | array(0) { 125 | } 126 | ["bytea"]=> 127 | string(4) "foo 128 | " 129 | ["bytea_text"]=> 130 | string(10) "\x666f6f0a" 131 | } 132 | DONE 133 | -------------------------------------------------------------------------------- /tests/unbuffered001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | unbuffered result 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | unbuffered ? true : false); 17 | $c->unbuffered = 1; 18 | var_dump($c->unbuffered); 19 | 20 | $c->execAsync("SELECT a from generate_series(1,10) a", function($res) { 21 | switch ($res->status) { 22 | case pq\Result::SINGLE_TUPLE: 23 | $res->fetchCol($val, "a"); 24 | printf("%s\n", $val); 25 | break; 26 | case pq\Result::TUPLES_OK: 27 | printf("-> fetching done\n"); 28 | break; 29 | default: 30 | printf("!! %s\n", $res->errorMessage); 31 | break; 32 | } 33 | }); 34 | 35 | do { 36 | while ($c->busy) { 37 | $r = array($c->socket); 38 | $w = $e = null; 39 | if (stream_select($r, $w, $e, null)) { 40 | $c->poll(); 41 | } 42 | } 43 | } while ($c->getResult()); 44 | 45 | ?> 46 | DONE 47 | --EXPECTF-- 48 | Test 49 | bool(false) 50 | bool(true) 51 | 1 52 | 2 53 | 3 54 | 4 55 | 5 56 | 6 57 | 7 58 | 8 59 | 9 60 | 10 61 | -> fetching done 62 | DONE 63 | --------------------------------------------------------------------------------