diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml
index 41453650c08..3a85e5cfb2a 100644
--- a/.github/workflows/ext.yml
+++ b/.github/workflows/ext.yml
@@ -74,7 +74,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- php-version: [ '8.1', '8.2', '8.3', '8.4' ]
+ php-version: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
max-parallel: 8
fail-fast: false
steps:
diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml
index 85405901fa7..6d0c72b6c50 100644
--- a/.github/workflows/framework.yml
+++ b/.github/workflows/framework.yml
@@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- php-version: [ '8.1', '8.2', '8.3', '8.4' ]
+ php-version: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ]
name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }}
steps:
diff --git a/.github/workflows/iouring.yml b/.github/workflows/iouring.yml
index 99480e42f1c..c4f6c5f5ecd 100644
--- a/.github/workflows/iouring.yml
+++ b/.github/workflows/iouring.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- php: [ '8.1', '8.2', '8.3', '8.4' ]
+ php: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
os: [ ubuntu-24.04, ubuntu-24.04-arm ]
name: ${{ matrix.php }}-${{ matrix.os }}-iouring
runs-on: ${{ matrix.os }}
diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml
index 979377886f8..9f7bee4b1cb 100644
--- a/.github/workflows/thread.yml
+++ b/.github/workflows/thread.yml
@@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- php: ['8.1-zts', '8.2-zts', '8.3-zts', '8.4-zts']
+ php: ['8.1-zts', '8.2-zts', '8.3-zts', '8.4-zts', '8.5-zts']
os: [ ubuntu-24.04, ubuntu-24.04-arm ]
name: ${{ matrix.php }}-thread-${{ matrix.os }}
runs-on: ${{ matrix.os }}
diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml
index cad8f971348..27c4f545e4a 100644
--- a/.github/workflows/unit.yml
+++ b/.github/workflows/unit.yml
@@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- php: ['8.1', '8.2', '8.3', '8.4']
+ php: ['8.1', '8.2', '8.3', '8.4', '8.5']
os: [ ubuntu-24.04, ubuntu-24.04-arm ]
name: ${{ matrix.php }}-${{ matrix.os }}
runs-on: ${{ matrix.os }}
@@ -50,7 +50,7 @@ jobs:
strategy:
fail-fast: false
matrix:
-# php-version: [ '8.1', '8.2', '8.3', '8.4' ]
+# php-version: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
php-version: [ '8.1' ]
name: ${{ matrix.php-version }} - macOS
steps:
diff --git a/config.m4 b/config.m4
index aeb9b521d1a..170e047365b 100644
--- a/config.m4
+++ b/config.m4
@@ -1360,20 +1360,24 @@ EOF
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql)
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_pgsql)
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_pgsql)
+ PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php85/pdo_pgsql)
fi
if test "$PHP_SWOOLE_ODBC" != "no"; then
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_odbc)
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_odbc)
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_odbc)
+ PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php85/pdo_odbc)
fi
if test "$PHP_SWOOLE_ORACLE" != "no"; then
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_oci)
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_oci)
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_oci)
+ PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php85/pdo_oci)
fi
if test "$PHP_SWOOLE_SQLITE" != "no"; then
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_sqlite)
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_sqlite)
PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_sqlite)
+ PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php85/pdo_sqlite)
fi
fi
diff --git a/ext-src/php_swoole_odbc.h b/ext-src/php_swoole_odbc.h
index 59e19c0f57b..a9fcc7d140f 100644
--- a/ext-src/php_swoole_odbc.h
+++ b/ext-src/php_swoole_odbc.h
@@ -32,8 +32,10 @@ BEGIN_EXTERN_C()
#include "thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h"
#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400
#include "thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h"
-#else
+#elif PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h"
+#else
+#include "thirdparty/php85/pdo_odbc/php_pdo_odbc_int.h"
#endif
extern const pdo_driver_t swoole_pdo_odbc_driver;
diff --git a/ext-src/php_swoole_oracle.h b/ext-src/php_swoole_oracle.h
index 7ce78a99e3f..02acdd3c5ae 100644
--- a/ext-src/php_swoole_oracle.h
+++ b/ext-src/php_swoole_oracle.h
@@ -34,8 +34,10 @@ BEGIN_EXTERN_C()
#include "thirdparty/php81/pdo_oci/php_pdo_oci_int.h"
#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400
#include "thirdparty/php83/pdo_oci/php_pdo_oci_int.h"
-#else
+#elif PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "thirdparty/php84/pdo_oci/php_pdo_oci_int.h"
+#else
+#include "thirdparty/php85/pdo_oci/php_pdo_oci_int.h"
#endif
extern const pdo_driver_t swoole_pdo_oci_driver;
diff --git a/ext-src/php_swoole_pgsql.h b/ext-src/php_swoole_pgsql.h
index 187c67d22af..d6f87d439f2 100644
--- a/ext-src/php_swoole_pgsql.h
+++ b/ext-src/php_swoole_pgsql.h
@@ -33,8 +33,10 @@ BEGIN_EXTERN_C()
#include "thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h"
#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400
#include "thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h"
-#else
+#elif PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h"
+#else
+#include "thirdparty/php85/pdo_pgsql/php_pdo_pgsql_int.h"
#endif
diff --git a/ext-src/php_swoole_sqlite.h b/ext-src/php_swoole_sqlite.h
index 209a4ad5565..9d949c64a5a 100644
--- a/ext-src/php_swoole_sqlite.h
+++ b/ext-src/php_swoole_sqlite.h
@@ -33,8 +33,10 @@ BEGIN_EXTERN_C()
#include "thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h"
#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400
#include "thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h"
-#else
+#elif PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h"
+#else
+#include "thirdparty/php85/pdo_sqlite/php_pdo_sqlite_int.h"
#endif
extern const pdo_driver_t swoole_pdo_sqlite_driver;
diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc
index 77a7bc5fc78..1963165348b 100644
--- a/ext-src/swoole_runtime.cc
+++ b/ext-src/swoole_runtime.cc
@@ -190,12 +190,24 @@ static zend_internal_arg_info *copy_arginfo(const zend_function *zf, zend_intern
memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types));
ZEND_TYPE_SET_PTR(new_arg_info[i].type, new_list);
+#if PHP_VERSION_ID >= 80500
+ // For PHP 8.5+, ZEND_TYPE_LIST_FOREACH gives a const pointer, which we can't modify.
+ // We must use a manual loop over the list we allocated ourselves.
+ for (uint32_t j = 0; j < new_list->num_types; j++) {
+ zend_type *list_type = &new_list->types[j];
+ if (ZEND_TYPE_HAS_NAME(*list_type)) {
+ zend_string *name = zend_string_dup(ZEND_TYPE_NAME(*list_type), 1);
+ ZEND_TYPE_SET_PTR(*list_type, name);
+ }
+ }
+#else
zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(new_list, list_type) {
zend_string *name = zend_string_dup(ZEND_TYPE_NAME(*list_type), true);
ZEND_TYPE_SET_PTR(*list_type, name);
}
ZEND_TYPE_LIST_FOREACH_END();
+#endif
} else if (ZEND_TYPE_HAS_NAME(arg_info[i].type)) {
zend_string *name = zend_string_dup(ZEND_TYPE_NAME(arg_info[i].type), true);
ZEND_TYPE_SET_PTR(new_arg_info[i].type, name);
diff --git a/thirdparty/php/main/SAPI.h b/thirdparty/php/main/SAPI.h
index 03634219b02..f434ad1dd0e 100644
--- a/thirdparty/php/main/SAPI.h
+++ b/thirdparty/php/main/SAPI.h
@@ -38,7 +38,11 @@ static void swoole_php_treat_data(int arg, char *str, zval *destArray) {
switch (arg) {
case PARSE_STRING:
+#if PHP_VERSION_ID >= 80500
+ separator = ZSTR_VAL(PG(arg_separator).input);
+#else
separator = PG(arg_separator).input;
+#endif
break;
case PARSE_COOKIE:
separator = (char *) ";\0";
diff --git a/thirdparty/php84/pdo_oci/oci_driver.c b/thirdparty/php84/pdo_oci/oci_driver.c
index 6ee73fd7110..b03c14c5296 100644
--- a/thirdparty/php84/pdo_oci/oci_driver.c
+++ b/thirdparty/php84/pdo_oci/oci_driver.c
@@ -17,7 +17,7 @@
#define SW_USE_ORACLE_HOOK
#include "php_swoole_oracle.h"
-#if PHP_VERSION_ID >= 80400
+#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "php.h"
#include "php_ini.h"
diff --git a/thirdparty/php84/pdo_oci/oci_statement.c b/thirdparty/php84/pdo_oci/oci_statement.c
index cd91dbf7223..8e97cb415df 100644
--- a/thirdparty/php84/pdo_oci/oci_statement.c
+++ b/thirdparty/php84/pdo_oci/oci_statement.c
@@ -17,7 +17,7 @@
#define SW_USE_ORACLE_HOOK
#include "php_swoole_oracle.h"
-#if PHP_VERSION_ID >= 80400
+#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "php.h"
#include "php_ini.h"
diff --git a/thirdparty/php84/pdo_odbc/odbc_driver.c b/thirdparty/php84/pdo_odbc/odbc_driver.c
index f410d42050c..9978af9e353 100644
--- a/thirdparty/php84/pdo_odbc/odbc_driver.c
+++ b/thirdparty/php84/pdo_odbc/odbc_driver.c
@@ -16,7 +16,7 @@
#define SW_USE_ODBC_HOOK
#include "php_swoole_odbc.h"
-#if PHP_VERSION_ID >= 80400
+#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
diff --git a/thirdparty/php84/pdo_odbc/odbc_stmt.c b/thirdparty/php84/pdo_odbc/odbc_stmt.c
index 8fe11cf2c6a..6bd65d205f9 100644
--- a/thirdparty/php84/pdo_odbc/odbc_stmt.c
+++ b/thirdparty/php84/pdo_odbc/odbc_stmt.c
@@ -17,7 +17,7 @@
#define SW_USE_ODBC_HOOK
#include "php_swoole_odbc.h"
-#if PHP_VERSION_ID >= 80400
+#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
diff --git a/thirdparty/php84/pdo_pgsql/pgsql_driver.c b/thirdparty/php84/pdo_pgsql/pgsql_driver.c
index cf98f5f45f9..565f59d91ec 100644
--- a/thirdparty/php84/pdo_pgsql/pgsql_driver.c
+++ b/thirdparty/php84/pdo_pgsql/pgsql_driver.c
@@ -19,7 +19,7 @@
#define SW_USE_PGSQL_HOOK
#include "php_swoole_pgsql.h"
-#if PHP_VERSION_ID >= 80400
+#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "php.h"
#include "php_ini.h"
#include "ext/standard/php_string.h" /* For php_addcslashes_str() in _pdo_pgsql_escape_credentials() */
diff --git a/thirdparty/php84/pdo_pgsql/pgsql_statement.c b/thirdparty/php84/pdo_pgsql/pgsql_statement.c
index acb21384814..0987b9825be 100644
--- a/thirdparty/php84/pdo_pgsql/pgsql_statement.c
+++ b/thirdparty/php84/pdo_pgsql/pgsql_statement.c
@@ -19,7 +19,7 @@
#define SW_USE_PGSQL_HOOK
#include "php_swoole_pgsql.h"
-#if PHP_VERSION_ID >= 80400
+#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "php.h"
#include "php_ini.h"
diff --git a/thirdparty/php84/pdo_sqlite/sqlite_driver.c b/thirdparty/php84/pdo_sqlite/sqlite_driver.c
index 27d53bbe991..760f180f169 100644
--- a/thirdparty/php84/pdo_sqlite/sqlite_driver.c
+++ b/thirdparty/php84/pdo_sqlite/sqlite_driver.c
@@ -18,7 +18,7 @@
#include "php_swoole_sqlite.h"
#include "php_swoole_call_stack.h"
-#if PHP_VERSION_ID >= 80400
+#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
diff --git a/thirdparty/php84/pdo_sqlite/sqlite_statement.c b/thirdparty/php84/pdo_sqlite/sqlite_statement.c
index 5f9793e4617..8b2fef12fbb 100644
--- a/thirdparty/php84/pdo_sqlite/sqlite_statement.c
+++ b/thirdparty/php84/pdo_sqlite/sqlite_statement.c
@@ -17,7 +17,7 @@
#define SW_USE_SQLITE_HOOK
#include "php_swoole_sqlite.h"
-#if PHP_VERSION_ID >= 80400
+#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
diff --git a/thirdparty/php85/pdo_oci/LICENSE b/thirdparty/php85/pdo_oci/LICENSE
new file mode 100644
index 00000000000..0815d7eb791
--- /dev/null
+++ b/thirdparty/php85/pdo_oci/LICENSE
@@ -0,0 +1,68 @@
+--------------------------------------------------------------------
+ The PHP License, version 3.01
+Copyright (c) 1999 - 2024 The PHP Group. All rights reserved.
+--------------------------------------------------------------------
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. The name "PHP" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact group@php.net.
+
+ 4. Products derived from this software may not be called "PHP", nor
+ may "PHP" appear in their name, without prior written permission
+ from group@php.net. You may indicate that your software works in
+ conjunction with PHP by saying "Foo for PHP" instead of calling
+ it "PHP Foo" or "phpfoo"
+
+ 5. The PHP Group may publish revised and/or new versions of the
+ license from time to time. Each version will be given a
+ distinguishing version number.
+ Once covered code has been published under a particular version
+ of the license, you may always continue to use it under the terms
+ of that version. You may also choose to use such covered code
+ under the terms of any subsequent version of the license
+ published by the PHP Group. No one other than the PHP Group has
+ the right to modify the terms applicable to covered code created
+ under this License.
+
+ 6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes PHP software, freely available from
+ ".
+
+THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
+ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
+DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+--------------------------------------------------------------------
+
+This software consists of voluntary contributions made by many
+individuals on behalf of the PHP Group.
+
+The PHP Group can be contacted via Email at group@php.net.
+
+For more information on the PHP Group and the PHP project,
+please see .
+
+PHP includes the Zend Engine, freely available at
+.
diff --git a/thirdparty/php85/pdo_oci/oci_driver.c b/thirdparty/php85/pdo_oci/oci_driver.c
new file mode 100644
index 00000000000..5e2577b60e1
--- /dev/null
+++ b/thirdparty/php85/pdo_oci/oci_driver.c
@@ -0,0 +1,892 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#define SW_USE_ORACLE_HOOK
+#include "php_swoole_oracle.h"
+
+#if PHP_VERSION_ID >= 80500
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "pdo/php_pdo.h"
+#include "pdo/php_pdo_driver.h"
+#include "php_pdo_oci.h"
+//#include "php_pdo_oci_int.h"
+#include "Zend/zend_exceptions.h"
+
+static inline ub4 pdo_oci_sanitize_prefetch(long prefetch);
+
+static void pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */
+{
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+ pdo_oci_error_info *einfo;
+
+ einfo = &H->einfo;
+
+ if (stmt) {
+ pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
+
+ if (S->einfo.errmsg) {
+ einfo = &S->einfo;
+ }
+ }
+
+ if (einfo->errcode) {
+ add_next_index_long(info, einfo->errcode);
+ add_next_index_string(info, einfo->errmsg);
+ }
+}
+/* }}} */
+
+ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line) /* {{{ */
+{
+ text errbuf[1024] = "<>";
+ char tmp_buf[2048];
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+ pdo_oci_error_info *einfo;
+ pdo_oci_stmt *S = NULL;
+ pdo_error_type *pdo_err = &dbh->error_code;
+
+ if (stmt) {
+ S = (pdo_oci_stmt*)stmt->driver_data;
+ einfo = &S->einfo;
+ pdo_err = &stmt->error_code;
+ }
+ else {
+ einfo = &H->einfo;
+ }
+
+ if (einfo->errmsg) {
+ pefree(einfo->errmsg, dbh->is_persistent);
+ }
+
+ einfo->errmsg = NULL;
+ einfo->errcode = 0;
+ einfo->file = file;
+ einfo->line = line;
+
+ if (isinit) { /* Initialization error */
+ strcpy(*pdo_err, "HY000");
+ slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line);
+ einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
+ }
+ else {
+ switch (status) {
+ case OCI_SUCCESS:
+ strcpy(*pdo_err, "00000");
+ break;
+ case OCI_ERROR:
+ OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
+ slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line);
+ einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
+ break;
+ case OCI_SUCCESS_WITH_INFO:
+ OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
+ slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line);
+ einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
+ break;
+ case OCI_NEED_DATA:
+ slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line);
+ einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
+ break;
+ case OCI_NO_DATA:
+ slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line);
+ einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
+ break;
+ case OCI_INVALID_HANDLE:
+ slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line);
+ einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
+ break;
+ case OCI_STILL_EXECUTING:
+ slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line);
+ einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
+ break;
+ case OCI_CONTINUE:
+ slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line);
+ einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
+ break;
+ }
+
+ if (einfo->errcode) {
+ switch (einfo->errcode) {
+ case 1013: /* user requested cancel of current operation */
+ zend_bailout();
+ break;
+
+ case 12154: /* ORA-12154: TNS:could not resolve service name */
+ strcpy(*pdo_err, "42S02");
+ break;
+
+ case 22: /* ORA-00022: invalid session id */
+ case 378:
+ case 602:
+ case 603:
+ case 604:
+ case 609:
+ case 1012: /* ORA-01012: */
+ case 1033:
+ case 1041:
+ case 1043:
+ case 1089:
+ case 1090:
+ case 1092:
+ case 3113: /* ORA-03133: end of file on communication channel */
+ case 3114:
+ case 3122:
+ case 3135:
+ case 12153:
+ case 27146:
+ case 28511:
+ /* consider the connection closed */
+ dbh->is_closed = 1;
+ H->attached = 0;
+ strcpy(*pdo_err, "01002"); /* FIXME */
+ break;
+
+ default:
+ strcpy(*pdo_err, "HY000");
+ }
+ }
+
+ if (stmt) {
+ /* always propagate the error code back up to the dbh,
+ * so that we can catch the error information when execute
+ * is called via query. See Bug #33707 */
+ if (H->einfo.errmsg) {
+ pefree(H->einfo.errmsg, dbh->is_persistent);
+ }
+ H->einfo = *einfo;
+ H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL;
+ strcpy(dbh->error_code, stmt->error_code);
+ }
+ }
+
+ /* little mini hack so that we can use this code from the dbh ctor */
+ if (!dbh->methods && status != OCI_SUCCESS_WITH_INFO) {
+ zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg);
+ }
+
+ return einfo->errcode;
+}
+/* }}} */
+
+static void oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */
+{
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+
+ if (H->svc) {
+ /* rollback any outstanding work */
+ OCITransRollback(H->svc, H->err, 0);
+ }
+
+ if (H->session) {
+ OCIHandleFree(H->session, OCI_HTYPE_SESSION);
+ H->session = NULL;
+ }
+
+ if (H->svc) {
+ OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX);
+ H->svc = NULL;
+ }
+
+ if (H->server && H->attached) {
+ H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT);
+ if (H->last_err) {
+ oci_drv_error("OCIServerDetach");
+ }
+ H->attached = 0;
+ }
+
+ if (H->server) {
+ OCIHandleFree(H->server, OCI_HTYPE_SERVER);
+ H->server = NULL;
+ }
+
+ if (H->err) {
+ OCIHandleFree(H->err, OCI_HTYPE_ERROR);
+ H->err = NULL;
+ }
+
+ if (H->charset && H->env) {
+ OCIHandleFree(H->env, OCI_HTYPE_ENV);
+ H->env = NULL;
+ }
+
+ if (H->einfo.errmsg) {
+ pefree(H->einfo.errmsg, dbh->is_persistent);
+ H->einfo.errmsg = NULL;
+ }
+
+ pefree(H, dbh->is_persistent);
+}
+/* }}} */
+
+static bool oci_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */
+{
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+ pdo_oci_stmt *S = ecalloc(1, sizeof(*S));
+ ub4 prefetch;
+ zend_string *nsql = NULL;
+ int ret;
+
+ S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
+ PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL ?
+ OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
+
+ S->H = H;
+ stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
+ ret = pdo_parse_params(stmt, sql, &nsql);
+
+ if (ret == 1) {
+ /* query was re-written */
+ sql = nsql;
+ } else if (ret == -1) {
+ /* couldn't grok it */
+ strcpy(dbh->error_code, stmt->error_code);
+ efree(S);
+ return false;
+ }
+
+ /* create an OCI statement handle */
+ OCIHandleAlloc(H->env, (dvoid*)&S->stmt, OCI_HTYPE_STMT, 0, NULL);
+
+ /* and our own private error handle */
+ OCIHandleAlloc(H->env, (dvoid*)&S->err, OCI_HTYPE_ERROR, 0, NULL);
+
+ if (ZSTR_LEN(sql) != 0) {
+ H->last_err = OCIStmtPrepare(S->stmt, H->err, (text*) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
+ if (nsql) {
+ zend_string_release(nsql);
+ nsql = NULL;
+ }
+ if (H->last_err) {
+ H->last_err = oci_drv_error("OCIStmtPrepare");
+ OCIHandleFree(S->stmt, OCI_HTYPE_STMT);
+ OCIHandleFree(S->err, OCI_HTYPE_ERROR);
+ efree(S);
+ return false;
+ }
+
+ }
+
+ prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/
+ H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0,
+ OCI_ATTR_PREFETCH_ROWS, H->err);
+ if (!H->last_err) {
+ prefetch *= PDO_OCI_PREFETCH_ROWSIZE;
+ H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0,
+ OCI_ATTR_PREFETCH_MEMORY, H->err);
+ }
+
+ stmt->driver_data = S;
+ stmt->methods = &oci_stmt_methods;
+ if (nsql) {
+ zend_string_release(nsql);
+ nsql = NULL;
+ }
+
+ return true;
+}
+/* }}} */
+
+static zend_long oci_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* {{{ */
+{
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+ OCIStmt *stmt;
+ ub2 stmt_type;
+ ub4 rowcount;
+ int ret = -1;
+
+ OCIHandleAlloc(H->env, (dvoid*)&stmt, OCI_HTYPE_STMT, 0, NULL);
+
+ H->last_err = OCIStmtPrepare(stmt, H->err, (text*)ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
+ if (H->last_err) {
+ H->last_err = oci_drv_error("OCIStmtPrepare");
+ OCIHandleFree(stmt, OCI_HTYPE_STMT);
+ return -1;
+ }
+
+ H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err);
+
+ if (stmt_type == OCI_STMT_SELECT) {
+ /* invalid usage; cancel it */
+ OCIHandleFree(stmt, OCI_HTYPE_STMT);
+ php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid");
+ return -1;
+ }
+
+ /* now we are good to go */
+ H->last_err = OCIStmtExecute(H->svc, stmt, H->err, 1, 0, NULL, NULL,
+ (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
+
+ sword last_err = H->last_err;
+
+ if (last_err) {
+ H->last_err = oci_drv_error("OCIStmtExecute");
+ }
+
+ if (!last_err || last_err == OCI_SUCCESS_WITH_INFO) {
+ /* return the number of affected rows */
+ H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err);
+ ret = rowcount;
+ }
+
+ OCIHandleFree(stmt, OCI_HTYPE_STMT);
+
+ return ret;
+}
+/* }}} */
+
+static zend_string* oci_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype ) /* {{{ */
+{
+ int qcount = 0;
+ char const *cu, *l, *r;
+ char *c, *quoted;
+ size_t quotedlen;
+ zend_string *quoted_str;
+
+ if (ZSTR_LEN(unquoted) == 0) {
+ return ZSTR_INIT_LITERAL("''", 0);
+ }
+
+ /* count single quotes */
+ for (cu = ZSTR_VAL(unquoted); (cu = strchr(cu,'\'')); qcount++, cu++)
+ ; /* empty loop */
+
+ quotedlen = ZSTR_LEN(unquoted) + qcount + 2;
+ quoted = c = emalloc(quotedlen+1);
+ *c++ = '\'';
+
+ /* foreach (chunk that ends in a quote) */
+ for (l = ZSTR_VAL(unquoted); (r = strchr(l,'\'')); l = r+1) {
+ strncpy(c, l, r-l+1);
+ c += (r-l+1);
+ *c++ = '\''; /* add second quote */
+ }
+
+ /* Copy remainder and add enclosing quote */
+ strncpy(c, l, quotedlen-(c-quoted)-1);
+ quoted[quotedlen-1] = '\'';
+ quoted[quotedlen] = '\0';
+
+ quoted_str = zend_string_init(quoted, quotedlen, 0);
+ efree(quoted);
+ return quoted_str;
+}
+/* }}} */
+
+static bool oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */
+{
+ /* with Oracle, there is nothing special to be done */
+ return true;
+}
+/* }}} */
+
+static bool oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */
+{
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+
+ H->last_err = OCITransCommit(H->svc, H->err, 0);
+
+ if (H->last_err) {
+ H->last_err = oci_drv_error("OCITransCommit");
+ return false;
+ }
+ return true;
+}
+/* }}} */
+
+static bool oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */
+{
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+
+ H->last_err = OCITransRollback(H->svc, H->err, 0);
+
+ if (H->last_err) {
+ H->last_err = oci_drv_error("OCITransRollback");
+ return false;
+ }
+ return true;
+}
+/* }}} */
+
+static bool oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */
+{
+ zend_long lval;
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+
+ switch (attr) {
+ case PDO_ATTR_AUTOCOMMIT:
+ {
+ bool bval;
+ if (!pdo_get_bool_param(&bval, val)) {
+ return false;
+ }
+
+ if (dbh->in_txn) {
+ /* Assume they want to commit whatever is outstanding */
+ H->last_err = OCITransCommit(H->svc, H->err, 0);
+
+ if (H->last_err) {
+ H->last_err = oci_drv_error("OCITransCommit");
+ return false;
+ }
+ dbh->in_txn = false;
+ }
+
+ dbh->auto_commit = (unsigned int) bval;
+ return true;
+ }
+ case PDO_ATTR_PREFETCH:
+ {
+ if (!pdo_get_long_param(&lval, val)) {
+ return false;
+ }
+
+ H->prefetch = pdo_oci_sanitize_prefetch(lval);
+ return true;
+ }
+ case PDO_OCI_ATTR_ACTION:
+ {
+#if (OCI_MAJOR_VERSION >= 10)
+ zend_string *action = zval_try_get_string(val);
+ if (UNEXPECTED(!action)) {
+ return false;
+ }
+
+ H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
+ (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action),
+ OCI_ATTR_ACTION, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION");
+ return false;
+ }
+ return true;
+#else
+ oci_drv_error("Unsupported attribute type");
+ return false;
+#endif
+ }
+ case PDO_OCI_ATTR_CLIENT_INFO:
+ {
+#if (OCI_MAJOR_VERSION >= 10)
+ zend_string *client_info = zval_try_get_string(val);
+ if (UNEXPECTED(!client_info)) {
+ return false;
+ }
+
+ H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
+ (dvoid *) ZSTR_VAL(client_info), (ub4) ZSTR_LEN(client_info),
+ OCI_ATTR_CLIENT_INFO, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO");
+ return false;
+ }
+ return true;
+#else
+ oci_drv_error("Unsupported attribute type");
+ return false;
+#endif
+ }
+ case PDO_OCI_ATTR_CLIENT_IDENTIFIER:
+ {
+#if (OCI_MAJOR_VERSION >= 10)
+ zend_string *identifier = zval_try_get_string(val);
+ if (UNEXPECTED(!identifier)) {
+ return false;
+ }
+
+ H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
+ (dvoid *) ZSTR_VAL(identifier), (ub4) ZSTR_LEN(identifier),
+ OCI_ATTR_CLIENT_IDENTIFIER, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER");
+ return false;
+ }
+ return true;
+#else
+ oci_drv_error("Unsupported attribute type");
+ return false;
+#endif
+ }
+ case PDO_OCI_ATTR_MODULE:
+ {
+#if (OCI_MAJOR_VERSION >= 10)
+ zend_string *module = zval_try_get_string(val);
+ if (UNEXPECTED(!module)) {
+ return false;
+ }
+
+ H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
+ (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module),
+ OCI_ATTR_MODULE, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE");
+ return false;
+ }
+ return true;
+#else
+ oci_drv_error("Unsupported attribute type");
+ return false;
+#endif
+ }
+ case PDO_OCI_ATTR_CALL_TIMEOUT:
+ {
+#if (OCI_MAJOR_VERSION >= 18)
+ if (!pdo_get_long_param(&lval, val)) {
+ return false;
+ }
+ ub4 timeout = (ub4) lval;
+
+ H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX,
+ (dvoid *) &timeout, (ub4) 0,
+ OCI_ATTR_CALL_TIMEOUT, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT");
+ return false;
+ }
+ return true;
+#else
+ oci_drv_error("Unsupported attribute type");
+ return false;
+#endif
+ }
+ default:
+ return false;
+ }
+
+}
+/* }}} */
+
+static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */
+{
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+
+ switch (attr) {
+ case PDO_ATTR_SERVER_VERSION:
+ case PDO_ATTR_SERVER_INFO:
+ {
+ text infostr[512];
+ char verstr[15];
+ ub4 vernum;
+
+ if (OCIServerRelease(H->svc, H->err, infostr, (ub4)sizeof(infostr), (ub1)OCI_HTYPE_SVCCTX, &vernum))
+ {
+ ZVAL_STRING(return_value, "<>");
+ } else {
+ if (attr == PDO_ATTR_SERVER_INFO) {
+ ZVAL_STRING(return_value, (char *)infostr);
+ } else {
+ slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d",
+ (int)((vernum>>24) & 0xFF), /* version number */
+ (int)((vernum>>20) & 0x0F), /* release number*/
+ (int)((vernum>>12) & 0xFF), /* update number */
+ (int)((vernum>>8) & 0x0F), /* port release number */
+ (int)((vernum>>0) & 0xFF)); /* port update number */
+
+ ZVAL_STRING(return_value, verstr);
+ }
+ }
+ return TRUE;
+ }
+
+ case PDO_ATTR_CLIENT_VERSION:
+ {
+#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2)
+ /* Run time client version */
+ sword major, minor, update, patch, port_update;
+ char verstr[15];
+
+ OCIClientVersion(&major, &minor, &update, &patch, &port_update);
+ slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update);
+ ZVAL_STRING(return_value, verstr);
+#elif defined(PHP_PDO_OCI_CLIENT_VERSION)
+ /* Compile time client version */
+ ZVAL_STRING(return_value, PHP_PDO_OCI_CLIENT_VERSION);
+#else
+ return FALSE;
+
+#endif /* Check for OCIClientVersion() support */
+
+ return TRUE;
+ }
+
+ case PDO_ATTR_AUTOCOMMIT:
+ ZVAL_BOOL(return_value, dbh->auto_commit);
+ return TRUE;
+
+ case PDO_ATTR_PREFETCH:
+ ZVAL_LONG(return_value, H->prefetch);
+ return TRUE;
+ case PDO_OCI_ATTR_CALL_TIMEOUT:
+ {
+#if (OCI_MAJOR_VERSION >= 18)
+ ub4 timeout;
+
+ H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX,
+ (dvoid *) &timeout, NULL,
+ OCI_ATTR_CALL_TIMEOUT, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT");
+ return FALSE;
+ }
+
+ ZVAL_LONG(return_value, (zend_long) timeout);
+ return TRUE;
+#else
+ oci_drv_error("Unsupported attribute type");
+ return FALSE;
+#endif
+ }
+ default:
+ return FALSE;
+
+ }
+ return FALSE;
+
+}
+/* }}} */
+
+static zend_result pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */
+{
+ pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
+ sb4 error_code = 0;
+#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))))
+ char version[256];
+#endif
+
+ /* TODO move attached check to PDO level */
+ if (H->attached == 0) {
+ return FAILURE;
+ }
+ /* TODO add persistent_timeout check at PDO level */
+
+
+ /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation)
+ * such as from Pre-10.1 servers, the error is still from the server and we would have
+ * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for
+ * Pre-10.2 clients
+ */
+#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */
+ H->last_err = OCIPing (H->svc, H->err, OCI_DEFAULT);
+#else
+ /* use good old OCIServerVersion() */
+ H->last_err = OCIServerVersion (H->svc, H->err, (text *)version, sizeof(version), OCI_HTYPE_SVCCTX);
+#endif
+ if (H->last_err == OCI_SUCCESS) {
+ return SUCCESS;
+ }
+
+ OCIErrorGet (H->err, (ub4)1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR);
+
+ if (error_code == 1010) {
+ return SUCCESS;
+ }
+ return FAILURE;
+}
+/* }}} */
+
+static const struct pdo_dbh_methods oci_methods = {
+ oci_handle_closer,
+ oci_handle_preparer,
+ oci_handle_doer,
+ oci_handle_quoter,
+ oci_handle_begin,
+ oci_handle_commit,
+ oci_handle_rollback,
+ oci_handle_set_attribute,
+ NULL, /* last_id not supported */
+ pdo_oci_fetch_error_func,
+ oci_handle_get_attribute,
+ pdo_oci_check_liveness, /* check_liveness */
+ NULL, /* get_driver_methods */
+ NULL, /* request_shutdown */
+ NULL, /* in transaction, use PDO's internal tracking mechanism */
+ NULL /* get_gc */
+};
+
+static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
+{
+ pdo_oci_db_handle *H;
+ int i, ret = 0;
+ struct pdo_data_src_parser vars[] = {
+ { "charset", NULL, 0 },
+ { "dbname", "", 0 },
+ { "user", NULL, 0 },
+ { "password", NULL, 0 }
+ };
+
+ php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4);
+
+ H = pecalloc(1, sizeof(*H), dbh->is_persistent);
+ dbh->driver_data = H;
+
+ dbh->skip_param_evt =
+ 1 << PDO_PARAM_EVT_FETCH_PRE |
+ 1 << PDO_PARAM_EVT_FETCH_POST |
+ 1 << PDO_PARAM_EVT_NORMALIZE;
+
+ H->prefetch = PDO_OCI_PREFETCH_DEFAULT;
+
+ /* allocate an environment */
+#ifdef HAVE_OCIENVNLSCREATE
+ if (vars[0].optval) {
+ H->charset = OCINlsCharSetNameToId(pdo_oci_Env, (const oratext *)vars[0].optval);
+ if (!H->charset) {
+ oci_init_error("OCINlsCharSetNameToId: unknown character set name");
+ goto cleanup;
+ } else {
+ if (OCIEnvNlsCreate(&H->env, PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != OCI_SUCCESS) {
+ oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle libraries and NLS data");
+ goto cleanup;
+ }
+ }
+ }
+#endif
+ if (H->env == NULL) {
+ /* use the global environment */
+ H->env = pdo_oci_Env;
+ }
+
+ /* something to hold errors */
+ OCIHandleAlloc(H->env, (dvoid **)&H->err, OCI_HTYPE_ERROR, 0, NULL);
+
+ /* handle for the server */
+ OCIHandleAlloc(H->env, (dvoid **)&H->server, OCI_HTYPE_SERVER, 0, NULL);
+
+ H->last_err = OCIServerAttach(H->server, H->err, (text*)vars[1].optval,
+ (sb4) strlen(vars[1].optval), OCI_DEFAULT);
+
+ if (H->last_err) {
+ oci_drv_error("pdo_oci_handle_factory");
+ goto cleanup;
+ }
+
+ H->attached = 1;
+
+ /* create a service context */
+ H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->svc, OCI_HTYPE_SVCCTX, 0, NULL);
+ if (H->last_err) {
+ oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX");
+ goto cleanup;
+ }
+
+ H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->session, OCI_HTYPE_SESSION, 0, NULL);
+ if (H->last_err) {
+ oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION");
+ goto cleanup;
+ }
+
+ /* set server handle into service handle */
+ H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER");
+ goto cleanup;
+ }
+
+ /* username */
+ if (!dbh->username && vars[2].optval) {
+ dbh->username = pestrdup(vars[2].optval, dbh->is_persistent);
+ }
+
+ if (dbh->username) {
+ H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
+ dbh->username, (ub4) strlen(dbh->username),
+ OCI_ATTR_USERNAME, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME");
+ goto cleanup;
+ }
+ }
+
+ /* password */
+ if (!dbh->password && vars[3].optval) {
+ dbh->password = pestrdup(vars[3].optval, dbh->is_persistent);
+ }
+
+ if (dbh->password) {
+ H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
+ dbh->password, (ub4) strlen(dbh->password),
+ OCI_ATTR_PASSWORD, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD");
+ goto cleanup;
+ }
+ }
+
+ /* Now fire up the session */
+ H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT);
+ if (H->last_err) {
+ oci_drv_error("OCISessionBegin");
+ /* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when
+ * user's password has expired, but is still usable.
+ */
+ if (H->last_err != OCI_SUCCESS_WITH_INFO) {
+ goto cleanup;
+ }
+ }
+
+ /* set the server handle into service handle */
+ H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err);
+ if (H->last_err) {
+ oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION");
+ goto cleanup;
+ }
+
+ /* Get max character width */
+ H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ);
+ if (H->last_err) {
+ oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ");
+ goto cleanup;
+ }
+
+ dbh->methods = &oci_methods;
+ dbh->alloc_own_columns = 1;
+ dbh->native_case = PDO_CASE_UPPER;
+
+ ret = 1;
+
+cleanup:
+ for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) {
+ if (vars[i].freeme) {
+ efree(vars[i].optval);
+ }
+ }
+
+ if (!ret) {
+ oci_handle_closer(dbh);
+ }
+
+ return ret;
+}
+/* }}} */
+
+const pdo_driver_t swoole_pdo_oci_driver = {
+ PDO_DRIVER_HEADER(oci),
+ pdo_oci_handle_factory
+};
+
+static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */
+{
+ if (prefetch < 0) {
+ prefetch = 0;
+ } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) {
+ prefetch = PDO_OCI_PREFETCH_DEFAULT;
+ }
+ return ((ub4)prefetch);
+}
+/* }}} */
+#endif
diff --git a/thirdparty/php85/pdo_oci/oci_statement.c b/thirdparty/php85/pdo_oci/oci_statement.c
new file mode 100644
index 00000000000..90ab13f4efb
--- /dev/null
+++ b/thirdparty/php85/pdo_oci/oci_statement.c
@@ -0,0 +1,1000 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#define SW_USE_ORACLE_HOOK
+#include "php_swoole_oracle.h"
+
+#if PHP_VERSION_ID >= 80500
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "pdo/php_pdo.h"
+#include "pdo/php_pdo_driver.h"
+#include "php_pdo_oci.h"
+//#include "php_pdo_oci_int.h"
+#include "Zend/zend_extensions.h"
+
+#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */
+
+#define STMT_CALL(name, params) \
+ do { \
+ S->last_err = name params; \
+ S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \
+ if (S->last_err) { \
+ return 0; \
+ } \
+ } while(0)
+
+#define STMT_CALL_MSG(name, msg, params) \
+ do { \
+ S->last_err = name params; \
+ S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \
+ if (S->last_err) { \
+ return 0; \
+ } \
+ } while(0)
+
+static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob);
+
+#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \
+ do \
+ { \
+ boolean isTempLOB; \
+ OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \
+ if (isTempLOB) \
+ OCILobFreeTemporary(svchp, errhp, lob); \
+ } while(0)
+
+static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
+{
+ pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
+ HashTable *BC = stmt->bound_columns;
+ HashTable *BP = stmt->bound_params;
+
+ int i;
+
+ if (S->stmt) {
+ /* cancel server side resources for the statement if we didn't
+ * fetch it all */
+ OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
+
+ /* free the handle */
+ OCIHandleFree(S->stmt, OCI_HTYPE_STMT);
+ S->stmt = NULL;
+ }
+ if (S->err) {
+ OCIHandleFree(S->err, OCI_HTYPE_ERROR);
+ S->err = NULL;
+ }
+
+ /* need to ensure these go away now */
+ if (BC) {
+ zend_hash_destroy(BC);
+ FREE_HASHTABLE(stmt->bound_columns);
+ stmt->bound_columns = NULL;
+ }
+
+ if (BP) {
+ zend_hash_destroy(BP);
+ FREE_HASHTABLE(stmt->bound_params);
+ stmt->bound_params = NULL;
+ }
+
+ if (S->einfo.errmsg) {
+ pefree(S->einfo.errmsg, stmt->dbh->is_persistent);
+ S->einfo.errmsg = NULL;
+ }
+
+#if PHP_API_VERSION < 20240925
+ bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle)
+ && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)])
+ && !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED);
+#else
+ bool server_obj_usable = php_pdo_stmt_valid_db_obj_handle(stmt);
+#endif
+
+ if (S->cols) {
+ for (i = 0; i < stmt->column_count; i++) {
+ if (S->cols[i].data) {
+ switch (S->cols[i].dtype) {
+ case SQLT_BLOB:
+ case SQLT_CLOB:
+ if (server_obj_usable) {
+ OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err,
+ (OCILobLocator *) S->cols[i].data);
+ }
+ OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB);
+ break;
+ default:
+ efree(S->cols[i].data);
+ }
+ }
+ }
+ efree(S->cols);
+ S->cols = NULL;
+ }
+ efree(S);
+
+ stmt->driver_data = NULL;
+
+ return 1;
+} /* }}} */
+
+static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
+{
+ pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
+ ub4 rowcount;
+ b4 mode;
+
+ if (!S->stmt_type) {
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_STMT_TYPE",
+ (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err));
+ }
+
+ if (stmt->executed) {
+ /* ensure that we cancel the cursor from a previous fetch */
+ OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
+ }
+
+#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */
+ if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) {
+ mode = OCI_STMT_SCROLLABLE_READONLY;
+ } else
+#endif
+ if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) {
+ mode = OCI_COMMIT_ON_SUCCESS;
+ } else {
+ mode = OCI_DEFAULT;
+ }
+
+ STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err,
+ (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL,
+ mode));
+
+ if (!stmt->executed) {
+ ub4 colcount;
+ /* do first-time-only definition of bind/mapping stuff */
+
+ /* how many columns do we have ? */
+ STMT_CALL_MSG(OCIAttrGet, "ATTR_PARAM_COUNT",
+ (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err));
+
+ stmt->column_count = (int)colcount;
+
+ if (S->cols) {
+ int i;
+ for (i = 0; i < stmt->column_count; i++) {
+ if (S->cols[i].data) {
+ switch (S->cols[i].dtype) {
+ case SQLT_BLOB:
+ case SQLT_CLOB:
+ /* do nothing */
+ break;
+ default:
+ efree(S->cols[i].data);
+ }
+ }
+ }
+ efree(S->cols);
+ }
+
+ S->cols = ecalloc(colcount, sizeof(pdo_oci_column));
+ }
+
+ STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT",
+ (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err));
+ stmt->row_count = (long)rowcount;
+
+ return 1;
+} /* }}} */
+
+static sb4 oci_bind_input_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */
+{
+ struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
+ pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
+ zval *parameter;
+
+ ZEND_ASSERT(param);
+
+ *indpp = &P->indicator;
+
+ if (Z_ISREF(param->parameter))
+ parameter = Z_REFVAL(param->parameter);
+ else
+ parameter = ¶m->parameter;
+
+ if (P->thing) {
+ *bufpp = P->thing;
+ *alenp = sizeof(void*);
+ } else if (ZVAL_IS_NULL(parameter)) {
+ /* insert a NULL value into the column */
+ P->indicator = -1; /* NULL */
+ *bufpp = 0;
+ *alenp = -1;
+ } else if (!P->thing) {
+ /* regular string bind */
+ if (!try_convert_to_string(parameter)) {
+ return OCI_ERROR;
+ }
+ *bufpp = Z_STRVAL_P(parameter);
+ *alenp = (ub4) Z_STRLEN_P(parameter);
+ }
+
+ *piecep = OCI_ONE_PIECE;
+ return OCI_CONTINUE;
+} /* }}} */
+
+static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) /* {{{ */
+{
+ struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
+ pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
+ zval *parameter;
+
+ ZEND_ASSERT(param);
+
+ if (Z_ISREF(param->parameter)) {
+ parameter = Z_REFVAL(param->parameter);
+ } else {
+ parameter = ¶m->parameter;
+ }
+
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
+ P->actual_len = sizeof(OCILobLocator*);
+ *bufpp = P->thing;
+ *alenpp = &P->actual_len;
+ *piecep = OCI_ONE_PIECE;
+ *rcodepp = &P->retcode;
+ *indpp = &P->indicator;
+ return OCI_CONTINUE;
+ }
+
+ if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) {
+ return OCI_CONTINUE;
+ }
+
+ zval_ptr_dtor(parameter);
+
+ Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1);
+ P->used_for_output = 1;
+
+ P->actual_len = (ub4) Z_STRLEN_P(parameter);
+ *alenpp = &P->actual_len;
+ *bufpp = (Z_STR_P(parameter))->val;
+ *piecep = OCI_ONE_PIECE;
+ *rcodepp = &P->retcode;
+ *indpp = &P->indicator;
+
+ return OCI_CONTINUE;
+} /* }}} */
+
+static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) /* {{{ */
+{
+ pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
+
+ /* we're only interested in parameters for prepared SQL right now */
+ if (param->is_param) {
+ pdo_oci_bound_param *P;
+ sb4 value_sz = -1;
+ zval *parameter;
+
+ if (Z_ISREF(param->parameter))
+ parameter = Z_REFVAL(param->parameter);
+ else
+ parameter = ¶m->parameter;
+
+ P = (pdo_oci_bound_param*)param->driver_data;
+
+ switch (event_type) {
+ case PDO_PARAM_EVT_FETCH_PRE:
+ case PDO_PARAM_EVT_FETCH_POST:
+ case PDO_PARAM_EVT_NORMALIZE:
+ /* Do nothing */
+ break;
+
+ case PDO_PARAM_EVT_FREE:
+ P = param->driver_data;
+ if (P && P->thing) {
+ OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
+ OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
+ P->thing = NULL;
+ efree(P);
+ }
+ else if (P) {
+ efree(P);
+ }
+ break;
+
+ case PDO_PARAM_EVT_ALLOC:
+ P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param));
+ param->driver_data = P;
+
+ /* figure out what we're doing */
+ switch (PDO_PARAM_TYPE(param->param_type)) {
+ case PDO_PARAM_STMT:
+ return 0;
+
+ case PDO_PARAM_LOB:
+ /* P->thing is now an OCILobLocator * */
+ P->oci_type = SQLT_BLOB;
+ value_sz = (sb4) sizeof(OCILobLocator*);
+ break;
+
+ case PDO_PARAM_STR:
+ default:
+ P->oci_type = SQLT_CHR;
+ value_sz = (sb4) param->max_value_len;
+ if (param->max_value_len == 0) {
+ value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */
+ }
+
+ }
+
+ if (param->name) {
+ STMT_CALL(OCIBindByName, (S->stmt,
+ &P->bind, S->err, (text*)param->name->val,
+ (sb4) param->name->len, 0, value_sz, P->oci_type,
+ &P->indicator, 0, &P->retcode, 0, 0,
+ OCI_DATA_AT_EXEC));
+ } else {
+ STMT_CALL(OCIBindByPos, (S->stmt,
+ &P->bind, S->err, ((ub4)param->paramno)+1,
+ 0, value_sz, P->oci_type,
+ &P->indicator, 0, &P->retcode, 0, 0,
+ OCI_DATA_AT_EXEC));
+ }
+
+ STMT_CALL(OCIBindDynamic, (P->bind,
+ S->err,
+ param, oci_bind_input_cb,
+ param, oci_bind_output_cb));
+
+ return 1;
+
+ case PDO_PARAM_EVT_EXEC_PRE:
+ P->indicator = 0;
+ P->used_for_output = 0;
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
+ ub4 empty = 0;
+ STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL));
+ STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err));
+ S->have_blobs = 1;
+ }
+ return 1;
+
+ case PDO_PARAM_EVT_EXEC_POST:
+ /* fixup stuff set in motion in oci_bind_output_cb */
+ if (P->used_for_output) {
+ if (P->indicator == -1) {
+ /* set up a NULL value */
+ if (Z_TYPE_P(parameter) == IS_STRING) {
+ /* OCI likes to stick non-terminated strings in things */
+ *Z_STRVAL_P(parameter) = '\0';
+ }
+ zval_ptr_dtor_str(parameter);
+ ZVAL_UNDEF(parameter);
+ } else if (Z_TYPE_P(parameter) == IS_STRING) {
+ Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1);
+ }
+ } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) {
+ php_stream *stm;
+
+ if (Z_TYPE_P(parameter) == IS_NULL) {
+ /* if the param is NULL, then we assume that they
+ * wanted to bind a lob locator into it from the query
+ * */
+
+ stm = oci_create_lob_stream(stmt, (OCILobLocator*)P->thing);
+ if (stm) {
+ OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
+ php_stream_to_zval(stm, parameter);
+ }
+ } else {
+ /* we're a LOB being used for insert; transfer the data now */
+ size_t n;
+ ub4 amt, offset = 1;
+ char *consume;
+
+ php_stream_from_zval_no_verify(stm, parameter);
+ if (stm) {
+ OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
+ do {
+ char buf[8192];
+ n = php_stream_read(stm, buf, sizeof(buf));
+ if ((int)n <= 0) {
+ break;
+ }
+ consume = buf;
+ do {
+ amt = (ub4) n;
+ OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
+ &amt, offset, consume, (ub4) n,
+ OCI_ONE_PIECE,
+ NULL, NULL, 0, SQLCS_IMPLICIT);
+ offset += amt;
+ n -= amt;
+ consume += amt;
+ } while (n);
+ } while (1);
+ OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
+ OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0);
+ } else if (Z_TYPE_P(parameter) == IS_STRING) {
+ /* stick the string into the LOB */
+ consume = Z_STRVAL_P(parameter);
+ n = Z_STRLEN_P(parameter);
+ if (n) {
+ OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
+ while (n) {
+ amt = (ub4) n;
+ OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
+ &amt, offset, consume, (ub4) n,
+ OCI_ONE_PIECE,
+ NULL, NULL, 0, SQLCS_IMPLICIT);
+ consume += amt;
+ n -= amt;
+ }
+ OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
+ }
+ }
+ OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
+ OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
+ P->thing = NULL;
+ }
+ }
+
+ return 1;
+ }
+ }
+
+ return 1;
+} /* }}} */
+
+static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */
+{
+ ub4 ociori = OCI_FETCH_NEXT;
+ pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
+
+ switch (ori) {
+ case PDO_FETCH_ORI_NEXT: ociori = OCI_FETCH_NEXT; break;
+ case PDO_FETCH_ORI_PRIOR: ociori = OCI_FETCH_PRIOR; break;
+ case PDO_FETCH_ORI_FIRST: ociori = OCI_FETCH_FIRST; break;
+ case PDO_FETCH_ORI_LAST: ociori = OCI_FETCH_LAST; break;
+ case PDO_FETCH_ORI_ABS: ociori = OCI_FETCH_ABSOLUTE; break;
+ case PDO_FETCH_ORI_REL: ociori = OCI_FETCH_RELATIVE; break;
+ }
+ S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT);
+
+ if (S->last_err == OCI_NO_DATA) {
+ /* no (more) data */
+ return 0;
+ }
+
+ if (S->last_err == OCI_NEED_DATA) {
+ oci_stmt_error("OCI_NEED_DATA");
+ return 0;
+ }
+
+ if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) {
+ return 1;
+ }
+
+ oci_stmt_error("OCIStmtFetch");
+
+ return 0;
+} /* }}} */
+
+static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp,
+ ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp)
+{
+ pdo_oci_column *col = (pdo_oci_column*)octxp;
+
+ switch (col->dtype) {
+ case SQLT_BLOB:
+ case SQLT_CLOB:
+ *piecep = OCI_ONE_PIECE;
+ *bufpp = col->data;
+ *alenpp = &col->datalen;
+ *indpp = (dvoid *)&col->indicator;
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+
+ return OCI_CONTINUE;
+}
+
+static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
+{
+ pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
+ OCIParam *param = NULL;
+ text *colname;
+ ub2 dtype, data_size, precis;
+ ub4 namelen;
+ struct pdo_column_data *col = &stmt->columns[colno];
+ bool dyn = FALSE;
+
+ /* describe the column */
+ STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1));
+
+ /* what type ? */
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
+ (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
+
+ /* how big ? */
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE",
+ (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));
+
+ /* precision ? */
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
+ (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
+
+ /* name ? */
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME",
+ (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));
+
+ col->precision = precis;
+ col->maxlen = data_size;
+ col->name = zend_string_init((char *)colname, namelen, 0);
+
+ S->cols[colno].dtype = dtype;
+
+ /* how much room do we need to store the field */
+ switch (dtype) {
+ case SQLT_LBI:
+ case SQLT_LNG:
+ if (dtype == SQLT_LBI) {
+ dtype = SQLT_BIN;
+ } else {
+ dtype = SQLT_CHR;
+ }
+ S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */
+ S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
+ break;
+
+ case SQLT_BLOB:
+ case SQLT_CLOB:
+ STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL));
+ S->cols[colno].datalen = sizeof(OCILobLocator*);
+ dyn = TRUE;
+ break;
+
+ case SQLT_BIN:
+ default:
+ if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD
+#ifdef SQLT_TIMESTAMP
+ || dtype == SQLT_TIMESTAMP
+#endif
+#ifdef SQLT_TIMESTAMP_TZ
+ || dtype == SQLT_TIMESTAMP_TZ
+#endif
+ ) {
+ /* should be big enough for most date formats and numbers */
+ S->cols[colno].datalen = 512;
+#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)
+ } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) {
+ S->cols[colno].datalen = 1024;
+#endif
+ } else if (dtype == SQLT_BIN) {
+ S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */
+ } else {
+ S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width);
+ }
+
+ S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
+ dtype = SQLT_CHR;
+ }
+
+ STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1,
+ S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator,
+ &S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT));
+
+ if (dyn) {
+ STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno],
+ oci_define_callback));
+ }
+
+ return 1;
+} /* }}} */
+
+struct _oci_lob_env {
+ OCISvcCtx *svc;
+ OCIError *err;
+};
+typedef struct _oci_lob_env oci_lob_env;
+
+struct oci_lob_self {
+ zval dbh;
+ pdo_stmt_t *stmt;
+ pdo_oci_stmt *S;
+ OCILobLocator *lob;
+ oci_lob_env *E;
+ ub4 offset;
+ ub1 csfrm;
+};
+
+static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
+{
+ struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
+ ub4 amt;
+ sword r;
+
+ amt = (ub4) count;
+ r = OCILobWrite(self->E->svc, self->E->err, self->lob,
+ &amt, self->offset, (char*)buf, (ub4) count,
+ OCI_ONE_PIECE,
+ NULL, NULL, 0, SQLCS_IMPLICIT);
+
+ if (r != OCI_SUCCESS) {
+ return (ssize_t)-1;
+ }
+
+ self->offset += amt;
+ return amt;
+}
+
+static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count)
+{
+ struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
+ oraub8 byte_amt = (oraub8) count;
+ oraub8 char_amt = 0;
+
+ sword r = OCILobRead2(self->E->svc, self->E->err, self->lob,
+ &byte_amt, &char_amt, (oraub8) self->offset, buf, (oraub8) count,
+ OCI_ONE_PIECE, NULL, NULL, 0, self->csfrm);
+
+ if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
+ return (ssize_t)-1;
+ }
+
+ self->offset += self->csfrm == 0 ? byte_amt : char_amt;
+ if (byte_amt < count) {
+ stream->eof = 1;
+ }
+ return byte_amt;
+}
+
+static int oci_blob_close(php_stream *stream, int close_handle)
+{
+ struct oci_lob_self *self = (struct oci_lob_self *)stream->abstract;
+ pdo_stmt_t *stmt = self->stmt;
+
+ if (close_handle) {
+ zend_object *obj = &stmt->std;
+
+ OCILobClose(self->E->svc, self->E->err, self->lob);
+ zval_ptr_dtor(&self->dbh);
+ GC_DELREF(obj);
+ efree(self->E);
+ efree(self);
+ }
+
+ /* php_pdo_free_statement(stmt); */
+ return 0;
+}
+
+static int oci_blob_flush(php_stream *stream)
+{
+ struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
+ OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0);
+ return 0;
+}
+
+static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
+{
+ struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
+
+ if (offset >= PDO_OCI_LOBMAXSIZE) {
+ return -1;
+ } else {
+ self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */
+ return 0;
+ }
+}
+
+static const php_stream_ops oci_blob_stream_ops = {
+ oci_blob_write,
+ oci_blob_read,
+ oci_blob_close,
+ oci_blob_flush,
+ "pdo_oci blob stream",
+ oci_blob_seek,
+ NULL,
+ NULL,
+ NULL
+};
+
+static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob)
+{
+ php_stream *stm;
+ struct oci_lob_self *self = ecalloc(1, sizeof(*self));
+
+#if PHP_API_VERSION < 20240925
+ ZVAL_COPY_VALUE(&self->dbh, &stmt->database_object_handle);
+#else
+ ZVAL_OBJ(&self->dbh, stmt->database_object_handle);
+#endif
+ self->lob = lob;
+ self->offset = 1; /* 1-based */
+ self->stmt = stmt;
+ self->S = (pdo_oci_stmt*)stmt->driver_data;
+ self->E = ecalloc(1, sizeof(oci_lob_env));
+ self->E->svc = self->S->H->svc;
+ self->E->err = self->S->err;
+
+ OCILobCharSetForm(self->S->H->env, self->S->err, self->lob, &self->csfrm);
+
+ stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
+
+ if (stm) {
+ zend_object *obj;
+ obj = &stmt->std;
+ Z_ADDREF(self->dbh);
+ GC_ADDREF(obj);
+ return stm;
+ }
+
+ efree(self);
+ return NULL;
+}
+
+static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) /* {{{ */
+{
+ pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
+ pdo_oci_column *C = &S->cols[colno];
+
+ /* check the indicator to ensure that the data is intact */
+ if (C->indicator == -1) {
+ /* A NULL value */
+ ZVAL_NULL(result);
+ return 1;
+ } else if (C->indicator == 0) {
+ /* it was stored perfectly */
+
+ if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) {
+ if (C->data) {
+ php_stream *stream = oci_create_lob_stream(stmt, (OCILobLocator*)C->data);
+ OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY);
+ php_stream_to_zval(stream, result);
+ return 1;
+ }
+ return 0;
+ }
+
+ ZVAL_STRINGL_FAST(result, C->data, C->fetched_len);
+ return 1;
+ } else {
+ /* it was truncated */
+ php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno);
+ ZVAL_STRINGL(result, C->data, C->fetched_len);
+ return 1;
+ }
+} /* }}} */
+
+
+static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */
+{
+ pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
+ OCIParam *param = NULL;
+ ub2 dtype, precis;
+ sb1 scale;
+ zval flags;
+ ub1 isnull, charset_form;
+ if (!S->stmt) {
+ return FAILURE;
+ }
+ if (colno >= stmt->column_count) {
+ /* error invalid column */
+ return FAILURE;
+ }
+
+ array_init(return_value);
+ array_init(&flags);
+
+ /* describe the column */
+ STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1));
+
+ /* column data type */
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
+ (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
+
+ /* column precision */
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
+ (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
+
+ /* column scale */
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
+ (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
+
+ /* string column charset form */
+ if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) {
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_CHARSET_FORM",
+ (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err));
+ }
+
+
+ if (dtype) {
+ /* if there is a declared type */
+ switch (dtype) {
+#ifdef SQLT_TIMESTAMP
+ case SQLT_TIMESTAMP:
+ add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP");
+ add_assoc_string(return_value, "native_type", "TIMESTAMP");
+ break;
+#endif
+#ifdef SQLT_TIMESTAMP_TZ
+ case SQLT_TIMESTAMP_TZ:
+ add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE");
+ add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE");
+ break;
+#endif
+#ifdef SQLT_TIMESTAMP_LTZ
+ case SQLT_TIMESTAMP_LTZ:
+ add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE");
+ add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE");
+ break;
+#endif
+#ifdef SQLT_INTERVAL_YM
+ case SQLT_INTERVAL_YM:
+ add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH");
+ add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH");
+ break;
+#endif
+#ifdef SQLT_INTERVAL_DS
+ case SQLT_INTERVAL_DS:
+ add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND");
+ add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND");
+ break;
+#endif
+ case SQLT_DAT:
+ add_assoc_string(return_value, "oci:decl_type", "DATE");
+ add_assoc_string(return_value, "native_type", "DATE");
+ break;
+ case SQLT_FLT :
+ case SQLT_NUM:
+ /* if the precision is nonzero and scale is -127 then it is a FLOAT */
+ if (scale == -127 && precis != 0) {
+ add_assoc_string(return_value, "oci:decl_type", "FLOAT");
+ add_assoc_string(return_value, "native_type", "FLOAT");
+ } else {
+ add_assoc_string(return_value, "oci:decl_type", "NUMBER");
+ add_assoc_string(return_value, "native_type", "NUMBER");
+ }
+ break;
+ case SQLT_LNG:
+ add_assoc_string(return_value, "oci:decl_type", "LONG");
+ add_assoc_string(return_value, "native_type", "LONG");
+ break;
+ case SQLT_BIN:
+ add_assoc_string(return_value, "oci:decl_type", "RAW");
+ add_assoc_string(return_value, "native_type", "RAW");
+ break;
+ case SQLT_LBI:
+ add_assoc_string(return_value, "oci:decl_type", "LONG RAW");
+ add_assoc_string(return_value, "native_type", "LONG RAW");
+ break;
+ case SQLT_CHR:
+ case SQLT_VCS:
+ if (charset_form == SQLCS_NCHAR) {
+ add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2");
+ add_assoc_string(return_value, "native_type", "NVARCHAR2");
+ } else {
+ add_assoc_string(return_value, "oci:decl_type", "VARCHAR2");
+ add_assoc_string(return_value, "native_type", "VARCHAR2");
+ }
+ break;
+ case SQLT_AFC:
+ if (charset_form == SQLCS_NCHAR) {
+ add_assoc_string(return_value, "oci:decl_type", "NCHAR");
+ add_assoc_string(return_value, "native_type", "NCHAR");
+ } else {
+ add_assoc_string(return_value, "oci:decl_type", "CHAR");
+ add_assoc_string(return_value, "native_type", "CHAR");
+ }
+ break;
+ case SQLT_BLOB:
+ add_assoc_string(return_value, "oci:decl_type", "BLOB");
+ add_next_index_string(&flags, "blob");
+ add_assoc_string(return_value, "native_type", "BLOB");
+ break;
+ case SQLT_CLOB:
+ if (charset_form == SQLCS_NCHAR) {
+ add_assoc_string(return_value, "oci:decl_type", "NCLOB");
+ add_assoc_string(return_value, "native_type", "NCLOB");
+ } else {
+ add_assoc_string(return_value, "oci:decl_type", "CLOB");
+ add_assoc_string(return_value, "native_type", "CLOB");
+ }
+ add_next_index_string(&flags, "blob");
+ break;
+ case SQLT_BFILE:
+ add_assoc_string(return_value, "oci:decl_type", "BFILE");
+ add_next_index_string(&flags, "blob");
+ add_assoc_string(return_value, "native_type", "BFILE");
+ break;
+ case SQLT_RDD:
+ add_assoc_string(return_value, "oci:decl_type", "ROWID");
+ add_assoc_string(return_value, "native_type", "ROWID");
+ break;
+ case SQLT_BFLOAT:
+ case SQLT_IBFLOAT:
+ add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT");
+ add_assoc_string(return_value, "native_type", "BINARY_FLOAT");
+ break;
+ case SQLT_BDOUBLE:
+ case SQLT_IBDOUBLE:
+ add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE");
+ add_assoc_string(return_value, "native_type", "BINARY_DOUBLE");
+ break;
+ default:
+ add_assoc_long(return_value, "oci:decl_type", dtype);
+ add_assoc_string(return_value, "native_type", "UNKNOWN");
+ }
+ } else {
+ /* if the column is NULL */
+ add_assoc_long(return_value, "oci:decl_type", 0);
+ add_assoc_string(return_value, "native_type", "NULL");
+ }
+
+ switch (dtype) {
+ case SQLT_BLOB:
+ case SQLT_CLOB:
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB);
+ break;
+ default:
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
+ break;
+ }
+
+ /* column can be null */
+ STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL",
+ (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err));
+
+ if (isnull) {
+ add_next_index_string(&flags, "nullable");
+ } else {
+ add_next_index_string(&flags, "not_null");
+ }
+
+ /* PDO type */
+ switch (dtype) {
+ case SQLT_BFILE:
+ case SQLT_BLOB:
+ case SQLT_CLOB:
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB);
+ break;
+ default:
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
+ }
+
+ add_assoc_long(return_value, "scale", scale);
+ add_assoc_zval(return_value, "flags", &flags);
+
+ OCIDescriptorFree(param, OCI_DTYPE_PARAM);
+ return SUCCESS;
+} /* }}} */
+
+const struct pdo_stmt_methods oci_stmt_methods = {
+ oci_stmt_dtor,
+ oci_stmt_execute,
+ oci_stmt_fetch,
+ oci_stmt_describe,
+ oci_stmt_get_col,
+ oci_stmt_param_hook,
+ NULL, /* set_attr */
+ NULL, /* get_attr */
+ oci_stmt_col_meta,
+ NULL,
+ NULL
+};
+#endif
\ No newline at end of file
diff --git a/thirdparty/php85/pdo_oci/php_pdo_oci.h b/thirdparty/php85/pdo_oci/php_pdo_oci.h
new file mode 100644
index 00000000000..7096823ad2d
--- /dev/null
+++ b/thirdparty/php85/pdo_oci/php_pdo_oci.h
@@ -0,0 +1,36 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_PDO_OCI_H
+#define PHP_PDO_OCI_H
+
+extern zend_module_entry pdo_oci_module_entry;
+#define phpext_pdo_oci_ptr &pdo_oci_module_entry
+
+#include "php_version.h"
+#define PHP_PDO_OCI_VERSION "1.1.0"
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(pdo_oci);
+PHP_MSHUTDOWN_FUNCTION(pdo_oci);
+PHP_RINIT_FUNCTION(pdo_oci);
+PHP_RSHUTDOWN_FUNCTION(pdo_oci);
+PHP_MINFO_FUNCTION(pdo_oci);
+
+#endif /* PHP_PDO_OCI_H */
diff --git a/thirdparty/php85/pdo_oci/php_pdo_oci_int.h b/thirdparty/php85/pdo_oci/php_pdo_oci_int.h
new file mode 100644
index 00000000000..dd513ff94e0
--- /dev/null
+++ b/thirdparty/php85/pdo_oci/php_pdo_oci_int.h
@@ -0,0 +1,116 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_PDO_OCI_INT_H
+#define PHP_PDO_OCI_INT_H
+
+#include "zend_portability.h"
+
+ZEND_DIAGNOSTIC_IGNORED_START("-Wstrict-prototypes")
+#include
+ZEND_DIAGNOSTIC_IGNORED_END
+
+typedef struct {
+ const char *file;
+ int line;
+ sb4 errcode;
+ char *errmsg;
+} pdo_oci_error_info;
+
+/* stuff we use in an OCI database handle */
+typedef struct {
+ OCIServer *server;
+ OCISession *session;
+ OCIEnv *env;
+ OCIError *err;
+ OCISvcCtx *svc;
+ /* OCI9; 0 == use NLS_LANG */
+ ub4 prefetch;
+ ub2 charset;
+ sword last_err;
+ sb4 max_char_width;
+
+ unsigned attached:1;
+ unsigned _reserved:31;
+
+ pdo_oci_error_info einfo;
+} pdo_oci_db_handle;
+
+typedef struct {
+ OCIDefine *def;
+ ub2 fetched_len;
+ ub2 retcode;
+ sb2 indicator;
+
+ char *data;
+ ub4 datalen;
+
+ ub2 dtype;
+
+} pdo_oci_column;
+
+typedef struct {
+ pdo_oci_db_handle *H;
+ OCIStmt *stmt;
+ OCIError *err;
+ sword last_err;
+ ub2 stmt_type;
+ ub4 exec_type;
+ pdo_oci_column *cols;
+ pdo_oci_error_info einfo;
+ unsigned int have_blobs:1;
+} pdo_oci_stmt;
+
+typedef struct {
+ OCIBind *bind; /* allocated by OCI */
+ sb2 oci_type;
+ sb2 indicator;
+ ub2 retcode;
+
+ ub4 actual_len;
+
+ dvoid *thing; /* for LOBS, REFCURSORS etc. */
+
+ unsigned used_for_output;
+} pdo_oci_bound_param;
+
+extern const ub4 PDO_OCI_INIT_MODE;
+extern const pdo_driver_t pdo_oci_driver;
+extern OCIEnv *pdo_oci_Env;
+
+ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line);
+#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__)
+#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__)
+#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__)
+
+extern const struct pdo_stmt_methods oci_stmt_methods;
+
+/* Default prefetch size in number of rows */
+#define PDO_OCI_PREFETCH_DEFAULT 100
+
+/* Arbitrary assumed row length for prefetch memory limit calcuation */
+#define PDO_OCI_PREFETCH_ROWSIZE 1024
+
+
+enum {
+ PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC,
+ PDO_OCI_ATTR_CLIENT_INFO,
+ PDO_OCI_ATTR_CLIENT_IDENTIFIER,
+ PDO_OCI_ATTR_MODULE,
+ PDO_OCI_ATTR_CALL_TIMEOUT
+};
+
+#endif /* PHP_PDO_OCI_INT_H */
diff --git a/thirdparty/php85/pdo_odbc/odbc_driver.c b/thirdparty/php85/pdo_odbc/odbc_driver.c
new file mode 100644
index 00000000000..854de1d704c
--- /dev/null
+++ b/thirdparty/php85/pdo_odbc/odbc_driver.c
@@ -0,0 +1,632 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+#define SW_USE_ODBC_HOOK
+#include "php_swoole_odbc.h"
+
+#if PHP_VERSION_ID >= 80500
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "ext/pdo/php_pdo.h"
+#include "ext/pdo/php_pdo_driver.h"
+/* this file actually lives in main/ */
+#include "php_odbc_utils.h"
+#include "php_pdo_odbc.h"
+//#include "php_pdo_odbc_int.h"
+#include "zend_exceptions.h"
+
+static void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info)
+{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ pdo_odbc_errinfo *einfo = &H->einfo;
+ pdo_odbc_stmt *S = NULL;
+ zend_string *message = NULL;
+
+ if (stmt) {
+ S = (pdo_odbc_stmt*)stmt->driver_data;
+ einfo = &S->einfo;
+ }
+
+ /* If we don't have a driver error do not populate the info array */
+ if (strlen(einfo->last_err_msg) == 0) {
+ return;
+ }
+
+ message = strpprintf(0, "%s (%s[%ld] at %s:%d)",
+ einfo->last_err_msg,
+ einfo->what, (long) einfo->last_error,
+ einfo->file, einfo->line);
+
+ add_next_index_long(info, einfo->last_error);
+ add_next_index_str(info, message);
+ add_next_index_string(info, einfo->last_state);
+}
+
+
+void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */
+{
+ SQLRETURN rc;
+ SQLSMALLINT errmsgsize = 0;
+ SQLHANDLE eh;
+ SQLSMALLINT htype, recno = 1;
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
+ pdo_odbc_errinfo *einfo = &H->einfo;
+ pdo_odbc_stmt *S = NULL;
+ pdo_error_type *pdo_err = &dbh->error_code;
+
+ if (stmt) {
+ S = (pdo_odbc_stmt*)stmt->driver_data;
+
+ einfo = &S->einfo;
+ pdo_err = &stmt->error_code;
+ }
+
+ if (statement == SQL_NULL_HSTMT && S) {
+ statement = S->stmt;
+ }
+
+ if (statement) {
+ htype = SQL_HANDLE_STMT;
+ eh = statement;
+ } else if (H->dbc) {
+ htype = SQL_HANDLE_DBC;
+ eh = H->dbc;
+ } else {
+ htype = SQL_HANDLE_ENV;
+ eh = H->env;
+ }
+
+ rc = SQLGetDiagRec(htype, eh, recno++, (SQLCHAR *) einfo->last_state, &einfo->last_error,
+ (SQLCHAR *) einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize);
+
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ errmsgsize = 0;
+ }
+
+ einfo->last_err_msg[errmsgsize] = '\0';
+ einfo->file = file;
+ einfo->line = line;
+ einfo->what = what;
+
+ strcpy(*pdo_err, einfo->last_state);
+/* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */
+ if (!dbh->methods) {
+ zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error, "SQLSTATE[%s] %s: %d %s",
+ *pdo_err, what, einfo->last_error, einfo->last_err_msg);
+ }
+
+ /* just like a cursor, once you start pulling, you need to keep
+ * going until the end; SQL Server (at least) will mess with the
+ * actual cursor state if you don't finish retrieving all the
+ * diagnostic records (which can be generated by PRINT statements
+ * in the query, for instance). */
+ while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
+ SQLCHAR discard_state[6];
+ SQLCHAR discard_buf[1024];
+ SQLINTEGER code;
+ rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code,
+ discard_buf, sizeof(discard_buf)-1, &errmsgsize);
+ }
+
+}
+/* }}} */
+
+static void odbc_handle_closer(pdo_dbh_t *dbh)
+{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
+
+ if (H->dbc != SQL_NULL_HANDLE) {
+ SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
+ SQLDisconnect(H->dbc);
+ SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);
+ H->dbc = NULL;
+ }
+ SQLFreeHandle(SQL_HANDLE_ENV, H->env);
+ H->env = NULL;
+ pefree(H, dbh->is_persistent);
+ dbh->driver_data = NULL;
+}
+
+static bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)
+{
+ RETCODE rc;
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));
+ enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;
+ int ret;
+ zend_string *nsql = NULL;
+
+ S->H = H;
+ S->assume_utf8 = H->assume_utf8;
+
+ /* before we prepare, we need to peek at the query; if it uses named parameters,
+ * we want PDO to rewrite them for us */
+ stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
+ ret = pdo_parse_params(stmt, sql, &nsql);
+
+ if (ret == 1) {
+ /* query was re-written */
+ sql = nsql;
+ } else if (ret == -1) {
+ /* couldn't grok it */
+ strcpy(dbh->error_code, stmt->error_code);
+ efree(S);
+ return false;
+ }
+
+ rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);
+
+ if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {
+ efree(S);
+ if (nsql) {
+ zend_string_release(nsql);
+ }
+ pdo_odbc_drv_error("SQLAllocStmt");
+ return false;
+ }
+
+ stmt->driver_data = S;
+
+ cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY);
+ if (cursor_type != PDO_CURSOR_FWDONLY) {
+ rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0);
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE");
+ SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
+ if (nsql) {
+ zend_string_release(nsql);
+ }
+ return false;
+ }
+ }
+
+ rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS);
+ if (nsql) {
+ zend_string_release(nsql);
+ }
+
+ stmt->methods = &odbc_stmt_methods;
+
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_stmt_error("SQLPrepare");
+ if (rc != SQL_SUCCESS_WITH_INFO) {
+ /* clone error information into the db handle */
+ strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);
+ H->einfo.file = S->einfo.file;
+ H->einfo.line = S->einfo.line;
+ H->einfo.what = S->einfo.what;
+ strcpy(dbh->error_code, stmt->error_code);
+ }
+ }
+
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ return false;
+ }
+ return true;
+}
+
+static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)
+{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ RETCODE rc;
+ SQLLEN row_count = -1;
+ PDO_ODBC_HSTMT stmt;
+
+ rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_drv_error("SQLAllocHandle: STMT");
+ return -1;
+ }
+
+ rc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql));
+
+ if (rc == SQL_NO_DATA) {
+ /* If SQLExecDirect executes a searched update or delete statement that
+ * does not affect any rows at the data source, the call to
+ * SQLExecDirect returns SQL_NO_DATA. */
+ row_count = 0;
+ goto out;
+ }
+
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_doer_error("SQLExecDirect");
+ goto out;
+ }
+
+ rc = SQLRowCount(stmt, &row_count);
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_doer_error("SQLRowCount");
+ goto out;
+ }
+ if (row_count == -1) {
+ row_count = 0;
+ }
+out:
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return row_count;
+}
+
+/* TODO: Do ODBC quoter
+static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type param_type )
+{
+ // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ // TODO: figure it out
+ return 0;
+}
+*/
+
+static bool odbc_handle_begin(pdo_dbh_t *dbh)
+{
+ if (dbh->auto_commit) {
+ /* we need to disable auto-commit now, to be able to initiate a transaction */
+ RETCODE rc;
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+
+ rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF");
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool odbc_handle_commit(pdo_dbh_t *dbh)
+{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ RETCODE rc;
+
+ rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);
+
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_drv_error("SQLEndTran: Commit");
+
+ if (rc != SQL_SUCCESS_WITH_INFO) {
+ return false;
+ }
+ }
+
+ if (dbh->auto_commit) {
+ /* turn auto-commit back on again */
+ rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool odbc_handle_rollback(pdo_dbh_t *dbh)
+{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ RETCODE rc;
+
+ rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
+
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_drv_error("SQLEndTran: Rollback");
+
+ if (rc != SQL_SUCCESS_WITH_INFO) {
+ return false;
+ }
+ }
+ if (dbh->auto_commit && H->dbc) {
+ /* turn auto-commit back on again */
+ rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
+{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ bool bval;
+
+ switch (attr) {
+ case PDO_ODBC_ATTR_ASSUME_UTF8:
+ if (!pdo_get_bool_param(&bval, val)) {
+ return false;
+ }
+ H->assume_utf8 = bval;
+ return true;
+ case PDO_ATTR_AUTOCOMMIT:
+ if (!pdo_get_bool_param(&bval, val)) {
+ return false;
+ }
+ if (dbh->in_txn) {
+ pdo_raise_impl_error(dbh, NULL, "HY000", "Cannot change autocommit mode while a transaction is already open");
+ return false;
+ }
+ if (dbh->auto_commit ^ bval) {
+ dbh->auto_commit = bval;
+ RETCODE rc = SQLSetConnectAttr(
+ H->dbc,
+ SQL_ATTR_AUTOCOMMIT,
+ dbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF,
+ SQL_IS_INTEGER
+ );
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_drv_error(
+ dbh->auto_commit ? "SQLSetConnectAttr AUTOCOMMIT = ON" : "SQLSetConnectAttr AUTOCOMMIT = OFF"
+ );
+ return false;
+ }
+ }
+ return true;
+ default:
+ strcpy(H->einfo.last_err_msg, "Unknown Attribute");
+ H->einfo.what = "setAttribute";
+ strcpy(H->einfo.last_state, "IM001");
+ return false;
+ }
+}
+
+static int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val)
+{
+ RETCODE rc;
+ SQLSMALLINT out_len;
+ char buf[256];
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ rc = SQLGetInfo(H->dbc, type, (SQLPOINTER)buf, sizeof(buf), &out_len);
+ /* returning -1 is treated as an error, not as unsupported */
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ return -1;
+ }
+ ZVAL_STRINGL(val, buf, out_len);
+ return 1;
+}
+
+static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
+{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ switch (attr) {
+ case PDO_ATTR_CLIENT_VERSION:
+ ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE);
+ return 1;
+
+ case PDO_ATTR_SERVER_VERSION:
+ return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val);
+ case PDO_ATTR_SERVER_INFO:
+ return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val);
+ case PDO_ATTR_PREFETCH:
+ case PDO_ATTR_TIMEOUT:
+ case PDO_ATTR_CONNECTION_STATUS:
+ break;
+ case PDO_ODBC_ATTR_ASSUME_UTF8:
+ ZVAL_BOOL(val, H->assume_utf8);
+ return 1;
+ case PDO_ATTR_AUTOCOMMIT:
+ ZVAL_BOOL(val, dbh->auto_commit);
+ return 1;
+ }
+ return 0;
+}
+
+static zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh)
+{
+ RETCODE ret;
+ UCHAR d_name[32];
+ SQLSMALLINT len;
+ SQLUINTEGER dead = SQL_CD_FALSE;
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+
+ ret = SQLGetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL);
+ if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) {
+ /* Bail early here, since we know it's gone */
+ return FAILURE;
+ }
+ /*
+ * If the driver doesn't support SQL_ATTR_CONNECTION_DEAD, or if
+ * it returns false (which could be a false positive), fall back
+ * to using SQL_DATA_SOURCE_READ_ONLY, which isn't semantically
+ * correct, but works with many drivers.
+ */
+ ret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name,
+ sizeof(d_name), &len);
+
+ if (ret != SQL_SUCCESS || len == 0) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static const struct pdo_dbh_methods odbc_methods = {
+ odbc_handle_closer,
+ odbc_handle_preparer,
+ odbc_handle_doer,
+ NULL, /* quoter */
+ odbc_handle_begin,
+ odbc_handle_commit,
+ odbc_handle_rollback,
+ odbc_handle_set_attr,
+ NULL, /* last id */
+ pdo_odbc_fetch_error_func,
+ odbc_handle_get_attr, /* get attr */
+ odbc_handle_check_liveness, /* check_liveness */
+ NULL, /* get_driver_methods */
+ NULL, /* request_shutdown */
+ NULL, /* in transaction, use PDO's internal tracking mechanism */
+ NULL, /* get_gc */
+ NULL /* scanner */
+};
+
+static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
+{
+ pdo_odbc_db_handle *H;
+ RETCODE rc;
+ int use_direct = 0;
+ zend_ulong cursor_lib;
+
+ H = pecalloc(1, sizeof(*H), dbh->is_persistent);
+
+ dbh->driver_data = H;
+
+ rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_drv_error("SQLAllocHandle: ENV");
+ goto fail;
+ }
+
+ rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
+
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3");
+ goto fail;
+ }
+
+#ifdef SQL_ATTR_CONNECTION_POOLING
+ if (pdo_odbc_pool_on != SQL_CP_OFF) {
+ rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0);
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH");
+ goto fail;
+ }
+ }
+#endif
+
+ rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_drv_error("SQLAllocHandle: DBC");
+ goto fail;
+ }
+
+ rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER)(intptr_t)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER);
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT");
+ goto fail;
+ }
+
+ /* set up the cursor library, if needed, or if configured explicitly */
+ cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED);
+ rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER);
+ if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {
+ pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS");
+ goto fail;
+ }
+
+ /* a connection string may have = but not ; - i.e. "DSN=PHP" */
+ if (strchr(dbh->data_source, '=')) {
+ SQLCHAR dsnbuf[1024];
+ SQLSMALLINT dsnbuflen;
+
+ use_direct = 1;
+
+ bool use_uid_arg = dbh->username != NULL && !php_memnistr(dbh->data_source, "uid=", strlen("uid="), dbh->data_source + dbh->data_source_len);
+ bool use_pwd_arg = dbh->password != NULL && !php_memnistr(dbh->data_source, "pwd=", strlen("pwd="), dbh->data_source + dbh->data_source_len);
+
+ if (use_uid_arg || use_pwd_arg) {
+ char *db = (char*) estrndup(dbh->data_source, dbh->data_source_len);
+ char *db_end = db + dbh->data_source_len;
+ db_end--;
+ if ((unsigned char)*(db_end) == ';') {
+ *db_end = '\0';
+ }
+
+ char *uid = NULL, *pwd = NULL, *dsn = NULL;
+ bool should_quote_uid, should_quote_pwd;
+ size_t new_dsn_size;
+
+ if (use_uid_arg) {
+ should_quote_uid = !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username);
+ if (should_quote_uid) {
+ size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username);
+ uid = emalloc(estimated_length);
+ php_odbc_connstr_quote(uid, dbh->username, estimated_length);
+ } else {
+ uid = dbh->username;
+ }
+
+ if (!use_pwd_arg) {
+ new_dsn_size = strlen(db) + strlen(uid) + strlen(";UID=;") + 1;
+ dsn = pemalloc(new_dsn_size, dbh->is_persistent);
+ snprintf(dsn, new_dsn_size, "%s;UID=%s;", db, uid);
+ }
+ }
+
+ if (use_pwd_arg) {
+ should_quote_pwd = !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password);
+ if (should_quote_pwd) {
+ size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password);
+ pwd = emalloc(estimated_length);
+ php_odbc_connstr_quote(pwd, dbh->password, estimated_length);
+ } else {
+ pwd = dbh->password;
+ }
+
+ if (!use_uid_arg) {
+ new_dsn_size = strlen(db) + strlen(pwd) + strlen(";PWD=;") + 1;
+ dsn = pemalloc(new_dsn_size, dbh->is_persistent);
+ snprintf(dsn, new_dsn_size, "%s;PWD=%s;", db, pwd);
+ }
+ }
+
+ if (use_uid_arg && use_pwd_arg) {
+ new_dsn_size = strlen(db)
+ + strlen(uid) + strlen(pwd)
+ + strlen(";UID=;PWD=;") + 1;
+ dsn = pemalloc(new_dsn_size, dbh->is_persistent);
+ snprintf(dsn, new_dsn_size, "%s;UID=%s;PWD=%s;", db, uid, pwd);
+ }
+
+ pefree((char*)dbh->data_source, dbh->is_persistent);
+ dbh->data_source = dsn;
+ dbh->data_source_len = strlen(dsn);
+ if (uid && should_quote_uid) {
+ efree(uid);
+ }
+ if (pwd && should_quote_pwd) {
+ efree(pwd);
+ }
+ efree(db);
+ }
+
+ rc = SQLDriverConnect(H->dbc, NULL, (SQLCHAR *) dbh->data_source, dbh->data_source_len,
+ dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
+ }
+ if (!use_direct) {
+ rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS);
+ }
+
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect");
+ goto fail;
+ }
+
+ /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */
+
+ dbh->methods = &odbc_methods;
+ dbh->alloc_own_columns = 1;
+
+ return 1;
+
+fail:
+ dbh->methods = &odbc_methods;
+ return 0;
+}
+/* }}} */
+
+const pdo_driver_t swoole_pdo_odbc_driver = {
+ PDO_DRIVER_HEADER(odbc),
+ pdo_odbc_handle_factory
+};
+#endif
\ No newline at end of file
diff --git a/thirdparty/php85/pdo_odbc/odbc_stmt.c b/thirdparty/php85/pdo_odbc/odbc_stmt.c
new file mode 100644
index 00000000000..a579b2aa7b3
--- /dev/null
+++ b/thirdparty/php85/pdo_odbc/odbc_stmt.c
@@ -0,0 +1,898 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#define SW_USE_ODBC_HOOK
+#include "php_swoole_odbc.h"
+
+#if PHP_VERSION_ID >= 80500
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "ext/pdo/php_pdo.h"
+#include "ext/pdo/php_pdo_driver.h"
+#include "php_pdo_odbc.h"
+//#include "php_pdo_odbc_int.h"
+
+/* Buffer size; bigger columns than this become a "long column" */
+#define LONG_COLUMN_BUFFER_SIZE (ZEND_MM_PAGE_SIZE- ZSTR_MAX_OVERHEAD)
+
+enum pdo_odbc_conv_result {
+ PDO_ODBC_CONV_NOT_REQUIRED,
+ PDO_ODBC_CONV_OK,
+ PDO_ODBC_CONV_FAIL
+};
+
+static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype)
+{
+ if (!S->assume_utf8) return 0;
+ switch (sqltype) {
+#ifdef SQL_WCHAR
+ case SQL_WCHAR:
+ return 1;
+#endif
+#ifdef SQL_WLONGVARCHAR
+ case SQL_WLONGVARCHAR:
+ return 1;
+#endif
+#ifdef SQL_WVARCHAR
+ case SQL_WVARCHAR:
+ return 1;
+#endif
+ default:
+ return 0;
+ }
+}
+
+static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf,
+ zend_ulong buflen, zend_ulong *outlen)
+{
+#ifdef PHP_WIN32
+ if (is_unicode && buflen) {
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+ DWORD ret;
+
+ ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);
+ if (ret == 0) {
+ /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/
+ return PDO_ODBC_CONV_FAIL;
+ }
+
+ ret *= sizeof(WCHAR);
+
+ if (S->convbufsize <= ret) {
+ S->convbufsize = ret + sizeof(WCHAR);
+ S->convbuf = erealloc(S->convbuf, S->convbufsize);
+ }
+
+ ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR));
+ if (ret == 0) {
+ /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/
+ return PDO_ODBC_CONV_FAIL;
+ }
+
+ ret *= sizeof(WCHAR);
+ *outlen = ret;
+ return PDO_ODBC_CONV_OK;
+ }
+#endif
+ return PDO_ODBC_CONV_NOT_REQUIRED;
+}
+
+static int pdo_odbc_ucs22utf8(int is_unicode, zval *result)
+{
+#ifdef PHP_WIN32
+ ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING);
+ if (is_unicode && Z_STRLEN_P(result) != 0) {
+ DWORD ret;
+
+ ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), NULL, 0, NULL, NULL);
+ if (ret == 0) {
+ return PDO_ODBC_CONV_FAIL;
+ }
+
+ zend_string *str = zend_string_alloc(ret, 0);
+ ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), ZSTR_VAL(str), ZSTR_LEN(str), NULL, NULL);
+ if (ret == 0) {
+ zend_string_efree(str);
+ return PDO_ODBC_CONV_FAIL;
+ }
+
+ ZSTR_VAL(str)[ret] = '\0';
+ zval_ptr_dtor_str(result);
+ ZVAL_STR(result, str);
+ return PDO_ODBC_CONV_OK;
+ }
+#endif
+ return PDO_ODBC_CONV_NOT_REQUIRED;
+}
+
+static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S)
+{
+ if (S->cols) {
+ int i;
+
+ for (i = 0; i < S->col_count; i++) {
+ if (S->cols[i].data) {
+ efree(S->cols[i].data);
+ }
+ }
+ efree(S->cols);
+ S->cols = NULL;
+ S->col_count = 0;
+ }
+}
+
+static int odbc_stmt_dtor(pdo_stmt_t *stmt)
+{
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+
+ if (S->stmt != SQL_NULL_HANDLE && php_pdo_stmt_valid_db_obj_handle(stmt)) {
+ if (stmt->executed) {
+ SQLCloseCursor(S->stmt);
+ }
+ SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
+ S->stmt = SQL_NULL_HANDLE;
+ }
+
+ free_cols(stmt, S);
+ if (S->convbuf) {
+ efree(S->convbuf);
+ }
+ efree(S);
+
+ return 1;
+}
+
+static int odbc_stmt_execute(pdo_stmt_t *stmt)
+{
+ RETCODE rc, rc1;
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+ char *buf = NULL;
+ SQLLEN row_count = -1;
+
+ if (stmt->executed) {
+ SQLCloseCursor(S->stmt);
+ }
+
+ rc = SQLExecute(S->stmt);
+
+ while (rc == SQL_NEED_DATA) {
+ struct pdo_bound_param_data *param;
+
+ rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m);
+ if (rc == SQL_NEED_DATA) {
+ php_stream *stm;
+ int len;
+ pdo_odbc_param *P;
+ zval *parameter;
+
+ P = (pdo_odbc_param*)param->driver_data;
+ if (Z_ISREF(param->parameter)) {
+ parameter = Z_REFVAL(param->parameter);
+ } else {
+ parameter = ¶m->parameter;
+ }
+ if (Z_TYPE_P(parameter) != IS_RESOURCE) {
+ /* they passed in a string */
+ zend_ulong ulen;
+ convert_to_string(parameter);
+
+ switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
+ Z_STRVAL_P(parameter),
+ Z_STRLEN_P(parameter),
+ &ulen)) {
+ case PDO_ODBC_CONV_NOT_REQUIRED:
+ rc1 = SQLPutData(S->stmt, Z_STRVAL_P(parameter),
+ Z_STRLEN_P(parameter));
+ if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {
+ rc = rc1;
+ }
+ break;
+ case PDO_ODBC_CONV_OK:
+ rc1 = SQLPutData(S->stmt, S->convbuf, ulen);
+ if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {
+ rc = rc1;
+ }
+ break;
+ case PDO_ODBC_CONV_FAIL:
+ pdo_odbc_stmt_error("error converting input string");
+ SQLCloseCursor(S->stmt);
+ if (buf) {
+ efree(buf);
+ }
+ return 0;
+ }
+ continue;
+ }
+
+ /* we assume that LOBs are binary and don't need charset
+ * conversion */
+
+ php_stream_from_zval_no_verify(stm, parameter);
+ if (!stm) {
+ /* shouldn't happen either */
+ pdo_odbc_stmt_error("input LOB is no longer a stream");
+ SQLCloseCursor(S->stmt);
+ if (buf) {
+ efree(buf);
+ }
+ return 0;
+ }
+
+ /* now suck data from the stream and stick it into the database */
+ if (buf == NULL) {
+ buf = emalloc(8192);
+ }
+
+ do {
+ len = php_stream_read(stm, buf, 8192);
+ if (len == 0) {
+ break;
+ }
+ rc1 = SQLPutData(S->stmt, buf, len);
+ if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {
+ rc = rc1;
+ }
+ } while (1);
+ }
+ }
+
+ if (buf) {
+ efree(buf);
+ }
+
+ switch (rc) {
+ case SQL_SUCCESS:
+ break;
+ case SQL_NO_DATA_FOUND:
+ case SQL_SUCCESS_WITH_INFO:
+ pdo_odbc_stmt_error("SQLExecute");
+ break;
+
+ default:
+ pdo_odbc_stmt_error("SQLExecute");
+ return 0;
+ }
+
+ SQLRowCount(S->stmt, &row_count);
+ stmt->row_count = row_count;
+
+ if (S->cols == NULL) {
+ /* do first-time-only definition of bind/mapping stuff */
+ SQLSMALLINT colcount;
+
+ /* how many columns do we have ? */
+ SQLNumResultCols(S->stmt, &colcount);
+
+ stmt->column_count = S->col_count = (int)colcount;
+ S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
+ S->going_long = 0;
+ }
+
+ return 1;
+}
+
+static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
+ enum pdo_param_event event_type)
+{
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+ RETCODE rc;
+ SQLSMALLINT sqltype = 0, ctype = 0, scale = 0, nullable = 0;
+ SQLULEN precision = 0;
+ pdo_odbc_param *P;
+ zval *parameter;
+
+ /* we're only interested in parameters for prepared SQL right now */
+ if (param->is_param) {
+
+ switch (event_type) {
+ case PDO_PARAM_EVT_FETCH_PRE:
+ case PDO_PARAM_EVT_FETCH_POST:
+ case PDO_PARAM_EVT_NORMALIZE:
+ /* Do nothing */
+ break;
+
+ case PDO_PARAM_EVT_FREE:
+ P = param->driver_data;
+ if (P) {
+ efree(P);
+ }
+ break;
+
+ case PDO_PARAM_EVT_ALLOC:
+ {
+ /* figure out what we're doing */
+ switch (PDO_PARAM_TYPE(param->param_type)) {
+ case PDO_PARAM_LOB:
+ break;
+
+ case PDO_PARAM_STMT:
+ return 0;
+
+ default:
+ break;
+ }
+
+ rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable);
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ /* MS Access, for instance, doesn't support SQLDescribeParam,
+ * so we need to guess */
+ switch (PDO_PARAM_TYPE(param->param_type)) {
+ case PDO_PARAM_INT:
+ sqltype = SQL_INTEGER;
+ break;
+ case PDO_PARAM_LOB:
+ sqltype = SQL_LONGVARBINARY;
+ break;
+ default:
+ sqltype = SQL_LONGVARCHAR;
+ }
+ precision = 4000;
+ scale = 5;
+ nullable = 1;
+
+ if (param->max_value_len > 0) {
+ precision = param->max_value_len;
+ }
+ }
+ if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {
+ ctype = SQL_C_BINARY;
+ } else {
+ ctype = SQL_C_CHAR;
+ }
+
+ P = emalloc(sizeof(*P));
+ param->driver_data = P;
+
+ P->len = 0; /* is re-populated each EXEC_PRE */
+ P->outbuf = NULL;
+
+ P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);
+ if (P->is_unicode) {
+ /* avoid driver auto-translation: we'll do it ourselves */
+ ctype = SQL_C_BINARY;
+ }
+
+ if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {
+ P->paramtype = SQL_PARAM_INPUT_OUTPUT;
+ } else if (param->max_value_len <= 0) {
+ P->paramtype = SQL_PARAM_INPUT;
+ } else {
+ P->paramtype = SQL_PARAM_OUTPUT;
+ }
+
+ if (P->paramtype != SQL_PARAM_INPUT) {
+ if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {
+ /* need an explicit buffer to hold result */
+ P->len = param->max_value_len > 0 ? param->max_value_len : precision;
+ if (P->is_unicode) {
+ P->len *= 2;
+ }
+ P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
+ }
+ }
+
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {
+ pdo_odbc_stmt_error("Can't bind a lob for output");
+ return 0;
+ }
+
+ rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1,
+ P->paramtype, ctype, sqltype, precision, scale,
+ P->paramtype == SQL_PARAM_INPUT ?
+ (SQLPOINTER)param :
+ P->outbuf,
+ P->len,
+ &P->len
+ );
+
+ if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
+ return 1;
+ }
+ pdo_odbc_stmt_error("SQLBindParameter");
+ return 0;
+ }
+
+ case PDO_PARAM_EVT_EXEC_PRE:
+ P = param->driver_data;
+ if (!Z_ISREF(param->parameter)) {
+ parameter = ¶m->parameter;
+ } else {
+ parameter = Z_REFVAL(param->parameter);
+ }
+
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
+ if (Z_TYPE_P(parameter) == IS_RESOURCE) {
+ php_stream *stm;
+ php_stream_statbuf sb;
+
+ php_stream_from_zval_no_verify(stm, parameter);
+
+ if (!stm) {
+ return 0;
+ }
+
+ if (0 == php_stream_stat(stm, &sb)) {
+ if (P->outbuf) {
+ int len, amount;
+ char *ptr = P->outbuf;
+ char *end = P->outbuf + P->len;
+
+ P->len = 0;
+ do {
+ amount = end - ptr;
+ if (amount == 0) {
+ break;
+ }
+ if (amount > 8192)
+ amount = 8192;
+ len = php_stream_read(stm, ptr, amount);
+ if (len == 0) {
+ break;
+ }
+ ptr += len;
+ P->len += len;
+ } while (1);
+
+ } else {
+ P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
+ }
+ } else {
+ if (P->outbuf) {
+ P->len = 0;
+ } else {
+ P->len = SQL_LEN_DATA_AT_EXEC(0);
+ }
+ }
+ } else {
+ convert_to_string(parameter);
+ if (P->outbuf) {
+ P->len = Z_STRLEN_P(parameter);
+ memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);
+ } else {
+ P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
+ }
+ }
+ } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {
+ P->len = SQL_NULL_DATA;
+ } else {
+ convert_to_string(parameter);
+ if (P->outbuf) {
+ zend_ulong ulen;
+ switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
+ Z_STRVAL_P(parameter),
+ Z_STRLEN_P(parameter),
+ &ulen)) {
+ case PDO_ODBC_CONV_FAIL:
+ case PDO_ODBC_CONV_NOT_REQUIRED:
+ P->len = Z_STRLEN_P(parameter);
+ memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);
+ break;
+ case PDO_ODBC_CONV_OK:
+ P->len = ulen;
+ memcpy(P->outbuf, S->convbuf, P->len);
+ break;
+ }
+ } else {
+ P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
+ }
+ }
+ return 1;
+
+ case PDO_PARAM_EVT_EXEC_POST:
+ P = param->driver_data;
+
+ if (P->outbuf) {
+ if (Z_ISREF(param->parameter)) {
+ parameter = Z_REFVAL(param->parameter);
+ } else {
+ parameter = ¶m->parameter;
+ }
+ zval_ptr_dtor(parameter);
+
+ if (P->len >= 0) {
+ ZVAL_STRINGL(parameter, P->outbuf, P->len);
+ switch (pdo_odbc_ucs22utf8(P->is_unicode, parameter)) {
+ case PDO_ODBC_CONV_FAIL:
+ /* something fishy, but allow it to come back as binary */
+ case PDO_ODBC_CONV_NOT_REQUIRED:
+ break;
+ case PDO_ODBC_CONV_OK:
+ break;
+ }
+ } else {
+ ZVAL_NULL(parameter);
+ }
+ }
+ return 1;
+ }
+ }
+ return 1;
+}
+
+static int odbc_stmt_fetch(pdo_stmt_t *stmt,
+ enum pdo_fetch_orientation ori, zend_long offset)
+{
+ RETCODE rc;
+ SQLSMALLINT odbcori;
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+
+ switch (ori) {
+ case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break;
+ case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break;
+ case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break;
+ case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break;
+ case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break;
+ case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break;
+ default:
+ strcpy(stmt->error_code, "HY106");
+ return 0;
+ }
+ rc = SQLFetchScroll(S->stmt, odbcori, offset);
+
+ if (rc == SQL_SUCCESS) {
+ return 1;
+ }
+ if (rc == SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_stmt_error("SQLFetchScroll");
+ return 1;
+ }
+
+ if (rc == SQL_NO_DATA) {
+ /* pdo_odbc_stmt_error("SQLFetchScroll"); */
+ return 0;
+ }
+
+ pdo_odbc_stmt_error("SQLFetchScroll");
+
+ return 0;
+}
+
+static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno)
+{
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+ struct pdo_column_data *col = &stmt->columns[colno];
+ RETCODE rc;
+ SQLSMALLINT colnamelen;
+ SQLULEN colsize;
+ SQLLEN displaysize = 0;
+
+ rc = SQLDescribeCol(S->stmt, colno+1, (SQLCHAR *) S->cols[colno].colname,
+ sizeof(S->cols[colno].colname)-1, &colnamelen,
+ &S->cols[colno].coltype, &colsize, NULL, NULL);
+
+ /* This fixes a known issue with SQL Server and (max) lengths,
+ may affect others as well. If we are SQL_VARCHAR,
+ SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations)
+ and zero is returned from colsize then consider it long */
+ if (0 == colsize &&
+ (S->cols[colno].coltype == SQL_VARCHAR ||
+ S->cols[colno].coltype == SQL_LONGVARCHAR ||
+#ifdef SQL_WVARCHAR
+ S->cols[colno].coltype == SQL_WVARCHAR ||
+#endif
+#ifdef SQL_WLONGVARCHAR
+ S->cols[colno].coltype == SQL_WLONGVARCHAR ||
+#endif
+ S->cols[colno].coltype == SQL_VARBINARY ||
+ S->cols[colno].coltype == SQL_LONGVARBINARY)) {
+ S->going_long = 1;
+ }
+
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_stmt_error("SQLDescribeCol");
+ if (rc != SQL_SUCCESS_WITH_INFO) {
+ return 0;
+ }
+ }
+
+ rc = SQLColAttribute(S->stmt, colno+1,
+ SQL_DESC_DISPLAY_SIZE,
+ NULL, 0, NULL, &displaysize);
+
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_stmt_error("SQLColAttribute");
+ if (rc != SQL_SUCCESS_WITH_INFO) {
+ return 0;
+ }
+ }
+ colsize = displaysize;
+
+ col->maxlen = S->cols[colno].datalen = colsize;
+ col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0);
+ S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
+
+ /* tell ODBC to put it straight into our buffer, but only if it
+ * isn't "long" data, and only if we haven't already bound a long
+ * column. */
+ if (colsize < LONG_COLUMN_BUFFER_SIZE && !S->going_long) {
+ S->cols[colno].data = emalloc(colsize+1);
+ S->cols[colno].is_long = 0;
+
+ rc = SQLBindCol(S->stmt, colno+1,
+ S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
+ S->cols[colno].data,
+ S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
+
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_stmt_error("SQLBindCol");
+ return 0;
+ }
+ } else {
+ /* allocate a smaller buffer to keep around for smaller
+ * "long" columns */
+ S->cols[colno].data = emalloc(LONG_COLUMN_BUFFER_SIZE);
+ S->going_long = 1;
+ S->cols[colno].is_long = 1;
+ }
+
+ return 1;
+}
+
+static int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
+{
+ array_init(return_value);
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
+ return 1;
+}
+
+static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)
+{
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+ pdo_odbc_column *C = &S->cols[colno];
+
+ /* if it is a column containing "long" data, perform late binding now */
+ if (C->is_long) {
+ SQLLEN orig_fetched_len = SQL_NULL_DATA;
+ RETCODE rc;
+
+ /* fetch it into C->data, which is allocated with a length
+ * of the page size minus zend_string overhead (LONG_COLUMN_BUFFER_SIZE);
+ * if there is more to be had, we then allocate
+ * bigger buffer for the caller to free */
+
+ rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
+ LONG_COLUMN_BUFFER_SIZE, &C->fetched_len);
+ orig_fetched_len = C->fetched_len;
+
+ if (rc == SQL_SUCCESS && C->fetched_len < LONG_COLUMN_BUFFER_SIZE) {
+ /* all the data fit into our little buffer;
+ * jump down to the generic bound data case */
+ goto in_data;
+ }
+
+ if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) {
+ /*
+ * This is a long column.
+ *
+ * Try to get as much as we can at once. If the
+ * driver somehow has more for us, get more. We'll
+ * assemble it into one big buffer at the end.
+ *
+ * N.B. with n and n+1 mentioned in the comments:
+ * n is the size returned without null terminator.
+ *
+ * The extension previously tried getting it in 256
+ * byte blocks, but this could have created trouble
+ * with some drivers.
+ *
+ * However, depending on the driver, fetched_len may
+ * not contain the number of bytes and SQL_NO_TOTAL
+ * may be passed.
+ * The behavior in this case is the same as before,
+ * dividing the data into blocks. However, it has been
+ * changed from 256 byte to LONG_COLUMN_BUFFER_SIZE.
+ */
+ ssize_t to_fetch_len;
+ if (orig_fetched_len == SQL_NO_TOTAL) {
+ to_fetch_len = C->datalen > (LONG_COLUMN_BUFFER_SIZE - 1) ? (LONG_COLUMN_BUFFER_SIZE - 1) : C->datalen;
+ } else {
+ to_fetch_len = orig_fetched_len;
+ }
+ ssize_t to_fetch_byte = to_fetch_len + 1;
+ char *buf2 = emalloc(to_fetch_byte);
+ zend_string *str = zend_string_init(C->data, to_fetch_byte, 0);
+ size_t used = to_fetch_len;
+
+ do {
+ C->fetched_len = 0;
+ /* read block. n + 1 bytes => n bytes are actually read, the last 1 is NULL */
+ rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, to_fetch_byte, &C->fetched_len);
+
+ /* adjust `used` in case we have proper length info from the driver */
+ if (orig_fetched_len >= 0 && C->fetched_len >= 0) {
+ SQLLEN fixed_used = orig_fetched_len - C->fetched_len;
+ if (fixed_used <= used + 1) {
+ used = fixed_used;
+ }
+ }
+
+ /* resize output buffer and reassemble block */
+ if (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > to_fetch_len)) {
+ /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
+ states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > n (greater than buf2's size)
+ (if a driver fails to follow that and wrote less than n bytes to buf2, this will AV or read garbage into buf) */
+ str = zend_string_realloc(str, used + to_fetch_byte, 0);
+ memcpy(ZSTR_VAL(str) + used, buf2, to_fetch_byte);
+ used = used + to_fetch_len;
+ } else if (rc==SQL_SUCCESS) {
+ str = zend_string_realloc(str, used + C->fetched_len, 0);
+ memcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len);
+ used = used + C->fetched_len;
+ } else {
+ /* includes SQL_NO_DATA */
+ break;
+ }
+
+ } while (1);
+
+ efree(buf2);
+
+ /* NULL terminate the buffer once, when finished, for use with the rest of PHP */
+ ZSTR_VAL(str)[used] = '\0';
+ ZVAL_STR(result, str);
+ if (C->is_unicode) {
+ goto unicode_conv;
+ }
+ return 1;
+ }
+
+ /* something went caca */
+ return 1;
+ }
+
+in_data:
+ /* check the indicator to ensure that the data is intact */
+ if (C->fetched_len == SQL_NULL_DATA) {
+ /* A NULL value */
+ ZVAL_NULL(result);
+ return 1;
+ } else if (C->fetched_len >= 0) {
+ /* it was stored perfectly */
+ ZVAL_STRINGL_FAST(result, C->data, C->fetched_len);
+ if (C->is_unicode) {
+ goto unicode_conv;
+ }
+ return 1;
+ } else {
+ /* no data? */
+ ZVAL_NULL(result);
+ return 1;
+ }
+
+unicode_conv:
+ switch (pdo_odbc_ucs22utf8(C->is_unicode, result)) {
+ case PDO_ODBC_CONV_FAIL:
+ /* oh well. They can have the binary version of it */
+ case PDO_ODBC_CONV_NOT_REQUIRED:
+ /* shouldn't happen... */
+ return 1;
+ case PDO_ODBC_CONV_OK:
+ return 1;
+ }
+ return 1;
+}
+
+static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val)
+{
+ SQLRETURN rc;
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+
+ switch (attr) {
+ case PDO_ATTR_CURSOR_NAME:
+ convert_to_string(val);
+ rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val));
+
+ if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
+ return 1;
+ }
+ pdo_odbc_stmt_error("SQLSetCursorName");
+ return 0;
+
+ case PDO_ODBC_ATTR_ASSUME_UTF8:
+ S->assume_utf8 = zval_is_true(val);
+ return 0;
+ default:
+ strcpy(S->einfo.last_err_msg, "Unknown Attribute");
+ S->einfo.what = "setAttribute";
+ strcpy(S->einfo.last_state, "IM001");
+ return -1;
+ }
+}
+
+static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val)
+{
+ SQLRETURN rc;
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+
+ switch (attr) {
+ case PDO_ATTR_CURSOR_NAME:
+ {
+ char buf[256];
+ SQLSMALLINT len = 0;
+ rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len);
+
+ if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
+ ZVAL_STRINGL(val, buf, len);
+ return 1;
+ }
+ pdo_odbc_stmt_error("SQLGetCursorName");
+ return 0;
+ }
+
+ case PDO_ODBC_ATTR_ASSUME_UTF8:
+ ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
+ return 0;
+
+ default:
+ strcpy(S->einfo.last_err_msg, "Unknown Attribute");
+ S->einfo.what = "getAttribute";
+ strcpy(S->einfo.last_state, "IM001");
+ return -1;
+ }
+}
+
+static int odbc_stmt_next_rowset(pdo_stmt_t *stmt)
+{
+ SQLRETURN rc;
+ SQLSMALLINT colcount;
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+
+ /* NOTE: can't guarantee that output or input/output parameters
+ * are set until this fella returns SQL_NO_DATA, according to
+ * MSDN ODBC docs */
+ rc = SQLMoreResults(S->stmt);
+
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ return 0;
+ }
+
+ free_cols(stmt, S);
+ /* how many columns do we have ? */
+ SQLNumResultCols(S->stmt, &colcount);
+ stmt->column_count = S->col_count = (int)colcount;
+ S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
+ S->going_long = 0;
+
+ return 1;
+}
+
+static int odbc_stmt_close_cursor(pdo_stmt_t *stmt)
+{
+ SQLRETURN rc;
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+
+ rc = SQLCloseCursor(S->stmt);
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ return 0;
+ }
+ return 1;
+}
+
+const struct pdo_stmt_methods odbc_stmt_methods = {
+ odbc_stmt_dtor,
+ odbc_stmt_execute,
+ odbc_stmt_fetch,
+ odbc_stmt_describe,
+ odbc_stmt_get_col,
+ odbc_stmt_param_hook,
+ odbc_stmt_set_param,
+ odbc_stmt_get_attr,
+ odbc_stmt_get_column_meta,
+ odbc_stmt_next_rowset,
+ odbc_stmt_close_cursor
+};
+#endif
\ No newline at end of file
diff --git a/thirdparty/php85/pdo_odbc/php_pdo_odbc.h b/thirdparty/php85/pdo_odbc/php_pdo_odbc.h
new file mode 100644
index 00000000000..3f041702978
--- /dev/null
+++ b/thirdparty/php85/pdo_odbc/php_pdo_odbc.h
@@ -0,0 +1,36 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_PDO_ODBC_H
+#define PHP_PDO_ODBC_H
+
+extern zend_module_entry pdo_odbc_module_entry;
+#define phpext_pdo_odbc_ptr &pdo_odbc_module_entry
+
+#include "php_version.h"
+#define PHP_PDO_ODBC_VERSION PHP_VERSION
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(pdo_odbc);
+PHP_MSHUTDOWN_FUNCTION(pdo_odbc);
+PHP_RINIT_FUNCTION(pdo_odbc);
+PHP_RSHUTDOWN_FUNCTION(pdo_odbc);
+PHP_MINFO_FUNCTION(pdo_odbc);
+
+#endif /* PHP_PDO_ODBC_H */
diff --git a/thirdparty/php85/pdo_odbc/php_pdo_odbc_int.h b/thirdparty/php85/pdo_odbc/php_pdo_odbc_int.h
new file mode 100644
index 00000000000..473d70ff707
--- /dev/null
+++ b/thirdparty/php85/pdo_odbc/php_pdo_odbc_int.h
@@ -0,0 +1,181 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+/* internal header; not supposed to be installed */
+
+#ifdef PHP_WIN32
+# define PDO_ODBC_TYPE "Win32"
+#endif
+
+#ifndef PDO_ODBC_TYPE
+# warning Please fix configure to give your ODBC libraries a name
+# define PDO_ODBC_TYPE "Unknown"
+#endif
+
+/* {{{ Roll a dice, pick a header at random... */
+#ifdef HAVE_SQLCLI1_H
+# include
+# if defined(DB268K) && HAVE_LIBRARYMANAGER_H
+# include
+# endif
+#endif
+
+#ifdef HAVE_ODBC_H
+# include
+#endif
+
+#ifdef HAVE_IODBC_H
+# include
+#endif
+
+#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32)
+# include
+#endif
+
+#ifdef HAVE_SQLTYPES_H
+# include
+#endif
+
+#ifdef HAVE_SQLUCODE_H
+# include
+#endif
+
+#ifdef HAVE_SQL_H
+# include
+#endif
+
+#ifdef HAVE_ISQL_H
+# include
+#endif
+
+#ifdef HAVE_SQLEXT_H
+# include
+#endif
+
+#ifdef HAVE_ISQLEXT_H
+# include
+#endif
+
+#ifdef HAVE_UDBCEXT_H
+# include
+#endif
+
+#ifdef HAVE_CLI0CORE_H
+# include
+#endif
+
+#ifdef HAVE_CLI0EXT1_H
+# include
+#endif
+
+#ifdef HAVE_CLI0CLI_H
+# include
+#endif
+
+#ifdef HAVE_CLI0DEFS_H
+# include
+#endif
+
+#ifdef HAVE_CLI0ENV_H
+# include
+#endif
+
+/* }}} */
+
+/* {{{ Figure out the type for handles */
+#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE)
+# define PDO_ODBC_HENV SQLHANDLE
+# define PDO_ODBC_HDBC SQLHANDLE
+# define PDO_ODBC_HSTMT SQLHANDLE
+#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER))
+# define PDO_ODBC_HENV SQLHENV
+# define PDO_ODBC_HDBC SQLHDBC
+# define PDO_ODBC_HSTMT SQLHSTMT
+#else
+# define PDO_ODBC_HENV HENV
+# define PDO_ODBC_HDBC HDBC
+# define PDO_ODBC_HSTMT HSTMT
+#endif
+/* }}} */
+
+typedef struct {
+ char last_state[6];
+ char last_err_msg[SQL_MAX_MESSAGE_LENGTH];
+ SQLINTEGER last_error;
+ const char *file, *what;
+ int line;
+} pdo_odbc_errinfo;
+
+typedef struct {
+ PDO_ODBC_HENV env;
+ PDO_ODBC_HDBC dbc;
+ pdo_odbc_errinfo einfo;
+ unsigned assume_utf8:1;
+ unsigned _spare:31;
+} pdo_odbc_db_handle;
+
+typedef struct {
+ char *data;
+ zend_ulong datalen;
+ SQLLEN fetched_len;
+ SQLSMALLINT coltype;
+ char colname[128];
+ unsigned is_long;
+ unsigned is_unicode:1;
+ unsigned _spare:31;
+} pdo_odbc_column;
+
+typedef struct {
+ PDO_ODBC_HSTMT stmt;
+ pdo_odbc_column *cols;
+ pdo_odbc_db_handle *H;
+ pdo_odbc_errinfo einfo;
+ char *convbuf;
+ zend_ulong convbufsize;
+ unsigned going_long:1;
+ unsigned assume_utf8:1;
+ signed col_count:16;
+ unsigned _spare:14;
+} pdo_odbc_stmt;
+
+typedef struct {
+ SQLLEN len;
+ SQLSMALLINT paramtype;
+ char *outbuf;
+ unsigned is_unicode:1;
+ unsigned _spare:31;
+} pdo_odbc_param;
+
+extern const pdo_driver_t pdo_odbc_driver;
+extern const struct pdo_stmt_methods odbc_stmt_methods;
+
+void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line);
+#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__)
+#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__)
+#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__)
+
+void pdo_odbc_init_error_table(void);
+void pdo_odbc_fini_error_table(void);
+
+#ifdef SQL_ATTR_CONNECTION_POOLING
+extern zend_ulong pdo_odbc_pool_on;
+extern zend_ulong pdo_odbc_pool_mode;
+#endif
+
+enum {
+ PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC,
+ PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */
+};
diff --git a/thirdparty/php85/pdo_pgsql/pgsql_driver.c b/thirdparty/php85/pdo_pgsql/pgsql_driver.c
new file mode 100644
index 00000000000..844c2279b0d
--- /dev/null
+++ b/thirdparty/php85/pdo_pgsql/pgsql_driver.c
@@ -0,0 +1,1479 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Edin Kadribasic |
+ | Ilia Alshanestsky |
+ | Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#define SW_USE_PGSQL_HOOK
+#include "php_swoole_pgsql.h"
+
+#if PHP_VERSION_ID >= 80500
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/php_string.h" /* For php_addcslashes_str() in _pdo_pgsql_escape_credentials() */
+#include "main/php_network.h"
+#include "ext/pdo/php_pdo.h"
+#include "ext/pdo/php_pdo_driver.h"
+#include "ext/pdo/php_pdo_error.h"
+#include "ext/standard/file.h"
+#include "php_pdo_pgsql.h"
+#include "php_pdo_pgsql_int.h"
+#include "zend_exceptions.h"
+#include "zend_interfaces.h"
+#include "zend_smart_str.h"
+#include "pgsql_driver_arginfo.h"
+
+static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh);
+
+static char * _pdo_pgsql_trim_message(const char *message, int persistent)
+{
+ size_t i = strlen(message)-1;
+ char *tmp;
+
+ if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
+ --i;
+ }
+ while (i>0 && (message[i] == '\r' || message[i] == '\n')) {
+ --i;
+ }
+ ++i;
+ tmp = pemalloc(i + 1, persistent);
+ memcpy(tmp, message, i);
+ tmp[i] = '\0';
+
+ return tmp;
+}
+
+static zend_string* _pdo_pgsql_escape_credentials(char *str)
+{
+ if (str) {
+ return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'"));
+ }
+
+ return NULL;
+}
+
+int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
+ pdo_pgsql_error_info *einfo = &H->einfo;
+ char *errmsg = PQerrorMessage(H->server);
+
+ einfo->errcode = errcode;
+ einfo->file = file;
+ einfo->line = line;
+
+ if (einfo->errmsg) {
+ pefree(einfo->errmsg, dbh->is_persistent);
+ einfo->errmsg = NULL;
+ }
+
+ if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {
+ strcpy(*pdo_err, "HY000");
+ }
+ else {
+ strcpy(*pdo_err, sqlstate);
+ }
+
+ if (msg) {
+ einfo->errmsg = pestrdup(msg, dbh->is_persistent);
+ }
+ else if (errmsg) {
+ einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);
+ }
+
+ if (!dbh->methods) {
+ pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);
+ }
+
+ return errcode;
+}
+/* }}} */
+
+static void _pdo_pgsql_notice(void *context, const char *message) /* {{{ */
+{
+ pdo_dbh_t * dbh = (pdo_dbh_t *)context;
+ zend_fcall_info_cache *fc = ((pdo_pgsql_db_handle *)dbh->driver_data)->notice_callback;
+ if (fc) {
+ zval zarg;
+ ZVAL_STRING(&zarg, message);
+ zend_call_known_fcc(fc, NULL, 1, &zarg, NULL);
+ zval_ptr_dtor_str(&zarg);
+ }
+}
+/* }}} */
+
+static void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ pdo_pgsql_error_info *einfo = &H->einfo;
+
+ if (einfo->errcode) {
+ add_next_index_long(info, einfo->errcode);
+ } else {
+ /* Add null to respect expected info array structure */
+ add_next_index_null(info);
+ }
+ if (einfo->errmsg) {
+ add_next_index_string(info, einfo->errmsg);
+ }
+}
+/* }}} */
+
+void pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H) /* {{{ */
+{
+ if (H->notice_callback) {
+ zend_fcc_dtor(H->notice_callback);
+ efree(H->notice_callback);
+ H->notice_callback = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ pdo_pgsql_create_lob_stream */
+static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count)
+{
+ struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
+ return lo_write(self->conn, self->lfd, (char*)buf, count);
+}
+
+static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count)
+{
+ struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
+ return lo_read(self->conn, self->lfd, buf, count);
+}
+
+static int pgsql_lob_close(php_stream *stream, int close_handle)
+{
+ struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(&self->dbh))->driver_data;
+
+ if (close_handle) {
+ lo_close(self->conn, self->lfd);
+ }
+ zend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream));
+ zval_ptr_dtor(&self->dbh);
+ efree(self);
+ return 0;
+}
+
+static int pgsql_lob_flush(php_stream *stream)
+{
+ return 0;
+}
+
+static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence,
+ zend_off_t *newoffset)
+{
+ struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
+#ifdef ZEND_ENABLE_ZVAL_LONG64
+ zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence);
+#else
+ zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence);
+#endif
+ *newoffset = pos;
+ return pos >= 0 ? 0 : -1;
+}
+
+const php_stream_ops pdo_pgsql_lob_stream_ops = {
+ pgsql_lob_write,
+ pgsql_lob_read,
+ pgsql_lob_close,
+ pgsql_lob_flush,
+ "pdo_pgsql lob stream",
+ pgsql_lob_seek,
+ NULL,
+ NULL,
+ NULL
+};
+
+php_stream *pdo_pgsql_create_lob_stream(zend_object *dbh, int lfd, Oid oid)
+{
+ php_stream *stm;
+ struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(php_pdo_dbh_fetch_inner(dbh))->driver_data;
+
+ ZVAL_OBJ(&self->dbh, dbh);
+ self->lfd = lfd;
+ self->oid = oid;
+ self->conn = H->server;
+
+ stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
+
+ if (stm) {
+ GC_ADDREF(dbh);
+ zend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res);
+ return stm;
+ }
+
+ efree(self);
+ return NULL;
+}
+/* }}} */
+
+void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh)
+{
+ zend_resource *res;
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ if (H->lob_streams) {
+ ZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) {
+ if (res->type >= 0) {
+ zend_list_close(res);
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+}
+
+static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ if (H) {
+ if (H->lob_streams) {
+ pdo_pgsql_close_lob_streams(dbh);
+ zend_hash_destroy(H->lob_streams);
+ pefree(H->lob_streams, dbh->is_persistent);
+ H->lob_streams = NULL;
+ }
+ pdo_pgsql_cleanup_notice_callback(H);
+ if (H->server) {
+ PQfinish(H->server);
+ H->server = NULL;
+ }
+ if (H->einfo.errmsg) {
+ pefree(H->einfo.errmsg, dbh->is_persistent);
+ H->einfo.errmsg = NULL;
+ }
+ pefree(H, dbh->is_persistent);
+ dbh->driver_data = NULL;
+ }
+}
+/* }}} */
+
+static bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
+ int scrollable;
+ int ret;
+ zend_string *nsql = NULL;
+ int emulate = 0;
+ int execute_only = 0;
+ zval *val;
+ zend_long lval;
+
+ S->H = H;
+ stmt->driver_data = S;
+ stmt->methods = &pgsql_stmt_methods;
+
+ scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
+ PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL;
+
+ if (scrollable) {
+ if (S->cursor_name) {
+ efree(S->cursor_name);
+ }
+ spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
+ emulate = 1;
+ } else if (driver_options) {
+ if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) {
+ emulate = 1;
+ }
+ if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) {
+ execute_only = 1;
+ }
+ } else {
+ emulate = H->emulate_prepares;
+ execute_only = H->disable_prepares;
+ }
+
+ if (emulate) {
+ stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
+ } else {
+ stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
+ stmt->named_rewrite_template = "$%d";
+ }
+
+ S->is_unbuffered =
+ driver_options
+ && (val = zend_hash_index_find(Z_ARRVAL_P(driver_options), PDO_ATTR_PREFETCH))
+ && pdo_get_long_param(&lval, val)
+ ? !lval
+ : H->default_fetching_laziness
+ ;
+
+ ret = pdo_parse_params(stmt, sql, &nsql);
+
+ if (ret == -1) {
+ /* couldn't grok it */
+ strcpy(dbh->error_code, stmt->error_code);
+ return false;
+ } else if (ret == 1) {
+ /* query was re-written */
+ S->query = nsql;
+ } else {
+ S->query = zend_string_copy(sql);
+ }
+
+ if (!emulate && !execute_only) {
+ /* prepared query: set the query name and defer the
+ actual prepare until the first execute call */
+ spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
+ }
+
+ return true;
+}
+
+static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ PGresult *res;
+ zend_long ret = 1;
+ ExecStatusType qs;
+
+ bool in_trans = pgsql_handle_in_transaction(dbh);
+
+ if (!(res = PQexec(H->server, ZSTR_VAL(sql)))) {
+ /* fatal error */
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ return -1;
+ }
+ qs = PQresultStatus(res);
+ if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
+ pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
+ PQclear(res);
+ return -1;
+ }
+ H->pgoid = PQoidValue(res);
+ if (qs == PGRES_COMMAND_OK) {
+ ret = ZEND_ATOL(PQcmdTuples(res));
+ } else {
+ ret = Z_L(0);
+ }
+ PQclear(res);
+ if (in_trans && !pgsql_handle_in_transaction(dbh)) {
+ pdo_pgsql_close_lob_streams(dbh);
+ }
+
+ return ret;
+}
+
+static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
+{
+ unsigned char *escaped;
+ char *quoted;
+ size_t quotedlen;
+ zend_string *quoted_str;
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ size_t tmp_len;
+ int err;
+
+ switch (paramtype) {
+ case PDO_PARAM_LOB:
+ /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
+ escaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len);
+ if (escaped == NULL) {
+ return NULL;
+ }
+ quotedlen = tmp_len + 1;
+ quoted = emalloc(quotedlen + 1);
+ memcpy(quoted+1, escaped, quotedlen-2);
+ quoted[0] = '\'';
+ quoted[quotedlen-1] = '\'';
+ quoted[quotedlen] = '\0';
+ PQfreemem(escaped);
+ break;
+ default:
+ quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);
+ quoted[0] = '\'';
+ quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &err);
+ if (err) {
+ efree(quoted);
+ return NULL;
+ }
+ quoted[quotedlen + 1] = '\'';
+ quoted[quotedlen + 2] = '\0';
+ quotedlen += 2;
+ }
+
+ quoted_str = zend_string_init(quoted, quotedlen, 0);
+ efree(quoted);
+ return quoted_str;
+}
+
+static zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name)
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ zend_string *id = NULL;
+ PGresult *res;
+ ExecStatusType status;
+
+ if (name == NULL) {
+ res = PQexec(H->server, "SELECT LASTVAL()");
+ } else {
+ const char *q[1];
+ q[0] = ZSTR_VAL(name);
+
+ res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
+ }
+ status = PQresultStatus(res);
+
+ if (res && (status == PGRES_TUPLES_OK)) {
+ id = zend_string_init((char *)PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0);
+ } else {
+ pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
+ }
+
+ if (res) {
+ PQclear(res);
+ }
+
+ return id;
+}
+
+void pdo_libpq_version(char *buf, size_t len)
+{
+ int version = PQlibVersion();
+ int major = version / 10000;
+ if (major >= 10) {
+ int minor = version % 10000;
+ snprintf(buf, len, "%d.%d", major, minor);
+ } else {
+ int minor = version / 100 % 100;
+ int revision = version % 100;
+ snprintf(buf, len, "%d.%d.%d", major, minor, revision);
+ }
+}
+
+static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ switch (attr) {
+ case PDO_ATTR_EMULATE_PREPARES:
+ ZVAL_BOOL(return_value, H->emulate_prepares);
+ break;
+
+ case PDO_PGSQL_ATTR_DISABLE_PREPARES:
+ ZVAL_BOOL(return_value, H->disable_prepares);
+ break;
+
+ case PDO_ATTR_CLIENT_VERSION: {
+ char buf[16];
+ pdo_libpq_version(buf, sizeof(buf));
+ ZVAL_STRING(return_value, buf);
+ break;
+ }
+
+ case PDO_ATTR_SERVER_VERSION:
+ ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"));
+ break;
+
+ case PDO_ATTR_CONNECTION_STATUS:
+ switch (PQstatus(H->server)) {
+ case CONNECTION_STARTED:
+ ZVAL_STRINGL(return_value, "Waiting for connection to be made.", strlen("Waiting for connection to be made."));
+ break;
+
+ case CONNECTION_MADE:
+ case CONNECTION_OK:
+ ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", strlen("Connection OK; waiting to send."));
+ break;
+
+ case CONNECTION_AWAITING_RESPONSE:
+ ZVAL_STRINGL(return_value, "Waiting for a response from the server.", strlen("Waiting for a response from the server."));
+ break;
+
+ case CONNECTION_AUTH_OK:
+ ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", strlen("Received authentication; waiting for backend start-up to finish."));
+ break;
+#ifdef CONNECTION_SSL_STARTUP
+ case CONNECTION_SSL_STARTUP:
+ ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", strlen("Negotiating SSL encryption."));
+ break;
+#endif
+ case CONNECTION_SETENV:
+ ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", strlen("Negotiating environment-driven parameter settings."));
+ break;
+
+#ifdef CONNECTION_CONSUME
+ case CONNECTION_CONSUME:
+ ZVAL_STRINGL(return_value, "Flushing send queue/consuming extra data.", strlen("Flushing send queue/consuming extra data."));
+ break;
+#endif
+#ifdef CONNECTION_GSS_STARTUP
+ case CONNECTION_SSL_STARTUP:
+ ZVAL_STRINGL(return_value, "Negotiating GSSAPI.", strlen("Negotiating GSSAPI."));
+ break;
+#endif
+#ifdef CONNECTION_CHECK_TARGET
+ case CONNECTION_CHECK_TARGET:
+ ZVAL_STRINGL(return_value, "Connection OK; checking target server properties.", strlen("Connection OK; checking target server properties."));
+ break;
+#endif
+#ifdef CONNECTION_CHECK_STANDBY
+ case CONNECTION_CHECK_STANDBY:
+ ZVAL_STRINGL(return_value, "Connection OK; checking if server in standby.", strlen("Connection OK; checking if server in standby."));
+ break;
+#endif
+ case CONNECTION_BAD:
+ default:
+ ZVAL_STRINGL(return_value, "Bad connection.", strlen("Bad connection."));
+ break;
+ }
+ break;
+
+ case PDO_ATTR_SERVER_INFO: {
+ int spid = PQbackendPID(H->server);
+
+
+ zend_string *str_info =
+ strpprintf(0,
+ "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
+ spid,
+ (char*)PQparameterStatus(H->server, "client_encoding"),
+ (char*)PQparameterStatus(H->server, "is_superuser"),
+ (char*)PQparameterStatus(H->server, "session_authorization"),
+ (char*)PQparameterStatus(H->server, "DateStyle"));
+
+ ZVAL_STR(return_value, str_info);
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* {{{ */
+static zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh)
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) {
+ PQreset(H->server);
+ }
+ return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
+}
+/* }}} */
+
+static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh)
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ return PQtransactionStatus(H->server) > PQTRANS_IDLE;
+}
+
+static bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ PGresult *res;
+ bool ret = true;
+
+ res = PQexec(H->server, cmd);
+
+ if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+ pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
+ ret = false;
+ }
+
+ PQclear(res);
+ return ret;
+}
+
+static bool pgsql_handle_begin(pdo_dbh_t *dbh)
+{
+ return pdo_pgsql_transaction_cmd("BEGIN", dbh);
+}
+
+static bool pgsql_handle_commit(pdo_dbh_t *dbh)
+{
+ bool ret = pdo_pgsql_transaction_cmd("COMMIT", dbh);
+
+ /* When deferred constraints are used the commit could
+ fail, and a ROLLBACK implicitly ran. See bug #67462 */
+ if (ret) {
+ pdo_pgsql_close_lob_streams(dbh);
+ } else {
+ dbh->in_txn = pgsql_handle_in_transaction(dbh);
+ }
+
+ return ret;
+}
+
+static bool pgsql_handle_rollback(pdo_dbh_t *dbh)
+{
+ int ret = pdo_pgsql_transaction_cmd("ROLLBACK", dbh);
+
+ if (ret) {
+ pdo_pgsql_close_lob_streams(dbh);
+ }
+
+ return ret;
+}
+
+static bool _pdo_pgsql_send_copy_data(pdo_pgsql_db_handle *H, zval *line) {
+ size_t query_len;
+ zend_string *query;
+
+ if (!try_convert_to_string(line)) {
+ return false;
+ }
+
+ query_len = Z_STRLEN_P(line);
+ query = zend_string_alloc(query_len + 2, false); /* room for \n\0 */
+ memcpy(ZSTR_VAL(query), Z_STRVAL_P(line), query_len + 1);
+ ZSTR_LEN(query) = query_len;
+
+ if (query_len > 0 && ZSTR_VAL(query)[query_len - 1] != '\n') {
+ ZSTR_VAL(query)[query_len] = '\n';
+ ZSTR_VAL(query)[query_len + 1] = '\0';
+ ZSTR_LEN(query) ++;
+ }
+
+ if (PQputCopyData(H->server, ZSTR_VAL(query), ZSTR_LEN(query)) != 1) {
+ zend_string_release_ex(query, false);
+ return false;
+ }
+
+ zend_string_release_ex(query, false);
+ return true;
+}
+
+void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+
+ zval *pg_rows;
+
+ char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
+ size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
+ char *query;
+
+ PGresult *pgsql_result;
+ ExecStatusType status;
+
+ ZEND_PARSE_PARAMETERS_START(2, 5)
+ Z_PARAM_STRING(table_name, table_name_len)
+ Z_PARAM_ITERABLE(pg_rows)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_STRING(pg_delim, pg_delim_len)
+ Z_PARAM_STRING(pg_null_as, pg_null_as_len)
+ Z_PARAM_STRING_OR_NULL(pg_fields, pg_fields_len)
+ ZEND_PARSE_PARAMETERS_END();
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+ PDO_DBH_CLEAR_ERR();
+
+ /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
+ if (pg_fields) {
+ spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ } else {
+ spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ }
+
+ /* Obtain db Handle */
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+ pgsql_result = PQexec(H->server, query);
+
+ efree(query);
+ query = NULL;
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(H->server);
+ }
+
+ if (status == PGRES_COPY_IN && pgsql_result) {
+ int command_failed = 0;
+ zval *tmp;
+ zend_object_iterator *iter;
+
+ PQclear(pgsql_result);
+
+ if (Z_TYPE_P(pg_rows) == IS_ARRAY) {
+ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {
+ if (!_pdo_pgsql_send_copy_data(H, tmp)) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+ } ZEND_HASH_FOREACH_END();
+ } else {
+ iter = Z_OBJCE_P(pg_rows)->get_iterator(Z_OBJCE_P(pg_rows), pg_rows, 0);
+ if (iter == NULL || EG(exception)) {
+ RETURN_THROWS();
+ }
+
+ if (iter->funcs->rewind) {
+ iter->funcs->rewind(iter);
+ if (EG(exception)) {
+ RETURN_THROWS();
+ }
+ }
+
+ for (; iter->funcs->valid(iter) == SUCCESS && EG(exception) == NULL; iter->funcs->move_forward(iter)) {
+ tmp = iter->funcs->get_current_data(iter);
+ if (!_pdo_pgsql_send_copy_data(H, tmp)) {
+ zend_iterator_dtor(iter);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+ }
+ zend_iterator_dtor(iter);
+ }
+
+ if (PQputCopyEnd(H->server, NULL) != 1) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
+ command_failed = 1;
+ }
+ PQclear(pgsql_result);
+ }
+
+ PDO_HANDLE_DBH_ERR();
+ RETURN_BOOL(!command_failed);
+ } else {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
+ PQclear(pgsql_result);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+}
+
+/* {{{ Returns true if the copy worked fine or false if error */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray)
+{
+ pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+
+ char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
+ size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
+ char *query;
+ PGresult *pgsql_result;
+ ExecStatusType status;
+ php_stream *stream;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!",
+ &table_name, &table_name_len, &filename, &filename_len,
+ &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ /* Obtain db Handler */
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+ PDO_DBH_CLEAR_ERR();
+
+ stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context));
+ if (!stream) {
+ pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+
+ /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
+ if (pg_fields) {
+ spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ } else {
+ spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ }
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+ pgsql_result = PQexec(H->server, query);
+
+ efree(query);
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(H->server);
+ }
+
+ if (status == PGRES_COPY_IN && pgsql_result) {
+ char *buf;
+ int command_failed = 0;
+ size_t line_len = 0;
+
+ PQclear(pgsql_result);
+ while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
+ if (PQputCopyData(H->server, buf, line_len) != 1) {
+ efree(buf);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ php_stream_close(stream);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+ efree(buf);
+ }
+ php_stream_close(stream);
+
+ if (PQputCopyEnd(H->server, NULL) != 1) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
+ command_failed = 1;
+ }
+ PQclear(pgsql_result);
+ }
+
+ PDO_HANDLE_DBH_ERR();
+ RETURN_BOOL(!command_failed);
+ } else {
+ php_stream_close(stream);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
+ PQclear(pgsql_result);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+}
+
+/* {{{ Returns true if the copy worked fine or false if error */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile)
+{
+ pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+
+ char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
+ size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
+ char *query;
+
+ PGresult *pgsql_result;
+ ExecStatusType status;
+
+ php_stream *stream;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!",
+ &table_name, &table_name_len, &filename, &filename_len,
+ &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+ PDO_DBH_CLEAR_ERR();
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context));
+ if (!stream) {
+ pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+
+ /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
+ if (pg_fields) {
+ spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ } else {
+ spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ }
+ pgsql_result = PQexec(H->server, query);
+ efree(query);
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(H->server);
+ }
+
+ if (status == PGRES_COPY_OUT && pgsql_result) {
+ PQclear(pgsql_result);
+ while (1) {
+ char *csv = NULL;
+ int ret = PQgetCopyData(H->server, &csv, 0);
+
+ if (ret == -1) {
+ break; /* done */
+ } else if (ret > 0) {
+ if (php_stream_write(stream, csv, ret) != (size_t)ret) {
+ pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
+ PQfreemem(csv);
+ php_stream_close(stream);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ } else {
+ PQfreemem(csv);
+ }
+ } else {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ php_stream_close(stream);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+ }
+ php_stream_close(stream);
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+ RETURN_TRUE;
+ } else {
+ php_stream_close(stream);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
+ PQclear(pgsql_result);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+}
+
+/* {{{ Returns true if the copy worked fine or false if error */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile)
+{
+ pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+
+}
+/* }}} */
+
+void pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+
+ char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
+ size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
+ char *query;
+
+ PGresult *pgsql_result;
+ ExecStatusType status;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss!",
+ &table_name, &table_name_len,
+ &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+ PDO_DBH_CLEAR_ERR();
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+
+ /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
+ if (pg_fields) {
+ spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ } else {
+ spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ }
+ pgsql_result = PQexec(H->server, query);
+ efree(query);
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(H->server);
+ }
+
+ if (status == PGRES_COPY_OUT && pgsql_result) {
+ PQclear(pgsql_result);
+ array_init(return_value);
+
+ while (1) {
+ char *csv = NULL;
+ int ret = PQgetCopyData(H->server, &csv, 0);
+ if (ret == -1) {
+ break; /* copy done */
+ } else if (ret > 0) {
+ add_next_index_stringl(return_value, csv, ret);
+ PQfreemem(csv);
+ } else {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+ }
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+ } else {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
+ PQclear(pgsql_result);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+}
+
+/* {{{ Returns true if the copy worked fine or false if error */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray)
+{
+ pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+ Oid lfd;
+
+ ZEND_PARSE_PARAMETERS_NONE();
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+ PDO_DBH_CLEAR_ERR();
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ lfd = lo_creat(H->server, INV_READ|INV_WRITE);
+
+ if (lfd != InvalidOid) {
+ zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);
+
+ RETURN_STR(buf);
+ }
+
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+}
+
+/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate)
+{
+ pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+ Oid oid;
+ int lfd;
+ char *oidstr;
+ size_t oidstrlen;
+ char *modestr = "rb";
+ size_t modestrlen;
+ int mode = INV_READ;
+ char *end_ptr;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s",
+ &oidstr, &oidstrlen, &modestr, &modestrlen)) {
+ RETURN_THROWS();
+ }
+
+ oid = (Oid)strtoul(oidstr, &end_ptr, 10);
+ if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
+ RETURN_FALSE;
+ }
+
+ if (strpbrk(modestr, "+w")) {
+ mode = INV_READ|INV_WRITE;
+ }
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+ PDO_DBH_CLEAR_ERR();
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ lfd = lo_open(H->server, oid, mode);
+
+ if (lfd >= 0) {
+ php_stream *stream = pdo_pgsql_create_lob_stream(Z_OBJ_P(ZEND_THIS), lfd, oid);
+ if (stream) {
+ php_stream_to_zval(stream, return_value);
+ return;
+ }
+ } else {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ }
+
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+}
+
+/* {{{ Opens an existing large object stream. Must be called inside a transaction. */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen)
+{
+ pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+ Oid oid;
+ char *oidstr, *end_ptr;
+ size_t oidlen;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+ &oidstr, &oidlen)) {
+ RETURN_THROWS();
+ }
+
+ oid = (Oid)strtoul(oidstr, &end_ptr, 10);
+ if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
+ RETURN_FALSE;
+ }
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+ PDO_DBH_CLEAR_ERR();
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ if (1 == lo_unlink(H->server, oid)) {
+ RETURN_TRUE;
+ }
+
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+}
+
+/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink)
+{
+ pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+ zend_long result_type = PDO_FETCH_USE_DEFAULT;
+ zend_long ms_timeout = 0;
+ PGnotify *pgsql_notify;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll",
+ &result_type, &ms_timeout)) {
+ RETURN_THROWS();
+ }
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+
+ if (result_type == PDO_FETCH_USE_DEFAULT) {
+ result_type = dbh->default_fetch_type;
+ }
+
+ if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {
+ zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM");
+ RETURN_THROWS();
+ }
+
+ if (ms_timeout < 0) {
+ zend_argument_value_error(2, "must be greater than or equal to 0");
+ RETURN_THROWS();
+#ifdef ZEND_ENABLE_ZVAL_LONG64
+ } else if (ms_timeout > INT_MAX) {
+ php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX);
+ ms_timeout = INT_MAX;
+#endif
+ }
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ if (!PQconsumeInput(H->server)) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+ pgsql_notify = PQnotifies(H->server);
+
+ if (ms_timeout && !pgsql_notify) {
+ php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout);
+
+ if (!PQconsumeInput(H->server)) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+ pgsql_notify = PQnotifies(H->server);
+ }
+
+ if (!pgsql_notify) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+ if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {
+ add_index_string(return_value, 0, pgsql_notify->relname);
+ add_index_long(return_value, 1, pgsql_notify->be_pid);
+ if (pgsql_notify->extra && pgsql_notify->extra[0]) {
+ add_index_string(return_value, 2, pgsql_notify->extra);
+ }
+ }
+ if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {
+ add_assoc_string(return_value, "message", pgsql_notify->relname);
+ add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
+ if (pgsql_notify->extra && pgsql_notify->extra[0]) {
+ add_assoc_string(return_value, "payload", pgsql_notify->extra);
+ }
+ }
+
+ PQfreemem(pgsql_notify);
+}
+
+/* {{{ Get asynchronous notification */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify)
+{
+ pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pdo_dbh_t *dbh;
+ pdo_pgsql_db_handle *H;
+
+ ZEND_PARSE_PARAMETERS_NONE();
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ RETURN_LONG(PQbackendPID(H->server));
+}
+
+/* {{{ Get backend(server) pid */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid)
+{
+ pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ Sets a callback to receive DB notices (after client_min_messages has been set) */
+PHP_METHOD(PDO_PGSql_Ext, pgsqlSetNoticeCallback)
+{
+ zend_fcall_info fci = empty_fcall_info;
+ zend_fcall_info_cache fcc = empty_fcall_info_cache;
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "F!", &fci, &fcc)) {
+ RETURN_THROWS();
+ }
+
+ pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK_WITH_CLEANUP(cleanup);
+
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ pdo_pgsql_cleanup_notice_callback(H);
+
+ if (ZEND_FCC_INITIALIZED(fcc)) {
+ H->notice_callback = emalloc(sizeof(zend_fcall_info_cache));
+ zend_fcc_dup(H->notice_callback, &fcc);
+ }
+
+ return;
+
+cleanup:
+ if (ZEND_FCC_INITIALIZED(fcc)) {
+ zend_fcc_dtor(&fcc);
+ }
+ RETURN_THROWS();
+}
+/* }}} */
+
+static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind)
+{
+ switch (kind) {
+ case PDO_DBH_DRIVER_METHOD_KIND_DBH:
+ return class_PDO_PGSql_Ext_methods;
+ default:
+ return NULL;
+ }
+}
+
+static bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
+{
+ bool bval;
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ switch (attr) {
+ case PDO_ATTR_EMULATE_PREPARES:
+ if (!pdo_get_bool_param(&bval, val)) {
+ return false;
+ }
+ H->emulate_prepares = bval;
+ return true;
+ case PDO_PGSQL_ATTR_DISABLE_PREPARES:
+ if (!pdo_get_bool_param(&bval, val)) {
+ return false;
+ }
+ H->disable_prepares = bval;
+ return true;
+ case PDO_ATTR_PREFETCH:
+ if (!pdo_get_bool_param(&bval, val)) {
+ return false;
+ }
+ H->default_fetching_laziness = !bval;
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct pdo_dbh_methods pgsql_methods = {
+ pgsql_handle_closer,
+ pgsql_handle_preparer,
+ pgsql_handle_doer,
+ pgsql_handle_quoter,
+ pgsql_handle_begin,
+ pgsql_handle_commit,
+ pgsql_handle_rollback,
+ pdo_pgsql_set_attr,
+ pdo_pgsql_last_insert_id,
+ pdo_pgsql_fetch_error_func,
+ pdo_pgsql_get_attribute,
+ pdo_pgsql_check_liveness, /* check_liveness */
+ pdo_pgsql_get_driver_methods, /* get_driver_methods */
+ NULL,
+ pgsql_handle_in_transaction,
+ NULL, /* get_gc */
+ pdo_pgsql_scanner
+};
+
+static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
+{
+ pdo_pgsql_db_handle *H;
+ int ret = 0;
+ char *p, *e;
+ zend_string *tmp_user, *tmp_pass;
+ smart_str conn_str = {0};
+ zend_long connect_timeout = 30;
+
+ H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
+ dbh->driver_data = H;
+
+ dbh->skip_param_evt =
+ 1 << PDO_PARAM_EVT_EXEC_POST |
+ 1 << PDO_PARAM_EVT_FETCH_PRE |
+ 1 << PDO_PARAM_EVT_FETCH_POST;
+
+ H->einfo.errcode = 0;
+ H->einfo.errmsg = NULL;
+
+ /* PostgreSQL wants params in the connect string to be separated by spaces,
+ * if the PDO standard semicolons are used, we convert them to spaces
+ */
+ e = (char *) dbh->data_source + dbh->data_source_len;
+ p = (char *) dbh->data_source;
+ while ((p = memchr(p, ';', (e - p)))) {
+ *p = ' ';
+ }
+
+ if (driver_options) {
+ connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);
+ }
+
+ /* escape username and password, if provided */
+ tmp_user = !strstr((char *) dbh->data_source, "user=") ? _pdo_pgsql_escape_credentials(dbh->username) : NULL;
+ tmp_pass = !strstr((char *) dbh->data_source, "password=") ? _pdo_pgsql_escape_credentials(dbh->password) : NULL;
+
+ smart_str_appendl(&conn_str, dbh->data_source, dbh->data_source_len);
+ smart_str_append_printf(&conn_str, " connect_timeout=" ZEND_LONG_FMT, connect_timeout);
+
+ /* support both full connection string & connection string + login and/or password */
+ if (tmp_user) {
+ smart_str_append_printf(&conn_str, " user='%s'", ZSTR_VAL(tmp_user));
+ }
+
+ if (tmp_pass) {
+ smart_str_append_printf(&conn_str, " password='%s'", ZSTR_VAL(tmp_pass));
+ }
+ smart_str_0(&conn_str);
+
+ H->server = PQconnectdb(ZSTR_VAL(conn_str.s));
+ H->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent);
+ zend_hash_init(H->lob_streams, 0, NULL, NULL, 1);
+
+ if (tmp_user) {
+ zend_string_release_ex(tmp_user, 0);
+ }
+ if (tmp_pass) {
+ zend_string_release_ex(tmp_pass, 0);
+ }
+
+ smart_str_free(&conn_str);
+
+ if (PQstatus(H->server) != CONNECTION_OK) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
+ goto cleanup;
+ }
+
+ PQsetNoticeProcessor(H->server, _pdo_pgsql_notice, (void *)dbh);
+
+ H->attached = 1;
+ H->pgoid = -1;
+
+ dbh->methods = &pgsql_methods;
+ dbh->alloc_own_columns = 1;
+ dbh->max_escaped_char_length = 2;
+
+ ret = 1;
+
+cleanup:
+ dbh->methods = &pgsql_methods;
+ if (!ret) {
+ pgsql_handle_closer(dbh);
+ }
+
+ return ret;
+}
+/* }}} */
+
+const pdo_driver_t swoole_pdo_pgsql_driver = {
+ PDO_DRIVER_HEADER(pgsql),
+ pdo_pgsql_handle_factory
+};
+#endif
\ No newline at end of file
diff --git a/thirdparty/php85/pdo_pgsql/pgsql_driver_arginfo.h b/thirdparty/php85/pdo_pgsql/pgsql_driver_arginfo.h
new file mode 100644
index 00000000000..cd01e2e8e71
--- /dev/null
+++ b/thirdparty/php85/pdo_pgsql/pgsql_driver_arginfo.h
@@ -0,0 +1,70 @@
+/* This is a generated file, edit the .stub.php file instead.
+ * Stub hash: 30c01b4d2e7f836b81a31dc0c1a115883eb41568 */
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 2, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)
+ ZEND_ARG_OBJ_TYPE_MASK(0, rows, Traversable, MAY_BE_ARRAY, NULL)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 2, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)
+ ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1)
+ ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 1, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, "PDO::FETCH_DEFAULT")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray);
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile);
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray);
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile);
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate);
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen);
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink);
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify);
+ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid);
+
+static const zend_function_entry class_PDO_PGSql_Ext_methods[] = {
+ ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC)
+ ZEND_FE_END
+};
diff --git a/thirdparty/php85/pdo_pgsql/pgsql_sql_parser.c b/thirdparty/php85/pdo_pgsql/pgsql_sql_parser.c
new file mode 100644
index 00000000000..ba40f6ed227
--- /dev/null
+++ b/thirdparty/php85/pdo_pgsql/pgsql_sql_parser.c
@@ -0,0 +1,322 @@
+/* Generated by re2c 4.3 on Fri Jul 18 21:15:02 2025 */
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Matteo Beccati |
+ +----------------------------------------------------------------------+
+*/
+
+
+#include "php.h"
+#include "ext/pdo/php_pdo_driver.h"
+#include "ext/pdo/pdo_sql_parser.h"
+
+int pdo_pgsql_scanner(pdo_scanner_t *s)
+{
+ const char *cursor = s->cur;
+
+ s->tok = cursor;
+
+
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept = 0;
+ if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+ if (yych <= '.') {
+ if (yych <= '$') {
+ if (yych <= '!') {
+ if (yych >= 0x01) goto yy2;
+ } else {
+ if (yych <= '"') goto yy4;
+ if (yych <= '#') goto yy2;
+ goto yy6;
+ }
+ } else {
+ if (yych <= '\'') {
+ if (yych <= '&') goto yy2;
+ goto yy7;
+ } else {
+ if (yych == '-') goto yy8;
+ goto yy2;
+ }
+ }
+ } else {
+ if (yych <= '?') {
+ if (yych <= '9') {
+ if (yych <= '/') goto yy9;
+ goto yy2;
+ } else {
+ if (yych <= ':') goto yy10;
+ if (yych <= '>') goto yy2;
+ goto yy11;
+ }
+ } else {
+ if (yych <= 'E') {
+ if (yych <= 'D') goto yy2;
+ goto yy12;
+ } else {
+ if (yych == 'e') goto yy12;
+ goto yy2;
+ }
+ }
+ }
+yy1:
+ YYCURSOR = YYMARKER;
+ if (yyaccept <= 1) {
+ if (yyaccept == 0) goto yy5;
+ else goto yy15;
+ } else {
+ if (yyaccept == 2) goto yy20;
+ else goto yy31;
+ }
+yy2:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych <= '.') {
+ if (yych <= '$') {
+ if (yych <= '!') {
+ if (yych >= 0x01) goto yy2;
+ } else {
+ if (yych == '#') goto yy2;
+ }
+ } else {
+ if (yych <= '\'') {
+ if (yych <= '&') goto yy2;
+ } else {
+ if (yych != '-') goto yy2;
+ }
+ }
+ } else {
+ if (yych <= '?') {
+ if (yych <= '9') {
+ if (yych >= '0') goto yy2;
+ } else {
+ if (yych <= ':') goto yy3;
+ if (yych <= '>') goto yy2;
+ }
+ } else {
+ if (yych <= 'E') {
+ if (yych <= 'D') goto yy2;
+ } else {
+ if (yych != 'e') goto yy2;
+ }
+ }
+ }
+yy3:
+ { RET(PDO_PARSER_TEXT); }
+yy4:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych >= 0x01) goto yy14;
+yy5:
+ { SKIP_ONE(PDO_PARSER_TEXT); }
+yy6:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= '^') {
+ if (yych <= '$') {
+ if (yych <= '#') goto yy5;
+ goto yy16;
+ } else {
+ if (yych <= '@') goto yy5;
+ if (yych <= 'Z') goto yy17;
+ goto yy5;
+ }
+ } else {
+ if (yych <= '`') {
+ if (yych <= '_') goto yy17;
+ goto yy5;
+ } else {
+ if (yych <= 'z') goto yy17;
+ if (yych <= 0x7F) goto yy5;
+ goto yy17;
+ }
+ }
+yy7:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 0x00) goto yy5;
+ goto yy19;
+yy8:
+ yych = *++YYCURSOR;
+ if (yych == '-') goto yy21;
+ goto yy5;
+yy9:
+ yych = *++YYCURSOR;
+ if (yych == '*') goto yy23;
+ goto yy5;
+yy10:
+ yych = *++YYCURSOR;
+ if (yych <= 'Z') {
+ if (yych <= '9') {
+ if (yych <= '/') goto yy5;
+ goto yy24;
+ } else {
+ if (yych <= ':') goto yy26;
+ if (yych <= '@') goto yy5;
+ goto yy24;
+ }
+ } else {
+ if (yych <= '_') {
+ if (yych <= '^') goto yy5;
+ goto yy24;
+ } else {
+ if (yych <= '`') goto yy5;
+ if (yych <= 'z') goto yy24;
+ goto yy5;
+ }
+ }
+yy11:
+ yych = *++YYCURSOR;
+ if (yych == '?') goto yy27;
+ { RET(PDO_PARSER_BIND_POS); }
+yy12:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == '\'') goto yy28;
+ goto yy5;
+yy13:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy14:
+ if (yych <= 0x00) goto yy1;
+ if (yych != '"') goto yy13;
+ yyaccept = 1;
+ YYMARKER = ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych == '"') goto yy13;
+yy15:
+ { RET(PDO_PARSER_TEXT); }
+yy16:
+ ++YYCURSOR;
+ { RET(PDO_PARSER_CUSTOM_QUOTE); }
+yy17:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych <= 'Z') {
+ if (yych <= '/') {
+ if (yych == '$') goto yy16;
+ goto yy1;
+ } else {
+ if (yych <= '9') goto yy17;
+ if (yych <= '@') goto yy1;
+ goto yy17;
+ }
+ } else {
+ if (yych <= '`') {
+ if (yych == '_') goto yy17;
+ goto yy1;
+ } else {
+ if (yych <= 'z') goto yy17;
+ if (yych <= 0x7F) goto yy1;
+ goto yy17;
+ }
+ }
+yy18:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy19:
+ if (yych <= 0x00) goto yy1;
+ if (yych != '\'') goto yy18;
+ yyaccept = 2;
+ YYMARKER = ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych == '\'') goto yy18;
+yy20:
+ { RET(PDO_PARSER_TEXT); }
+yy21:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych != '\n') goto yy21;
+yy22:
+ { RET(PDO_PARSER_TEXT); }
+yy23:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych == '*') goto yy29;
+ goto yy23;
+yy24:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych <= 'Z') {
+ if (yych <= '/') goto yy25;
+ if (yych <= '9') goto yy24;
+ if (yych >= 'A') goto yy24;
+ } else {
+ if (yych <= '_') {
+ if (yych >= '_') goto yy24;
+ } else {
+ if (yych <= '`') goto yy25;
+ if (yych <= 'z') goto yy24;
+ }
+ }
+yy25:
+ { RET(PDO_PARSER_BIND); }
+yy26:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych == ':') goto yy26;
+ { RET(PDO_PARSER_TEXT); }
+yy27:
+ ++YYCURSOR;
+ { RET(PDO_PARSER_ESCAPED_QUESTION); }
+yy28:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= 0x00) goto yy1;
+ if (yych <= '&') goto yy28;
+ goto yy30;
+ } else {
+ if (yych == '\\') goto yy32;
+ goto yy28;
+ }
+yy29:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych == '*') goto yy29;
+ if (yych == '/') goto yy33;
+ goto yy23;
+yy30:
+ yyaccept = 3;
+ YYMARKER = ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych == '\'') goto yy28;
+yy31:
+ { RET(PDO_PARSER_TEXT); }
+yy32:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ if (yych <= 0x00) goto yy1;
+ goto yy28;
+yy33:
+ ++YYCURSOR;
+ goto yy22;
+}
+
+}
diff --git a/thirdparty/php85/pdo_pgsql/pgsql_statement.c b/thirdparty/php85/pdo_pgsql/pgsql_statement.c
new file mode 100644
index 00000000000..1130a185524
--- /dev/null
+++ b/thirdparty/php85/pdo_pgsql/pgsql_statement.c
@@ -0,0 +1,872 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Edin Kadribasic |
+ | Ilia Alshanestsky |
+ | Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#define SW_USE_PGSQL_HOOK
+#include "php_swoole_pgsql.h"
+
+#if PHP_VERSION_ID >= 80500
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "ext/pdo/php_pdo.h"
+#include "ext/pdo/php_pdo_driver.h"
+#include "php_pdo_pgsql.h"
+#include "php_pdo_pgsql_int.h"
+#ifdef HAVE_NETINET_IN_H
+#include
+#endif
+
+/* from postgresql/src/include/catalog/pg_type.h */
+#define BOOLLABEL "bool"
+#define BOOLOID 16
+#define BYTEALABEL "bytea"
+#define BYTEAOID 17
+#define DATELABEL "date"
+#define DATEOID 1082
+#define INT2LABEL "int2"
+#define INT2OID 21
+#define INT4LABEL "int4"
+#define INT4OID 23
+#define INT8LABEL "int8"
+#define INT8OID 20
+#define OIDOID 26
+#define TEXTLABEL "text"
+#define TEXTOID 25
+#define TIMESTAMPLABEL "timestamp"
+#define TIMESTAMPOID 1114
+#define VARCHARLABEL "varchar"
+#define VARCHAROID 1043
+#define FLOAT4LABEL "float4"
+#define FLOAT4OID 700
+#define FLOAT8LABEL "float8"
+#define FLOAT8OID 701
+
+#define FIN_DISCARD 0x1
+#define FIN_CLOSE 0x2
+#define FIN_ABORT 0x4
+
+
+
+static void pgsql_stmt_finish(pdo_pgsql_stmt *S, int fin_mode)
+{
+ pdo_pgsql_db_handle *H = S->H;
+
+ if (S->is_running_unbuffered && S->result && (fin_mode & FIN_ABORT)) {
+ PGcancel *cancel = PQgetCancel(H->server);
+ char errbuf[256];
+ PQcancel(cancel, errbuf, 256);
+ PQfreeCancel(cancel);
+ S->is_running_unbuffered = false;
+ }
+
+ if (S->result) {
+ /* free the resource */
+ PQclear(S->result);
+ S->result = NULL;
+ }
+
+ if (S->is_running_unbuffered) {
+ /* https://postgresql.org/docs/current/libpq-async.html:
+ * "PQsendQuery cannot be called again until PQgetResult has returned NULL"
+ * And as all single-row functions are connection-wise instead of statement-wise,
+ * any new single-row query has to make sure no preceding one is still running.
+ */
+ // @todo Implement !(fin_mode & FIN_DISCARD)
+ // instead of discarding results we could store them to their statement
+ // so that their fetch() will get them (albeit not in lazy mode anymore).
+ while ((S->result = PQgetResult(H->server))) {
+ PQclear(S->result);
+ S->result = NULL;
+ }
+ S->is_running_unbuffered = false;
+ }
+
+ if (S->stmt_name && S->is_prepared && (fin_mode & FIN_CLOSE)) {
+ PGresult *res;
+#ifndef HAVE_PQCLOSEPREPARED
+ // TODO (??) libpq does not support close statement protocol < postgres 17
+ // check if we can circumvent this.
+ char *q = NULL;
+ spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name);
+ res = PQexec(H->server, q);
+ efree(q);
+#else
+ res = PQclosePrepared(H->server, S->stmt_name);
+#endif
+ if (res) {
+ PQclear(res);
+ }
+
+ S->is_prepared = false;
+ if (H->running_stmt == S) {
+ H->running_stmt = NULL;
+ }
+ }
+}
+
+static int pgsql_stmt_dtor(pdo_stmt_t *stmt)
+{
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+ bool server_obj_usable = php_pdo_stmt_valid_db_obj_handle(stmt);
+
+ pgsql_stmt_finish(S, FIN_DISCARD|(server_obj_usable ? FIN_CLOSE|FIN_ABORT : 0));
+
+ if (S->stmt_name) {
+ efree(S->stmt_name);
+ S->stmt_name = NULL;
+ }
+ if (S->param_lengths) {
+ efree(S->param_lengths);
+ S->param_lengths = NULL;
+ }
+ if (S->param_values) {
+ efree(S->param_values);
+ S->param_values = NULL;
+ }
+ if (S->param_formats) {
+ efree(S->param_formats);
+ S->param_formats = NULL;
+ }
+ if (S->param_types) {
+ efree(S->param_types);
+ S->param_types = NULL;
+ }
+ if (S->query) {
+ zend_string_release(S->query);
+ S->query = NULL;
+ }
+
+ if (S->cursor_name) {
+ if (server_obj_usable) {
+ pdo_pgsql_db_handle *H = S->H;
+ char *q = NULL;
+ PGresult *res;
+
+ spprintf(&q, 0, "CLOSE %s", S->cursor_name);
+ res = PQexec(H->server, q);
+ efree(q);
+ if (res) PQclear(res);
+ }
+ efree(S->cursor_name);
+ S->cursor_name = NULL;
+ }
+
+ if(S->cols) {
+ efree(S->cols);
+ S->cols = NULL;
+ }
+ efree(S);
+ stmt->driver_data = NULL;
+ return 1;
+}
+
+static int pgsql_stmt_execute(pdo_stmt_t *stmt)
+{
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+ pdo_pgsql_db_handle *H = S->H;
+ ExecStatusType status;
+ int dispatch_result = 1;
+
+ bool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh);
+
+ /* in unbuffered mode, finish any running statement: libpq explicitely prohibits this
+ * and returns a PGRES_FATAL_ERROR when PQgetResult gets called for stmt 2 if DEALLOCATE
+ * was called for stmt 1 inbetween
+ * (maybe it will change with pipeline mode in libpq 14?) */
+ if (S->is_unbuffered && H->running_stmt) {
+ pgsql_stmt_finish(H->running_stmt, FIN_CLOSE);
+ H->running_stmt = NULL;
+ }
+ /* ensure that we free any previous unfetched results */
+ pgsql_stmt_finish(S, 0);
+
+ S->current_row = 0;
+
+ if (S->cursor_name) {
+ char *q = NULL;
+
+ if (S->is_prepared) {
+ spprintf(&q, 0, "CLOSE %s", S->cursor_name);
+ PQclear(PQexec(H->server, q));
+ efree(q);
+ }
+
+ spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, ZSTR_VAL(stmt->active_query_string));
+ S->result = PQexec(H->server, q);
+ efree(q);
+
+ /* check if declare failed */
+ status = PQresultStatus(S->result);
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+ pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
+ return 0;
+ }
+ PQclear(S->result);
+
+ /* the cursor was declared correctly */
+ S->is_prepared = 1;
+
+ /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */
+ spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name);
+ S->result = PQexec(H->server, q);
+ efree(q);
+ } else if (S->stmt_name) {
+ /* using a prepared statement */
+
+ if (!S->is_prepared) {
+stmt_retry:
+ /* we deferred the prepare until now, because we didn't
+ * know anything about the parameter types; now we do */
+ S->result = PQprepare(H->server, S->stmt_name, ZSTR_VAL(S->query),
+ stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,
+ S->param_types);
+ status = PQresultStatus(S->result);
+ switch (status) {
+ case PGRES_COMMAND_OK:
+ case PGRES_TUPLES_OK:
+ /* it worked */
+ S->is_prepared = 1;
+ PQclear(S->result);
+ S->result = NULL;
+ break;
+ default: {
+ char *sqlstate = pdo_pgsql_sqlstate(S->result);
+ /* 42P05 means that the prepared statement already existed. this can happen if you use
+ * a connection pooling software line pgpool which doesn't close the db-connection once
+ * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no
+ * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we
+ * deallocate it and retry ONCE (thies 2005.12.15)
+ */
+ if (sqlstate && !strcmp(sqlstate, "42P05")) {
+ PGresult *res;
+#ifndef HAVE_PQCLOSEPREPARED
+ char buf[100]; /* stmt_name == "pdo_crsr_%08x" */
+ snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name);
+ res = PQexec(H->server, buf);
+#else
+ res = PQclosePrepared(H->server, S->stmt_name);
+#endif
+ if (res) {
+ PQclear(res);
+ }
+ goto stmt_retry;
+ } else {
+ pdo_pgsql_error_stmt(stmt, status, sqlstate);
+ return 0;
+ }
+ }
+ }
+ }
+ if (S->is_unbuffered) {
+ dispatch_result = PQsendQueryPrepared(H->server, S->stmt_name,
+ stmt->bound_params ?
+ zend_hash_num_elements(stmt->bound_params) :
+ 0,
+ (const char**)S->param_values,
+ S->param_lengths,
+ S->param_formats,
+ 0);
+ } else {
+ S->result = PQexecPrepared(H->server, S->stmt_name,
+ stmt->bound_params ?
+ zend_hash_num_elements(stmt->bound_params) :
+ 0,
+ (const char**)S->param_values,
+ S->param_lengths,
+ S->param_formats,
+ 0);
+ }
+ } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) {
+ /* execute query with parameters */
+ if (S->is_unbuffered) {
+ dispatch_result = PQsendQueryParams(H->server, ZSTR_VAL(S->query),
+ stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,
+ S->param_types,
+ (const char**)S->param_values,
+ S->param_lengths,
+ S->param_formats,
+ 0);
+ } else {
+ S->result = PQexecParams(H->server, ZSTR_VAL(S->query),
+ stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,
+ S->param_types,
+ (const char**)S->param_values,
+ S->param_lengths,
+ S->param_formats,
+ 0);
+ }
+ } else {
+ /* execute plain query (with embedded parameters) */
+ if (S->is_unbuffered) {
+ dispatch_result = PQsendQuery(H->server, ZSTR_VAL(stmt->active_query_string));
+ } else {
+ S->result = PQexec(H->server, ZSTR_VAL(stmt->active_query_string));
+ }
+ }
+
+ H->running_stmt = S;
+
+ if (S->is_unbuffered) {
+ if (!dispatch_result) {
+ pdo_pgsql_error_stmt(stmt, 0, NULL);
+ H->running_stmt = NULL;
+ return 0;
+ }
+ S->is_running_unbuffered = true;
+ (void)PQsetSingleRowMode(H->server);
+ /* no matter if it returns 0: PQ then transparently fallbacks to full result fetching */
+
+ /* try a first fetch to at least have column names and so on */
+ S->result = PQgetResult(S->H->server);
+ }
+
+ status = PQresultStatus(S->result);
+
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_SINGLE_TUPLE) {
+ pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
+ return 0;
+ }
+
+ stmt->column_count = (int) PQnfields(S->result);
+ if (S->cols == NULL) {
+ S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));
+ }
+
+ if (status == PGRES_COMMAND_OK) {
+ stmt->row_count = ZEND_ATOL(PQcmdTuples(S->result));
+ H->pgoid = PQoidValue(S->result);
+ } else {
+ stmt->row_count = (zend_long)PQntuples(S->result);
+ }
+
+ if (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) {
+ pdo_pgsql_close_lob_streams(stmt->dbh);
+ }
+
+ return 1;
+}
+
+static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
+ enum pdo_param_event event_type)
+{
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+
+ if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) {
+ switch (event_type) {
+ case PDO_PARAM_EVT_FREE:
+ if (param->driver_data) {
+ efree(param->driver_data);
+ }
+ break;
+
+ case PDO_PARAM_EVT_NORMALIZE:
+ /* decode name from $1, $2 into 0, 1 etc. */
+ if (param->name) {
+ if (ZSTR_VAL(param->name)[0] == '$') {
+ param->paramno = ZEND_ATOL(ZSTR_VAL(param->name) + 1);
+ } else {
+ /* resolve parameter name to rewritten name */
+ zend_string *namevar;
+
+ if (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map,
+ param->name)) != NULL) {
+ param->paramno = ZEND_ATOL(ZSTR_VAL(namevar) + 1);
+ param->paramno--;
+ } else {
+ pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", ZSTR_VAL(param->name));
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case PDO_PARAM_EVT_ALLOC:
+ if (!stmt->bound_param_map) {
+ return 1;
+ }
+ if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) {
+ pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", "parameter was not defined");
+ return 0;
+ }
+ ZEND_FALLTHROUGH;
+ case PDO_PARAM_EVT_EXEC_POST:
+ case PDO_PARAM_EVT_FETCH_PRE:
+ case PDO_PARAM_EVT_FETCH_POST:
+ /* work is handled by EVT_NORMALIZE */
+ return 1;
+
+ case PDO_PARAM_EVT_EXEC_PRE:
+ if (!stmt->bound_param_map) {
+ return 1;
+ }
+ if (!S->param_values) {
+ S->param_values = ecalloc(
+ zend_hash_num_elements(stmt->bound_param_map),
+ sizeof(char*));
+ S->param_lengths = ecalloc(
+ zend_hash_num_elements(stmt->bound_param_map),
+ sizeof(int));
+ S->param_formats = ecalloc(
+ zend_hash_num_elements(stmt->bound_param_map),
+ sizeof(int));
+ S->param_types = ecalloc(
+ zend_hash_num_elements(stmt->bound_param_map),
+ sizeof(Oid));
+ }
+ if (param->paramno >= 0) {
+ zval *parameter;
+
+ /*
+ if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) {
+ pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
+ return 0;
+ }
+ */
+
+ if (Z_ISREF(param->parameter)) {
+ parameter = Z_REFVAL(param->parameter);
+ } else {
+ parameter = ¶m->parameter;
+ }
+
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB &&
+ Z_TYPE_P(parameter) == IS_RESOURCE) {
+ php_stream *stm;
+ php_stream_from_zval_no_verify(stm, parameter);
+ if (stm) {
+ if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) {
+ struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract;
+ pdo_pgsql_bound_param *P = param->driver_data;
+
+ if (P == NULL) {
+ P = ecalloc(1, sizeof(*P));
+ param->driver_data = P;
+ }
+ P->oid = htonl(self->oid);
+ S->param_values[param->paramno] = (char*)&P->oid;
+ S->param_lengths[param->paramno] = sizeof(P->oid);
+ S->param_formats[param->paramno] = 1;
+ S->param_types[param->paramno] = OIDOID;
+ return 1;
+ } else {
+ zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
+ if (str != NULL) {
+ ZVAL_STR(parameter, str);
+ } else {
+ ZVAL_EMPTY_STRING(parameter);
+ }
+ }
+ } else {
+ /* expected a stream resource */
+ pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105");
+ return 0;
+ }
+ }
+
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL ||
+ Z_TYPE_P(parameter) == IS_NULL) {
+ S->param_values[param->paramno] = NULL;
+ S->param_lengths[param->paramno] = 0;
+ } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) {
+ S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f";
+ S->param_lengths[param->paramno] = 1;
+ S->param_formats[param->paramno] = 0;
+ } else {
+ convert_to_string(parameter);
+ S->param_values[param->paramno] = Z_STRVAL_P(parameter);
+ S->param_lengths[param->paramno] = Z_STRLEN_P(parameter);
+ S->param_formats[param->paramno] = 0;
+ }
+
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
+ S->param_types[param->paramno] = 0;
+ S->param_formats[param->paramno] = 1;
+ } else {
+ S->param_types[param->paramno] = 0;
+ }
+ }
+ break;
+ }
+ } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) {
+ /* We need to manually convert to a pg native boolean value */
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&
+ ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) {
+ const char *s = zend_is_true(¶m->parameter) ? "t" : "f";
+ param->param_type = PDO_PARAM_STR;
+ zval_ptr_dtor(¶m->parameter);
+ ZVAL_STRINGL(¶m->parameter, s, 1);
+ }
+ }
+ return 1;
+}
+
+static int pgsql_stmt_fetch(pdo_stmt_t *stmt,
+ enum pdo_fetch_orientation ori, zend_long offset)
+{
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+
+ if (S->cursor_name) {
+ char *ori_str = NULL;
+ char *q = NULL;
+ ExecStatusType status;
+
+ switch (ori) {
+ case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break;
+ case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break;
+ case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break;
+ case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break;
+ case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); break;
+ case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); break;
+ default:
+ return 0;
+ }
+
+ if(S->result) {
+ PQclear(S->result);
+ S->result = NULL;
+ }
+
+ spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name);
+ efree(ori_str);
+ S->result = PQexec(S->H->server, q);
+ efree(q);
+ status = PQresultStatus(S->result);
+
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+ pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
+ return 0;
+ }
+
+ if (PQntuples(S->result)) {
+ S->current_row = 1;
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ if (S->is_running_unbuffered && S->current_row >= stmt->row_count) {
+ ExecStatusType status;
+
+ /* @todo in unbuffered mode, PQ allows multiple queries to be passed:
+ * column_count should be recomputed on each iteration */
+
+ if(S->result) {
+ PQclear(S->result);
+ S->result = NULL;
+ }
+
+ S->result = PQgetResult(S->H->server);
+ status = PQresultStatus(S->result);
+
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_SINGLE_TUPLE) {
+ pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
+ return 0;
+ }
+
+ stmt->row_count = (zend_long)PQntuples(S->result);
+ S->current_row = 0;
+
+ if (!stmt->row_count) {
+ S->is_running_unbuffered = false;
+ /* libpq requires looping until getResult returns null */
+ pgsql_stmt_finish(S, 0);
+ }
+ }
+ if (S->current_row < stmt->row_count) {
+ S->current_row++;
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno)
+{
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+ struct pdo_column_data *cols = stmt->columns;
+ char *str;
+
+ if (!S->result) {
+ return 0;
+ }
+
+ str = PQfname(S->result, colno);
+ cols[colno].name = zend_string_init(str, strlen(str), 0);
+ cols[colno].maxlen = PQfsize(S->result, colno);
+ cols[colno].precision = PQfmod(S->result, colno);
+ S->cols[colno].pgsql_type = PQftype(S->result, colno);
+
+ return 1;
+}
+
+static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)
+{
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+ if (!S->result) {
+ return 0;
+ }
+
+ /* We have already increased count by 1 in pgsql_stmt_fetch() */
+ if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */
+ ZVAL_NULL(result);
+ } else {
+ char *ptr = PQgetvalue(S->result, S->current_row - 1, colno);
+ size_t len = PQgetlength(S->result, S->current_row - 1, colno);
+
+ switch (S->cols[colno].pgsql_type) {
+ case BOOLOID:
+ ZVAL_BOOL(result, *ptr == 't');
+ break;
+
+ case INT2OID:
+ case INT4OID:
+#if SIZEOF_ZEND_LONG >= 8
+ case INT8OID:
+#endif
+ ZVAL_LONG(result, ZEND_ATOL(ptr));
+ break;
+ case FLOAT4OID:
+ case FLOAT8OID:
+ if (strncmp(ptr, "Infinity", len) == 0) {
+ ZVAL_DOUBLE(result, ZEND_INFINITY);
+ } else if (strncmp(ptr, "-Infinity", len) == 0) {
+ ZVAL_DOUBLE(result, -ZEND_INFINITY);
+ } else if (strncmp(ptr, "NaN", len) == 0) {
+ ZVAL_DOUBLE(result, ZEND_NAN);
+ } else {
+ ZVAL_DOUBLE(result, zend_strtod(ptr, NULL));
+ }
+ break;
+
+ case OIDOID: {
+ char *end_ptr;
+ Oid oid = (Oid)strtoul(ptr, &end_ptr, 10);
+ if (type && *type == PDO_PARAM_LOB) {
+ /* If column was bound as LOB, return a stream. */
+ int loid = lo_open(S->H->server, oid, INV_READ);
+ if (loid >= 0) {
+ php_stream *stream = pdo_pgsql_create_lob_stream(stmt->database_object_handle, loid, oid);
+ if (stream) {
+ php_stream_to_zval(stream, result);
+ return 1;
+ }
+ }
+ return 0;
+ } else {
+ /* Otherwise return OID as integer. */
+ ZVAL_LONG(result, oid);
+ }
+ break;
+ }
+
+ case BYTEAOID: {
+ size_t tmp_len;
+ char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *) ptr, &tmp_len);
+ if (!tmp_ptr) {
+ /* PQunescapeBytea returned an error */
+ return 0;
+ }
+
+ zend_string *str = zend_string_init(tmp_ptr, tmp_len, 0);
+ php_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str);
+ php_stream_to_zval(stream, result);
+ zend_string_release(str);
+ PQfreemem(tmp_ptr);
+ break;
+ }
+
+ default:
+ ZVAL_STRINGL_FAST(result, ptr, len);
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn)
+{
+ char *table_name = NULL;
+ PGresult *tmp_res;
+ char *querystr = NULL;
+
+ spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid);
+
+ if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {
+ if (tmp_res) {
+ PQclear(tmp_res);
+ }
+ efree(querystr);
+ return 0;
+ }
+ efree(querystr);
+
+ if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {
+ PQclear(tmp_res);
+ return 0;
+ }
+
+ table_name = estrdup(table_name);
+
+ PQclear(tmp_res);
+ return table_name;
+}
+
+static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
+{
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+ PGresult *res;
+ char *q=NULL;
+ ExecStatusType status;
+ Oid table_oid;
+ char *table_name=NULL;
+
+ if (!S->result) {
+ return FAILURE;
+ }
+
+ if (colno >= stmt->column_count) {
+ return FAILURE;
+ }
+
+ array_init(return_value);
+ add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type);
+
+ table_oid = PQftable(S->result, colno);
+ add_assoc_long(return_value, "pgsql:table_oid", table_oid);
+ table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server);
+ if (table_name) {
+ add_assoc_string(return_value, "table", table_name);
+ efree(table_name);
+ }
+
+ switch (S->cols[colno].pgsql_type) {
+ case BOOLOID:
+ add_assoc_string(return_value, "native_type", BOOLLABEL);
+ break;
+ case BYTEAOID:
+ add_assoc_string(return_value, "native_type", BYTEALABEL);
+ break;
+ case INT8OID:
+ add_assoc_string(return_value, "native_type", INT8LABEL);
+ break;
+ case INT2OID:
+ add_assoc_string(return_value, "native_type", INT2LABEL);
+ break;
+ case INT4OID:
+ add_assoc_string(return_value, "native_type", INT4LABEL);
+ break;
+ case FLOAT4OID:
+ add_assoc_string(return_value, "native_type", FLOAT4LABEL);
+ break;
+ case FLOAT8OID:
+ add_assoc_string(return_value, "native_type", FLOAT8LABEL);
+ break;
+ case TEXTOID:
+ add_assoc_string(return_value, "native_type", TEXTLABEL);
+ break;
+ case VARCHAROID:
+ add_assoc_string(return_value, "native_type", VARCHARLABEL);
+ break;
+ case DATEOID:
+ add_assoc_string(return_value, "native_type", DATELABEL);
+ break;
+ case TIMESTAMPOID:
+ add_assoc_string(return_value, "native_type", TIMESTAMPLABEL);
+ break;
+ default:
+ /* Fetch metadata from Postgres system catalogue */
+ spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type);
+ res = PQexec(S->H->server, q);
+ efree(q);
+ status = PQresultStatus(res);
+ if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) {
+ add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0));
+ }
+ PQclear(res);
+ }
+
+ enum pdo_param_type param_type;
+ switch (S->cols[colno].pgsql_type) {
+ case BOOLOID:
+ param_type = PDO_PARAM_BOOL;
+ break;
+ case INT2OID:
+ case INT4OID:
+ case INT8OID:
+ param_type = PDO_PARAM_INT;
+ break;
+ case OIDOID:
+ case BYTEAOID:
+ param_type = PDO_PARAM_LOB;
+ break;
+ default:
+ param_type = PDO_PARAM_STR;
+ }
+ add_assoc_long(return_value, "pdo_type", param_type);
+
+ return 1;
+}
+
+static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt)
+{
+ return 1;
+}
+
+static int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val)
+{
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+
+ switch (attr) {
+#ifdef HAVE_PG_RESULT_MEMORY_SIZE
+ case PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE:
+ if(stmt->executed) {
+ ZVAL_LONG(val, PQresultMemorySize(S->result));
+ } else {
+ char *tmp;
+ spprintf(&tmp, 0, "statement '%s' has not been executed yet", S->stmt_name);
+
+ pdo_pgsql_error_stmt_msg(stmt, 0, "HY000", tmp);
+ efree(tmp);
+
+ ZVAL_NULL(val);
+ }
+ return 1;
+#endif
+
+ default:
+ (void)S;
+ return 0;
+ }
+}
+
+const struct pdo_stmt_methods pgsql_stmt_methods = {
+ pgsql_stmt_dtor,
+ pgsql_stmt_execute,
+ pgsql_stmt_fetch,
+ pgsql_stmt_describe,
+ pgsql_stmt_get_col,
+ pgsql_stmt_param_hook,
+ NULL, /* set_attr */
+ pgsql_stmt_get_attr,
+ pgsql_stmt_get_column_meta,
+ NULL, /* next_rowset */
+ pdo_pgsql_stmt_cursor_closer
+};
+#endif
\ No newline at end of file
diff --git a/thirdparty/php85/pdo_pgsql/php_pdo_pgsql.h b/thirdparty/php85/pdo_pgsql/php_pdo_pgsql.h
new file mode 100644
index 00000000000..fa28214f71a
--- /dev/null
+++ b/thirdparty/php85/pdo_pgsql/php_pdo_pgsql.h
@@ -0,0 +1,36 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Edin Kadribasic |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_PDO_PGSQL_H
+#define PHP_PDO_PGSQL_H
+
+#include
+
+extern zend_module_entry pdo_pgsql_module_entry;
+#define phpext_pdo_pgsql_ptr &pdo_pgsql_module_entry
+
+#include "php_version.h"
+#define PHP_PDO_PGSQL_VERSION PHP_VERSION
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(pdo_pgsql);
+PHP_MSHUTDOWN_FUNCTION(pdo_pgsql);
+PHP_MINFO_FUNCTION(pdo_pgsql);
+
+#endif /* PHP_PDO_PGSQL_H */
diff --git a/thirdparty/php85/pdo_pgsql/php_pdo_pgsql_int.h b/thirdparty/php85/pdo_pgsql/php_pdo_pgsql_int.h
new file mode 100644
index 00000000000..881b4e70465
--- /dev/null
+++ b/thirdparty/php85/pdo_pgsql/php_pdo_pgsql_int.h
@@ -0,0 +1,133 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Edin Kadribasic |
+ | Ilia Alshanestsky |
+ | Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+/* internal header; not supposed to be installed */
+
+#ifndef PHP_PDO_PGSQL_INT_H
+#define PHP_PDO_PGSQL_INT_H
+
+#include
+#include
+#include
+
+#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006"
+
+typedef struct {
+ const char *file;
+ int line;
+ unsigned int errcode;
+ char *errmsg;
+} pdo_pgsql_error_info;
+
+typedef struct pdo_pgsql_stmt pdo_pgsql_stmt;
+
+/* stuff we use in a pgsql database handle */
+typedef struct {
+ PGconn *server;
+ unsigned attached:1;
+ unsigned _reserved:31;
+ pdo_pgsql_error_info einfo;
+ Oid pgoid;
+ unsigned int stmt_counter;
+ bool emulate_prepares;
+ bool disable_prepares;
+ HashTable *lob_streams;
+ zend_fcall_info_cache *notice_callback;
+ bool default_fetching_laziness;
+ pdo_pgsql_stmt *running_stmt;
+} pdo_pgsql_db_handle;
+
+typedef struct {
+ Oid pgsql_type;
+} pdo_pgsql_column;
+
+struct pdo_pgsql_stmt {
+ pdo_pgsql_db_handle *H;
+ PGresult *result;
+ pdo_pgsql_column *cols;
+ char *cursor_name;
+ char *stmt_name;
+ zend_string *query;
+ char **param_values;
+ int *param_lengths;
+ int *param_formats;
+ Oid *param_types;
+ int current_row;
+ bool is_prepared;
+ bool is_unbuffered;
+ bool is_running_unbuffered;
+};
+
+typedef struct {
+ Oid oid;
+} pdo_pgsql_bound_param;
+
+extern const pdo_driver_t pdo_pgsql_driver;
+
+extern int pdo_pgsql_scanner(pdo_scanner_t *s);
+
+extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line);
+#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__)
+#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__)
+#define pdo_pgsql_error_stmt(s,e,z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__)
+#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \
+ _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__)
+
+extern const struct pdo_stmt_methods pgsql_stmt_methods;
+
+#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE)
+
+enum {
+ PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC,
+ PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE,
+};
+
+struct pdo_pgsql_lob_self {
+ zval dbh;
+ PGconn *conn;
+ int lfd;
+ Oid oid;
+};
+
+enum pdo_pgsql_specific_constants {
+ PGSQL_TRANSACTION_IDLE = PQTRANS_IDLE,
+ PGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE,
+ PGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS,
+ PGSQL_TRANSACTION_INERROR = PQTRANS_INERROR,
+ PGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN
+};
+
+php_stream *pdo_pgsql_create_lob_stream(zend_object *pdh, int lfd, Oid oid);
+extern const php_stream_ops pdo_pgsql_lob_stream_ops;
+
+void pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H);
+
+void pdo_libpq_version(char *buf, size_t len);
+void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh);
+
+void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS);
+
+#endif /* PHP_PDO_PGSQL_INT_H */
diff --git a/thirdparty/php85/pdo_sqlite/php_pdo_sqlite.h b/thirdparty/php85/pdo_sqlite/php_pdo_sqlite.h
new file mode 100644
index 00000000000..1bba9e3556a
--- /dev/null
+++ b/thirdparty/php85/pdo_sqlite/php_pdo_sqlite.h
@@ -0,0 +1,36 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_PDO_SQLITE_H
+#define PHP_PDO_SQLITE_H
+
+extern zend_module_entry pdo_sqlite_module_entry;
+#define phpext_pdo_sqlite_ptr &pdo_sqlite_module_entry
+
+#include "php_version.h"
+#define PHP_PDO_SQLITE_VERSION PHP_VERSION
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(pdo_sqlite);
+PHP_MSHUTDOWN_FUNCTION(pdo_sqlite);
+PHP_RINIT_FUNCTION(pdo_sqlite);
+PHP_RSHUTDOWN_FUNCTION(pdo_sqlite);
+PHP_MINFO_FUNCTION(pdo_sqlite);
+
+#endif /* PHP_PDO_SQLITE_H */
diff --git a/thirdparty/php85/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php85/pdo_sqlite/php_pdo_sqlite_int.h
new file mode 100644
index 00000000000..69ac003356b
--- /dev/null
+++ b/thirdparty/php85/pdo_sqlite/php_pdo_sqlite_int.h
@@ -0,0 +1,87 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_PDO_SQLITE_INT_H
+#define PHP_PDO_SQLITE_INT_H
+
+#include
+
+typedef struct {
+ const char *file;
+ int line;
+ unsigned int errcode;
+ char *errmsg;
+} pdo_sqlite_error_info;
+
+struct pdo_sqlite_func {
+ struct pdo_sqlite_func *next;
+
+ int argc;
+ zend_string *funcname;
+
+ /* accelerated callback references */
+ zend_fcall_info_cache func;
+ zend_fcall_info_cache step;
+ zend_fcall_info_cache fini;
+};
+
+struct pdo_sqlite_collation {
+ struct pdo_sqlite_collation *next;
+
+ zend_string *name;
+ zend_fcall_info_cache callback;
+};
+
+typedef struct {
+ sqlite3 *db;
+ pdo_sqlite_error_info einfo;
+ struct pdo_sqlite_func *funcs;
+ struct pdo_sqlite_collation *collations;
+ zend_fcall_info_cache authorizer_fcc;
+} pdo_sqlite_db_handle;
+
+typedef struct {
+ pdo_sqlite_db_handle *H;
+ sqlite3_stmt *stmt;
+ unsigned pre_fetched:1;
+ unsigned done:1;
+} pdo_sqlite_stmt;
+
+extern const pdo_driver_t pdo_sqlite_driver;
+
+extern int pdo_sqlite_scanner(pdo_scanner_t *s);
+
+extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line);
+#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__)
+#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__)
+
+extern const struct pdo_stmt_methods sqlite_stmt_methods;
+
+enum {
+ PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC,
+ PDO_SQLITE_ATTR_READONLY_STATEMENT,
+ PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES,
+ PDO_SQLITE_ATTR_BUSY_STATEMENT,
+ PDO_SQLITE_ATTR_EXPLAIN_STATEMENT
+};
+
+typedef int pdo_sqlite_create_collation_callback(void*, int, const void*, int, const void*);
+
+void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS);
+void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback);
+
+#endif
diff --git a/thirdparty/php85/pdo_sqlite/sqlite_driver.c b/thirdparty/php85/pdo_sqlite/sqlite_driver.c
new file mode 100644
index 00000000000..12739917af0
--- /dev/null
+++ b/thirdparty/php85/pdo_sqlite/sqlite_driver.c
@@ -0,0 +1,931 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#define SW_USE_SQLITE_HOOK
+#include "php_swoole_sqlite.h"
+#include "php_swoole_call_stack.h"
+
+#if PHP_VERSION_ID >= 80500
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "ext/pdo/php_pdo.h"
+#include "ext/pdo/php_pdo_driver.h"
+#include "php_pdo_sqlite.h"
+#include "php_pdo_sqlite_int.h"
+#include "zend_exceptions.h"
+#include "sqlite_driver_arginfo.h"
+
+int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+ pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
+ pdo_sqlite_error_info *einfo = &H->einfo;
+
+ einfo->errcode = sqlite3_errcode(H->db);
+ einfo->file = file;
+ einfo->line = line;
+
+ if (einfo->errcode != SQLITE_OK) {
+ if (einfo->errmsg) {
+ pefree(einfo->errmsg, dbh->is_persistent);
+ }
+ einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent);
+ } else { /* no error */
+ strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err));
+ return 0;
+ }
+ switch (einfo->errcode) {
+ case SQLITE_NOTFOUND:
+ strncpy(*pdo_err, "42S02", sizeof(*pdo_err));
+ break;
+
+ case SQLITE_INTERRUPT:
+ strncpy(*pdo_err, "01002", sizeof(*pdo_err));
+ break;
+
+ case SQLITE_NOLFS:
+ strncpy(*pdo_err, "HYC00", sizeof(*pdo_err));
+ break;
+
+ case SQLITE_TOOBIG:
+ strncpy(*pdo_err, "22001", sizeof(*pdo_err));
+ break;
+
+ case SQLITE_CONSTRAINT:
+ strncpy(*pdo_err, "23000", sizeof(*pdo_err));
+ break;
+
+ case SQLITE_ERROR:
+ default:
+ strncpy(*pdo_err, "HY000", sizeof(*pdo_err));
+ break;
+ }
+
+ if (!dbh->methods) {
+ pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);
+ }
+
+ return einfo->errcode;
+}
+/* }}} */
+
+static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+ pdo_sqlite_error_info *einfo = &H->einfo;
+
+ if (einfo->errcode) {
+ add_next_index_long(info, einfo->errcode);
+ add_next_index_string(info, einfo->errmsg);
+ }
+}
+
+static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H)
+{
+ struct pdo_sqlite_func *func;
+
+ if (ZEND_FCC_INITIALIZED(H->authorizer_fcc)) {
+ zend_fcc_dtor(&H->authorizer_fcc);
+ }
+
+ while (H->funcs) {
+ func = H->funcs;
+ H->funcs = func->next;
+
+ if (H->db) {
+ /* delete the function from the handle */
+ sqlite3_create_function(H->db,
+ ZSTR_VAL(func->funcname),
+ func->argc,
+ SQLITE_UTF8,
+ func,
+ NULL, NULL, NULL);
+ }
+
+ zend_string_release(func->funcname);
+ if (ZEND_FCC_INITIALIZED(func->func)) {
+ zend_fcc_dtor(&func->func);
+ }
+ if (ZEND_FCC_INITIALIZED(func->step)) {
+ zend_fcc_dtor(&func->step);
+ }
+ if (ZEND_FCC_INITIALIZED(func->fini)) {
+ zend_fcc_dtor(&func->fini);
+ }
+ efree(func);
+ }
+
+ while (H->collations) {
+ struct pdo_sqlite_collation *collation;
+ collation = H->collations;
+ H->collations = collation->next;
+
+ if (H->db) {
+ /* delete the collation from the handle */
+ sqlite3_create_collation(H->db,
+ ZSTR_VAL(collation->name),
+ SQLITE_UTF8,
+ collation,
+ NULL);
+ }
+
+ zend_string_release(collation->name);
+ if (ZEND_FCC_INITIALIZED(collation->callback)) {
+ zend_fcc_dtor(&collation->callback);
+ }
+ efree(collation);
+ }
+}
+
+static void sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ if (H) {
+ pdo_sqlite_error_info *einfo = &H->einfo;
+
+ pdo_sqlite_cleanup_callbacks(H);
+ if (H->db) {
+#ifdef HAVE_SQLITE3_CLOSE_V2
+ sqlite3_close_v2(H->db);
+#else
+ sqlite3_close(H->db);
+#endif
+ H->db = NULL;
+ }
+ if (einfo->errmsg) {
+ pefree(einfo->errmsg, dbh->is_persistent);
+ einfo->errmsg = NULL;
+ }
+ pefree(H, dbh->is_persistent);
+ dbh->driver_data = NULL;
+ }
+}
+/* }}} */
+
+static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+ pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt));
+ int i;
+ const char *tail;
+
+ S->H = H;
+ stmt->driver_data = S;
+ stmt->methods = &sqlite_stmt_methods;
+ stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED;
+
+ if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) {
+ H->einfo.errcode = SQLITE_ERROR;
+ pdo_sqlite_error(dbh);
+ return false;
+ }
+
+ i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail);
+ if (i == SQLITE_OK) {
+ return true;
+ }
+
+ pdo_sqlite_error(dbh);
+
+ return false;
+}
+
+static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, NULL) != SQLITE_OK) {
+ pdo_sqlite_error(dbh);
+ return -1;
+ } else {
+ return sqlite3_changes(H->db);
+ }
+}
+
+static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ return zend_i64_to_str(sqlite3_last_insert_rowid(H->db));
+}
+
+/* NB: doesn't handle binary strings... use prepared stmts for that */
+static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
+{
+ char *quoted;
+ if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) {
+ return NULL;
+ }
+
+ if (UNEXPECTED(zend_str_has_nul_byte(unquoted))) {
+ if (dbh->error_mode == PDO_ERRMODE_EXCEPTION) {
+ zend_throw_exception_ex(
+ php_pdo_get_exception(), 0,
+ "SQLite PDO::quote does not support null bytes");
+ } else if (dbh->error_mode == PDO_ERRMODE_WARNING) {
+ php_error_docref(NULL, E_WARNING,
+ "SQLite PDO::quote does not support null bytes");
+ }
+ return NULL;
+ }
+
+ quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);
+ /* TODO use %Q format? */
+ sqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted));
+ zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0);
+ efree(quoted);
+ return quoted_str;
+}
+
+static bool sqlite_handle_begin(pdo_dbh_t *dbh)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK) {
+ pdo_sqlite_error(dbh);
+ return false;
+ }
+ return true;
+}
+
+static bool sqlite_handle_commit(pdo_dbh_t *dbh)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) {
+ pdo_sqlite_error(dbh);
+ return false;
+ }
+ return true;
+}
+
+static bool sqlite_handle_rollback(pdo_dbh_t *dbh)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, NULL) != SQLITE_OK) {
+ pdo_sqlite_error(dbh);
+ return false;
+ }
+ return true;
+}
+
+static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)
+{
+ switch (attr) {
+ case PDO_ATTR_CLIENT_VERSION:
+ case PDO_ATTR_SERVER_VERSION:
+ ZVAL_STRING(return_value, (char *)sqlite3_libversion());
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static bool pdo_sqlite_in_transaction(pdo_dbh_t *dbh)
+{
+ pdo_sqlite_db_handle* H = (pdo_sqlite_db_handle*) dbh->driver_data;
+ /* It's not possible in sqlite3 to explicitly turn autocommit off other
+ * than manually starting a transaction. Manual transactions always are
+ * the mode of operation when autocommit is off. */
+ return H->db && sqlite3_get_autocommit(H->db) == 0;
+}
+
+static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+ zend_long lval;
+
+ switch (attr) {
+ case PDO_ATTR_TIMEOUT:
+ if (!pdo_get_long_param(&lval, val)) {
+ return false;
+ }
+ sqlite3_busy_timeout(H->db, lval * 1000);
+ return true;
+ case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES:
+ if (!pdo_get_long_param(&lval, val)) {
+ return false;
+ }
+ sqlite3_extended_result_codes(H->db, lval);
+ return true;
+ }
+ return false;
+}
+
+typedef struct {
+ zval val;
+ zend_long row;
+} aggregate_context;
+
+static int do_callback(zend_fcall_info_cache *fcc, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg)
+{
+ zval *zargs = NULL;
+ zval retval;
+ int i;
+ int ret = SUCCESS;
+ int fake_argc;
+ aggregate_context *agg_context = NULL;
+
+ if (is_agg) {
+ is_agg = 2;
+ }
+
+ fake_argc = argc + is_agg;
+
+ /* build up the params */
+ if (fake_argc) {
+ zargs = safe_emalloc(fake_argc, sizeof(zval), 0);
+ }
+
+ if (is_agg) {
+ agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context));
+ if (!agg_context) {
+ efree(zargs);
+ return FAILURE;
+ }
+ if (Z_ISUNDEF(agg_context->val)) {
+ ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval));
+ }
+ ZVAL_COPY_VALUE(&zargs[0], &agg_context->val);
+ ZVAL_LONG(&zargs[1], ++agg_context->row);
+ }
+
+ for (i = 0; i < argc; i++) {
+ /* get the value */
+ switch (sqlite3_value_type(argv[i])) {
+ case SQLITE_INTEGER:
+ ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i]));
+ break;
+
+ case SQLITE_FLOAT:
+ ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i]));
+ break;
+
+ case SQLITE_NULL:
+ ZVAL_NULL(&zargs[i + is_agg]);
+ break;
+
+ case SQLITE_BLOB:
+ case SQLITE3_TEXT:
+ default:
+ ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i]));
+ break;
+ }
+ }
+
+ zend_call_known_fcc(fcc, &retval, fake_argc, zargs, /* named_params */ NULL);
+
+ /* clean up the params */
+ if (zargs) {
+ for (i = is_agg; i < fake_argc; i++) {
+ zval_ptr_dtor(&zargs[i]);
+ }
+ if (is_agg) {
+ zval_ptr_dtor(&zargs[1]);
+ }
+ efree(zargs);
+ }
+
+ if (!is_agg || !argv) {
+ /* only set the sqlite return value if we are a scalar function,
+ * or if we are finalizing an aggregate */
+ if (!Z_ISUNDEF(retval)) {
+ switch (Z_TYPE(retval)) {
+ case IS_LONG:
+ sqlite3_result_int(context, Z_LVAL(retval));
+ break;
+
+ case IS_NULL:
+ sqlite3_result_null(context);
+ break;
+
+ case IS_DOUBLE:
+ sqlite3_result_double(context, Z_DVAL(retval));
+ break;
+
+ default:
+ if (!try_convert_to_string(&retval)) {
+ ret = FAILURE;
+ break;
+ }
+ sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT);
+ break;
+ }
+ } else {
+ sqlite3_result_error(context, "failed to invoke callback", 0);
+ }
+
+ if (agg_context) {
+ zval_ptr_dtor(&agg_context->val);
+ }
+ } else {
+ /* we're stepping in an aggregate; the return value goes into
+ * the context */
+ if (agg_context) {
+ if (Z_ISUNDEF(retval)) {
+ return FAILURE;
+ }
+ zval_ptr_dtor(Z_REFVAL(agg_context->val));
+ ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval);
+ ZVAL_UNDEF(&retval);
+ }
+ }
+
+ if (!Z_ISUNDEF(retval)) {
+ zval_ptr_dtor(&retval);
+ }
+
+ return ret;
+}
+
+static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, sqlite3_value **argv)
+{
+ struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context);
+
+ do_callback(&func->step, argc, argv, context, 1);
+}
+
+static void php_sqlite3_func_final_callback(sqlite3_context *context)
+{
+ struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context);
+
+ do_callback(&func->fini, 0, NULL, context, 1);
+}
+
+static int php_sqlite3_collation_callback(void *context, int string1_len, const void *string1, int string2_len, const void *string2)
+{
+ int ret = 0;
+ zval zargs[2];
+ zval retval;
+ struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context;
+
+ /* Prepare the arguments. */
+ ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len);
+ ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len);
+
+ zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL);
+
+ zval_ptr_dtor(&zargs[0]);
+ zval_ptr_dtor(&zargs[1]);
+
+ if (!Z_ISUNDEF(retval)) {
+ if (Z_TYPE(retval) != IS_LONG) {
+ zend_string *func_name = get_active_function_or_method_name();
+ zend_type_error("%s(): Return value of the collation callback must be of type int, %s returned",
+ ZSTR_VAL(func_name), zend_zval_value_name(&retval));
+ zend_string_release(func_name);
+ zval_ptr_dtor(&retval);
+ return FAILURE;
+ }
+ ret = ZEND_NORMALIZE_BOOL(Z_LVAL(retval));
+ }
+
+ return ret;
+}
+
+static void php_sqlite3_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv)
+{
+ struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context);
+
+ do_callback(&func->func, argc, argv, context, 0);
+}
+
+void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ struct pdo_sqlite_func *func;
+ zend_fcall_info fci = empty_fcall_info;
+ zend_fcall_info_cache fcc = empty_fcall_info_cache;
+ zend_string *func_name;
+ zend_long argc = -1;
+ zend_long flags = 0;
+ pdo_dbh_t *dbh;
+ pdo_sqlite_db_handle *H;
+ int ret;
+
+ ZEND_PARSE_PARAMETERS_START(2, 4)
+ Z_PARAM_STR(func_name)
+ Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_LONG(argc)
+ Z_PARAM_LONG(flags)
+ ZEND_PARSE_PARAMETERS_END_EX(goto error;);
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK_WITH_CLEANUP(error);
+
+ H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func));
+
+ ret = sqlite3_create_function(H->db, ZSTR_VAL(func_name), argc, flags | SQLITE_UTF8, func, php_sqlite3_func_callback, NULL, NULL);
+ if (ret == SQLITE_OK) {
+ func->funcname = zend_string_copy(func_name);
+
+ zend_fcc_dup(&func->func, &fcc);
+
+ func->argc = argc;
+
+ func->next = H->funcs;
+ H->funcs = func;
+
+ RETURN_TRUE;
+ }
+
+ efree(func);
+
+error:
+ zend_release_fcall_info_cache(&fcc);
+ RETURN_FALSE;
+}
+
+/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags])
+ Registers a UDF with the sqlite db handle */
+PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction)
+{
+ pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS)
+{
+ struct pdo_sqlite_func *func;
+ zend_fcall_info step_fci = empty_fcall_info;
+ zend_fcall_info fini_fci = empty_fcall_info;
+ zend_fcall_info_cache step_fcc = empty_fcall_info_cache;
+ zend_fcall_info_cache fini_fcc = empty_fcall_info_cache;
+ zend_string *func_name;
+ zend_long argc = -1;
+ pdo_dbh_t *dbh;
+ pdo_sqlite_db_handle *H;
+ int ret;
+
+ ZEND_PARSE_PARAMETERS_START(3, 4)
+ Z_PARAM_STR(func_name)
+ Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(step_fci, step_fcc)
+ Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fini_fci, fini_fcc)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_LONG(argc)
+ ZEND_PARSE_PARAMETERS_END_EX(goto error;);
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK_WITH_CLEANUP(error);
+
+ H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func));
+
+ ret = sqlite3_create_function(H->db, ZSTR_VAL(func_name), argc, SQLITE_UTF8, func, NULL,
+ php_sqlite3_func_step_callback, php_sqlite3_func_final_callback);
+ if (ret == SQLITE_OK) {
+ func->funcname = zend_string_copy(func_name);
+
+ zend_fcc_dup(&func->step, &step_fcc);
+ zend_fcc_dup(&func->fini, &fini_fcc);
+
+ func->argc = argc;
+
+ func->next = H->funcs;
+ H->funcs = func;
+
+ RETURN_TRUE;
+ }
+
+ efree(func);
+
+error:
+ zend_release_fcall_info_cache(&step_fcc);
+ zend_release_fcall_info_cache(&fini_fcc);
+ RETURN_FALSE;
+}
+
+/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount])
+ Registers a UDF with the sqlite db handle */
+
+/* The step function should have the prototype:
+ mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]])
+
+ $context will be null for the first row; on subsequent rows it will have
+ the value that was previously returned from the step function; you should
+ use this to maintain state for the aggregate.
+
+ The fini function should have the prototype:
+ mixed fini(mixed $context, int $rownumber)
+
+ $context will hold the return value from the very last call to the step function.
+ rownumber will hold the number of rows over which the aggregate was performed.
+ The return value of this function will be used as the return value for this
+ aggregate UDF.
+*/
+
+PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate)
+{
+ pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback)
+{
+ struct pdo_sqlite_collation *collation;
+ zend_fcall_info fci = empty_fcall_info;
+ zend_fcall_info_cache fcc = empty_fcall_info_cache;
+ zend_string *collation_name;
+ pdo_dbh_t *dbh;
+ pdo_sqlite_db_handle *H;
+ int ret;
+
+ ZEND_PARSE_PARAMETERS_START(2, 2)
+ Z_PARAM_STR(collation_name)
+ Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc)
+ ZEND_PARSE_PARAMETERS_END();
+
+ dbh = Z_PDO_DBH_P(ZEND_THIS);
+ PDO_CONSTRUCT_CHECK_WITH_CLEANUP(cleanup_fcc);
+
+ H = (pdo_sqlite_db_handle *)dbh->driver_data;
+
+ collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation));
+
+ ret = sqlite3_create_collation(H->db, ZSTR_VAL(collation_name), SQLITE_UTF8, collation, callback);
+ if (ret == SQLITE_OK) {
+ collation->name = zend_string_copy(collation_name);
+
+ zend_fcc_dup(&collation->callback, &fcc);
+
+ collation->next = H->collations;
+ H->collations = collation;
+
+ RETURN_TRUE;
+ }
+
+ zend_release_fcall_info_cache(&fcc);
+
+ efree(collation);
+ RETURN_FALSE;
+
+cleanup_fcc:
+ zend_release_fcall_info_cache(&fcc);
+ RETURN_THROWS();
+}
+
+/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback)
+ Registers a collation with the sqlite db handle */
+PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation)
+{
+ pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite3_collation_callback);
+}
+/* }}} */
+
+static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind)
+{
+ switch (kind) {
+ case PDO_DBH_DRIVER_METHOD_KIND_DBH:
+ return class_PDO_SQLite_Ext_methods;
+
+ default:
+ return NULL;
+ }
+}
+
+static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh)
+{
+ pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;
+ /* unregister functions, so that they don't linger for the next
+ * request */
+ if (H) {
+ pdo_sqlite_cleanup_callbacks(H);
+ }
+}
+
+static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer)
+{
+ pdo_sqlite_db_handle *H = dbh->driver_data;
+
+ if (ZEND_FCC_INITIALIZED(H->authorizer_fcc)) {
+ zend_get_gc_buffer_add_fcc(gc_buffer, &H->authorizer_fcc);
+ }
+
+ struct pdo_sqlite_func *func = H->funcs;
+ while (func) {
+ if (ZEND_FCC_INITIALIZED(func->func)) {
+ zend_get_gc_buffer_add_fcc(gc_buffer, &func->func);
+ }
+ if (ZEND_FCC_INITIALIZED(func->step)) {
+ zend_get_gc_buffer_add_fcc(gc_buffer, &func->step);
+ }
+ if (ZEND_FCC_INITIALIZED(func->fini)) {
+ zend_get_gc_buffer_add_fcc(gc_buffer, &func->fini);
+ }
+ func = func->next;
+ }
+
+ struct pdo_sqlite_collation *collation = H->collations;
+ while (collation) {
+ if (ZEND_FCC_INITIALIZED(collation->callback)) {
+ zend_get_gc_buffer_add_fcc(gc_buffer, &collation->callback);
+ }
+ collation = collation->next;
+ }
+}
+
+static const struct pdo_dbh_methods sqlite_methods = {
+ sqlite_handle_closer,
+ sqlite_handle_preparer,
+ sqlite_handle_doer,
+ sqlite_handle_quoter,
+ sqlite_handle_begin,
+ sqlite_handle_commit,
+ sqlite_handle_rollback,
+ pdo_sqlite_set_attr,
+ pdo_sqlite_last_insert_id,
+ pdo_sqlite_fetch_error_func,
+ pdo_sqlite_get_attribute,
+ NULL, /* check_liveness: not needed */
+ get_driver_methods,
+ pdo_sqlite_request_shutdown,
+ pdo_sqlite_in_transaction,
+ pdo_sqlite_get_gc,
+ pdo_sqlite_scanner
+};
+
+static char *make_filename_safe(const char *filename)
+{
+ if (!filename) {
+ return NULL;
+ }
+ if (*filename && strncasecmp(filename, "file:", 5) == 0) {
+ if (PG(open_basedir) && *PG(open_basedir)) {
+ return NULL;
+ }
+ return estrdup(filename);
+ }
+ if (*filename && strcmp(filename, ":memory:")) {
+ char *fullpath = expand_filepath(filename, NULL);
+
+ if (!fullpath) {
+ return NULL;
+ }
+
+ if (php_check_open_basedir(fullpath)) {
+ efree(fullpath);
+ return NULL;
+ }
+ return fullpath;
+ }
+ return estrdup(filename);
+}
+
+#define ZVAL_NULLABLE_STRING(zv, str) do { \
+ zval *zv_ = zv; \
+ const char *str_ = str; \
+ if (str_) { \
+ ZVAL_STRING(zv_, str_); \
+ } else { \
+ ZVAL_NULL(zv_); \
+ } \
+} while (0)
+
+static int authorizer(void *autharg, int access_type, const char *arg1, const char *arg2,
+ const char *arg3, const char *arg4)
+{
+ if (PG(open_basedir) && *PG(open_basedir)) {
+ if (access_type == SQLITE_ATTACH) {
+ char *filename = make_filename_safe(arg1);
+ if (!filename) {
+ return SQLITE_DENY;
+ }
+ efree(filename);
+ }
+ }
+
+ pdo_sqlite_db_handle *db_obj = autharg;
+
+ /* fallback to access allowed if authorizer callback is not defined */
+ if (!ZEND_FCC_INITIALIZED(db_obj->authorizer_fcc)) {
+ return SQLITE_OK;
+ }
+
+ /* call userland authorizer callback, if set */
+ zval retval;
+ zval argv[5];
+
+ ZVAL_LONG(&argv[0], access_type);
+ ZVAL_NULLABLE_STRING(&argv[1], arg1);
+ ZVAL_NULLABLE_STRING(&argv[2], arg2);
+ ZVAL_NULLABLE_STRING(&argv[3], arg3);
+ ZVAL_NULLABLE_STRING(&argv[4], arg4);
+
+ int authreturn = SQLITE_DENY;
+
+ zend_call_known_fcc(&db_obj->authorizer_fcc, &retval, /* argc */ 5, argv, /* named_params */ NULL);
+ if (Z_ISUNDEF(retval)) {
+ ZEND_ASSERT(EG(exception));
+ } else {
+ if (Z_TYPE(retval) != IS_LONG) {
+ zend_string *func_name = get_active_function_or_method_name();
+ zend_type_error("%s(): Return value of the authorizer callback must be of type int, %s returned",
+ ZSTR_VAL(func_name), zend_zval_value_name(&retval));
+ zend_string_release(func_name);
+ } else {
+ authreturn = Z_LVAL(retval);
+
+ if (authreturn != SQLITE_OK && authreturn != SQLITE_IGNORE && authreturn != SQLITE_DENY) {
+ zend_string *func_name = get_active_function_or_method_name();
+ zend_value_error("%s(): Return value of the authorizer callback must be one of Pdo\\Sqlite::OK, Pdo\\Sqlite::DENY, or Pdo\\Sqlite::IGNORE",
+ ZSTR_VAL(func_name));
+ zend_string_release(func_name);
+ authreturn = SQLITE_DENY;
+ }
+ }
+ }
+
+ zval_ptr_dtor(&retval);
+ zval_ptr_dtor(&argv[1]);
+ zval_ptr_dtor(&argv[2]);
+ zval_ptr_dtor(&argv[3]);
+ zval_ptr_dtor(&argv[4]);
+
+ return authreturn;
+}
+
+static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
+{
+ pdo_sqlite_db_handle *H;
+ int i, ret = 0;
+ zend_long timeout = 60, flags;
+ char *filename;
+
+ H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent);
+
+ H->einfo.errcode = 0;
+ H->einfo.errmsg = NULL;
+ dbh->driver_data = H;
+
+ /* skip all but this one param event */
+ dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE);
+
+ filename = make_filename_safe(dbh->data_source);
+
+ if (!filename) {
+ zend_throw_exception_ex(php_pdo_get_exception(), 0,
+ "open_basedir prohibits opening %s",
+ dbh->data_source);
+ goto cleanup;
+ }
+
+ flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
+
+ if (!(PG(open_basedir) && *PG(open_basedir))) {
+ flags |= SQLITE_OPEN_URI;
+ }
+ i = sqlite3_open_v2(filename, &H->db, flags, NULL);
+
+ efree(filename);
+
+ if (i != SQLITE_OK) {
+ pdo_sqlite_error(dbh);
+ goto cleanup;
+ }
+
+ sqlite3_set_authorizer(H->db, authorizer, H);
+
+ if (driver_options) {
+ timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout);
+ }
+ sqlite3_busy_timeout(H->db, timeout * 1000);
+
+ dbh->alloc_own_columns = 1;
+ dbh->max_escaped_char_length = 2;
+
+ ret = 1;
+
+cleanup:
+ dbh->methods = &sqlite_methods;
+
+ return ret;
+}
+/* }}} */
+
+const pdo_driver_t swoole_pdo_sqlite_driver = {
+ PDO_DRIVER_HEADER(sqlite),
+ pdo_sqlite_handle_factory
+};
+#endif
\ No newline at end of file
diff --git a/thirdparty/php85/pdo_sqlite/sqlite_driver_arginfo.h b/thirdparty/php85/pdo_sqlite/sqlite_driver_arginfo.h
new file mode 100644
index 00000000000..8785c187a97
--- /dev/null
+++ b/thirdparty/php85/pdo_sqlite/sqlite_driver_arginfo.h
@@ -0,0 +1,32 @@
+/* This is a generated file, edit the .stub.php file instead.
+ * Stub hash: dc901bd60d17c1a2cdb40a118e2c6cd6eb0396e3 */
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_SQLite_Ext_sqliteCreateFunction, 0, 2, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, numArgs, IS_LONG, 0, "-1")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_SQLite_Ext_sqliteCreateAggregate, 0, 3, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, step, IS_CALLABLE, 0)
+ ZEND_ARG_TYPE_INFO(0, finalize, IS_CALLABLE, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, numArgs, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_SQLite_Ext_sqliteCreateCollation, 0, 2, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_METHOD(PDO_SQLite_Ext, sqliteCreateFunction);
+ZEND_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate);
+ZEND_METHOD(PDO_SQLite_Ext, sqliteCreateCollation);
+
+static const zend_function_entry class_PDO_SQLite_Ext_methods[] = {
+ ZEND_ME(PDO_SQLite_Ext, sqliteCreateFunction, arginfo_class_PDO_SQLite_Ext_sqliteCreateFunction, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_SQLite_Ext, sqliteCreateAggregate, arginfo_class_PDO_SQLite_Ext_sqliteCreateAggregate, ZEND_ACC_PUBLIC)
+ ZEND_ME(PDO_SQLite_Ext, sqliteCreateCollation, arginfo_class_PDO_SQLite_Ext_sqliteCreateCollation, ZEND_ACC_PUBLIC)
+ ZEND_FE_END
+};
diff --git a/thirdparty/php85/pdo_sqlite/sqlite_sql_parser.c b/thirdparty/php85/pdo_sqlite/sqlite_sql_parser.c
new file mode 100644
index 00000000000..47406f3b6d4
--- /dev/null
+++ b/thirdparty/php85/pdo_sqlite/sqlite_sql_parser.c
@@ -0,0 +1,419 @@
+/* Generated by re2c 4.3 */
+#line 1 "ext/pdo_sqlite/sqlite_sql_parser.re"
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Matteo Beccati |
+ +----------------------------------------------------------------------+
+*/
+
+
+#include "php.h"
+#include "ext/pdo/php_pdo_driver.h"
+#include "ext/pdo/pdo_sql_parser.h"
+
+int pdo_sqlite_scanner(pdo_scanner_t *s)
+{
+ const char *cursor = s->cur;
+
+ s->tok = cursor;
+ #line 34 "ext/pdo_sqlite/sqlite_sql_parser.re"
+
+
+
+#line 34 "ext/pdo_sqlite/sqlite_sql_parser.c"
+{
+ YYCTYPE yych;
+ unsigned int yyaccept = 0;
+ if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 0x00: goto yy1;
+ case '"': goto yy4;
+ case '\'': goto yy6;
+ case '-': goto yy7;
+ case '/': goto yy8;
+ case ':': goto yy9;
+ case '?': goto yy10;
+ case '[': goto yy12;
+ case '`': goto yy13;
+ default: goto yy2;
+ }
+yy1:
+ YYCURSOR = YYMARKER;
+ switch (yyaccept) {
+ case 0: goto yy5;
+ case 1: goto yy17;
+ case 2: goto yy21;
+ case 3: goto yy33;
+ default: goto yy37;
+ }
+yy2:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 0x00:
+ case '"':
+ case '\'':
+ case '-':
+ case '/':
+ case ':':
+ case '?':
+ case '[':
+ case '`': goto yy3;
+ default: goto yy2;
+ }
+yy3:
+#line 46 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_TEXT); }
+#line 80 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy4:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych >= 0x01) goto yy15;
+yy5:
+#line 44 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { SKIP_ONE(PDO_PARSER_TEXT); }
+#line 88 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy6:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 0x00) goto yy5;
+ goto yy19;
+yy7:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case '-': goto yy22;
+ default: goto yy5;
+ }
+yy8:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case '*': goto yy24;
+ default: goto yy5;
+ }
+yy9:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '_':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy25;
+ case ':': goto yy27;
+ default: goto yy5;
+ }
+yy10:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case '?': goto yy29;
+ default: goto yy11;
+ }
+yy11:
+#line 43 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_BIND_POS); }
+#line 184 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy12:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 0x00) goto yy5;
+ goto yy31;
+yy13:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 0x00) goto yy5;
+ goto yy35;
+yy14:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy15:
+ switch (yych) {
+ case 0x00: goto yy1;
+ case '"': goto yy16;
+ default: goto yy14;
+ }
+yy16:
+ yyaccept = 1;
+ YYMARKER = ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 0x00: goto yy17;
+ case '"': goto yy16;
+ default: goto yy14;
+ }
+yy17:
+#line 37 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_TEXT); }
+#line 218 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy18:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy19:
+ switch (yych) {
+ case 0x00: goto yy1;
+ case '\'': goto yy20;
+ default: goto yy18;
+ }
+yy20:
+ yyaccept = 2;
+ YYMARKER = ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 0x00: goto yy21;
+ case '\'': goto yy20;
+ default: goto yy18;
+ }
+yy21:
+#line 38 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_TEXT); }
+#line 242 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy22:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case '\n': goto yy23;
+ default: goto yy22;
+ }
+yy23:
+#line 45 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_TEXT); }
+#line 254 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy24:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case '*': goto yy38;
+ default: goto yy24;
+ }
+yy25:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '_':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy25;
+ default: goto yy26;
+ }
+yy26:
+#line 42 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_BIND); }
+#line 336 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy27:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case ':': goto yy27;
+ default: goto yy28;
+ }
+yy28:
+#line 41 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_TEXT); }
+#line 348 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy29:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case '?': goto yy29;
+ default: goto yy28;
+ }
+yy30:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy31:
+ switch (yych) {
+ case 0x00: goto yy1;
+ case ']': goto yy32;
+ default: goto yy30;
+ }
+yy32:
+ yyaccept = 3;
+ YYMARKER = ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 0x00: goto yy33;
+ case ']': goto yy32;
+ default: goto yy30;
+ }
+yy33:
+#line 40 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_TEXT); }
+#line 380 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy34:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy35:
+ switch (yych) {
+ case 0x00: goto yy1;
+ case '`': goto yy36;
+ default: goto yy34;
+ }
+yy36:
+ yyaccept = 4;
+ YYMARKER = ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 0x00: goto yy37;
+ case '`': goto yy36;
+ default: goto yy34;
+ }
+yy37:
+#line 39 "ext/pdo_sqlite/sqlite_sql_parser.re"
+ { RET(PDO_PARSER_TEXT); }
+#line 404 "ext/pdo_sqlite/sqlite_sql_parser.c"
+yy38:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case '*': goto yy38;
+ case '/': goto yy39;
+ default: goto yy24;
+ }
+yy39:
+ ++YYCURSOR;
+ goto yy23;
+}
+#line 47 "ext/pdo_sqlite/sqlite_sql_parser.re"
+
+}
diff --git a/thirdparty/php85/pdo_sqlite/sqlite_statement.c b/thirdparty/php85/pdo_sqlite/sqlite_statement.c
new file mode 100644
index 00000000000..c1e0d8973d4
--- /dev/null
+++ b/thirdparty/php85/pdo_sqlite/sqlite_statement.c
@@ -0,0 +1,472 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong |
+ +----------------------------------------------------------------------+
+*/
+
+#define SW_USE_SQLITE_HOOK
+#include "php_swoole_sqlite.h"
+
+#if PHP_VERSION_ID >= 80500
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "ext/pdo/php_pdo.h"
+#include "ext/pdo/php_pdo_driver.h"
+#include "php_pdo_sqlite.h"
+#include "php_pdo_sqlite_int.h"
+
+#if defined(__APPLE__)
+// If more than one usage, a Zend macro could be created
+// around this runtime check
+#include
+#endif
+
+static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+
+ if (S->stmt) {
+ sqlite3_finalize(S->stmt);
+ S->stmt = NULL;
+ }
+ efree(S);
+ return 1;
+}
+
+static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+
+ if (stmt->executed && !S->done) {
+ sqlite3_reset(S->stmt);
+ }
+
+ S->done = 0;
+ switch (sqlite3_step(S->stmt)) {
+ case SQLITE_ROW:
+ S->pre_fetched = 1;
+ php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt));
+ return 1;
+
+ case SQLITE_DONE:
+ php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt));
+ stmt->row_count = sqlite3_changes(S->H->db);
+ sqlite3_reset(S->stmt);
+ S->done = 1;
+ return 1;
+
+ case SQLITE_ERROR:
+ sqlite3_reset(S->stmt);
+ ZEND_FALLTHROUGH;
+ case SQLITE_MISUSE:
+ case SQLITE_BUSY:
+ default:
+ pdo_sqlite_error_stmt(stmt);
+ return 0;
+ }
+}
+
+static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
+ enum pdo_param_event event_type)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+ zval *parameter;
+
+ switch (event_type) {
+ case PDO_PARAM_EVT_EXEC_PRE:
+ if (stmt->executed && !S->done) {
+ sqlite3_reset(S->stmt);
+ S->done = 1;
+ }
+
+ if (param->is_param) {
+
+ if (param->paramno == -1) {
+ param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1;
+ }
+
+ switch (PDO_PARAM_TYPE(param->param_type)) {
+ case PDO_PARAM_STMT:
+ return 0;
+
+ case PDO_PARAM_NULL:
+ if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) {
+ return 1;
+ }
+ pdo_sqlite_error_stmt(stmt);
+ return 0;
+
+ case PDO_PARAM_INT:
+ case PDO_PARAM_BOOL:
+ if (Z_ISREF(param->parameter)) {
+ parameter = Z_REFVAL(param->parameter);
+ } else {
+ parameter = ¶m->parameter;
+ }
+ if (Z_TYPE_P(parameter) == IS_NULL) {
+ if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) {
+ return 1;
+ }
+ } else {
+ convert_to_long(parameter);
+#if ZEND_LONG_MAX > 2147483647
+ if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) {
+ return 1;
+ }
+#else
+ if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) {
+ return 1;
+ }
+#endif
+ }
+ pdo_sqlite_error_stmt(stmt);
+ return 0;
+
+ case PDO_PARAM_LOB:
+ if (Z_ISREF(param->parameter)) {
+ parameter = Z_REFVAL(param->parameter);
+ } else {
+ parameter = ¶m->parameter;
+ }
+ if (Z_TYPE_P(parameter) == IS_RESOURCE) {
+ php_stream *stm = NULL;
+ php_stream_from_zval_no_verify(stm, parameter);
+ if (stm) {
+ zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
+ zval_ptr_dtor(parameter);
+ ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC());
+ } else {
+ pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
+ return 0;
+ }
+ } else if (Z_TYPE_P(parameter) == IS_NULL) {
+ if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) {
+ return 1;
+ }
+ pdo_sqlite_error_stmt(stmt);
+ return 0;
+ } else {
+ if (!try_convert_to_string(parameter)) {
+ return 0;
+ }
+ }
+
+ if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1,
+ Z_STRVAL_P(parameter),
+ Z_STRLEN_P(parameter),
+ SQLITE_STATIC)) {
+ return 1;
+ }
+ return 0;
+
+ case PDO_PARAM_STR:
+ default:
+ if (Z_ISREF(param->parameter)) {
+ parameter = Z_REFVAL(param->parameter);
+ } else {
+ parameter = ¶m->parameter;
+ }
+ if (Z_TYPE_P(parameter) == IS_NULL) {
+ if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) {
+ return 1;
+ }
+ } else {
+ if (!try_convert_to_string(parameter)) {
+ return 0;
+ }
+ if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1,
+ Z_STRVAL_P(parameter),
+ Z_STRLEN_P(parameter),
+ SQLITE_STATIC)) {
+ return 1;
+ }
+ }
+ pdo_sqlite_error_stmt(stmt);
+ return 0;
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+ return 1;
+}
+
+static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt,
+ enum pdo_fetch_orientation ori, zend_long offset)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+ int i;
+ if (!S->stmt) {
+ return 0;
+ }
+ if (S->pre_fetched) {
+ S->pre_fetched = 0;
+ return 1;
+ }
+ if (S->done) {
+ return 0;
+ }
+ i = sqlite3_step(S->stmt);
+ switch (i) {
+ case SQLITE_ROW:
+ return 1;
+
+ case SQLITE_DONE:
+ S->done = 1;
+ sqlite3_reset(S->stmt);
+ return 0;
+
+ case SQLITE_ERROR:
+ sqlite3_reset(S->stmt);
+ ZEND_FALLTHROUGH;
+ default:
+ pdo_sqlite_error_stmt(stmt);
+ return 0;
+ }
+}
+
+static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+ const char *str;
+
+ if(colno >= sqlite3_column_count(S->stmt)) {
+ /* error invalid column */
+ pdo_sqlite_error_stmt(stmt);
+ return 0;
+ }
+
+ str = sqlite3_column_name(S->stmt, colno);
+ stmt->columns[colno].name = zend_string_init(str, strlen(str), 0);
+ stmt->columns[colno].maxlen = SIZE_MAX;
+ stmt->columns[colno].precision = 0;
+
+ return 1;
+}
+
+static int pdo_sqlite_stmt_get_col(
+ pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+ if (!S->stmt) {
+ return 0;
+ }
+ if(colno >= sqlite3_data_count(S->stmt)) {
+ /* error invalid column */
+ pdo_sqlite_error_stmt(stmt);
+ return 0;
+ }
+ switch (sqlite3_column_type(S->stmt, colno)) {
+ case SQLITE_NULL:
+ ZVAL_NULL(result);
+ return 1;
+
+ case SQLITE_INTEGER: {
+ int64_t i = sqlite3_column_int64(S->stmt, colno);
+#if SIZEOF_ZEND_LONG < 8
+ if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) {
+ ZVAL_STRINGL(result,
+ (char *) sqlite3_column_text(S->stmt, colno),
+ sqlite3_column_bytes(S->stmt, colno));
+ return 1;
+ }
+#endif
+ ZVAL_LONG(result, i);
+ return 1;
+ }
+
+ case SQLITE_FLOAT:
+ ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno));
+ return 1;
+
+ case SQLITE_BLOB:
+ ZVAL_STRINGL_FAST(result,
+ sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno));
+ return 1;
+
+ default:
+ ZVAL_STRINGL_FAST(result,
+ (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno));
+ return 1;
+ }
+}
+
+static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+ const char *str;
+ zval flags;
+
+ if (!S->stmt || !stmt->executed) {
+ return FAILURE;
+ }
+ if(colno >= sqlite3_column_count(S->stmt)) {
+ /* error invalid column */
+ pdo_sqlite_error_stmt(stmt);
+ return FAILURE;
+ }
+
+ array_init(return_value);
+ array_init(&flags);
+
+ switch (sqlite3_column_type(S->stmt, colno)) {
+ case SQLITE_NULL:
+ add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE));
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL);
+ break;
+
+ case SQLITE_FLOAT:
+ add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_DOUBLE));
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
+ break;
+
+ case SQLITE_BLOB:
+ add_next_index_string(&flags, "blob");
+ /* TODO Check this is correct */
+ ZEND_FALLTHROUGH;
+ case SQLITE_TEXT:
+ add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_STRING));
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
+ break;
+
+ case SQLITE_INTEGER:
+ add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_INTEGER));
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
+ break;
+ }
+
+ str = sqlite3_column_decltype(S->stmt, colno);
+ if (str) {
+ add_assoc_string(return_value, "sqlite:decl_type", (char *)str);
+ }
+
+#ifdef HAVE_SQLITE3_COLUMN_TABLE_NAME
+ str = sqlite3_column_table_name(S->stmt, colno);
+ if (str) {
+ add_assoc_string(return_value, "table", (char *)str);
+ }
+#endif
+
+ add_assoc_zval(return_value, "flags", &flags);
+
+ return SUCCESS;
+}
+
+static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+ sqlite3_reset(S->stmt);
+ return 1;
+}
+
+static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val)
+{
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+
+ switch (attr) {
+ case PDO_SQLITE_ATTR_READONLY_STATEMENT:
+ ZVAL_FALSE(val);
+
+ if (sqlite3_stmt_readonly(S->stmt)) {
+ ZVAL_TRUE(val);
+ }
+ break;
+
+ case PDO_SQLITE_ATTR_BUSY_STATEMENT:
+ ZVAL_FALSE(val);
+
+ if (sqlite3_stmt_busy(S->stmt)) {
+ ZVAL_TRUE(val);
+ }
+ break;
+ case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT:
+#if SQLITE_VERSION_NUMBER >= 3043000
+#if defined(__APPLE__)
+ if (__builtin_available(macOS 14.2, *)) {
+#endif
+ ZVAL_LONG(val, (zend_long)sqlite3_stmt_isexplain(S->stmt));
+ return 1;
+#if defined(__APPLE__)
+ } else {
+ zend_value_error("explain statement unsupported");
+ return 0;
+ }
+#endif
+#else
+ zend_value_error("explain statement unsupported");
+ return 0;
+#endif
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static int pdo_sqlite_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *zval)
+{
+ switch (attr) {
+ case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT:
+#if SQLITE_VERSION_NUMBER >= 3043000
+#if defined(__APPLE__)
+ if (__builtin_available(macOS 14.2, *)) {
+#endif
+ if (Z_TYPE_P(zval) != IS_LONG) {
+ zend_type_error("explain mode must be of type int, %s given", zend_zval_value_name(zval));
+ return 0;
+ }
+ if (Z_LVAL_P(zval) < 0 || Z_LVAL_P(zval) > 2) {
+ zend_value_error("explain mode must be one of the Pdo\\Sqlite::EXPLAIN_MODE_* constants");
+ return 0;
+ }
+
+ pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
+ if (sqlite3_stmt_explain(S->stmt, (int)Z_LVAL_P(zval)) != SQLITE_OK) {
+ return 0;
+ }
+
+ return 1;
+#if defined(__APPLE__)
+ } else {
+ zend_value_error("explain statement unsupported");
+ return 0;
+ }
+#endif
+#else
+ zend_value_error("explain statement unsupported");
+ return 0;
+#endif
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+const struct pdo_stmt_methods sqlite_stmt_methods = {
+ pdo_sqlite_stmt_dtor,
+ pdo_sqlite_stmt_execute,
+ pdo_sqlite_stmt_fetch,
+ pdo_sqlite_stmt_describe,
+ pdo_sqlite_stmt_get_col,
+ pdo_sqlite_stmt_param_hook,
+ pdo_sqlite_stmt_set_attribute, /* set_attr */
+ pdo_sqlite_stmt_get_attribute, /* get_attr */
+ pdo_sqlite_stmt_col_meta,
+ NULL, /* next_rowset */
+ pdo_sqlite_stmt_cursor_closer
+};
+#endif
\ No newline at end of file