diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml
index 9329310b63f..9b24ab0ef52 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 d29f01c19fa..800266b0db1 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 48cd1673a49..cbdeaca9487 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 93b696fed29..f1a0b49b67a 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 9c9b2f74e45..7b8b413fd8c 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 975cfe999ed..3d2e3a49d9d 100644
--- a/config.m4
+++ b/config.m4
@@ -1401,20 +1401,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 8e77c9cf674..d05219f1539 100644
--- a/ext-src/php_swoole_odbc.h
+++ b/ext-src/php_swoole_odbc.h
@@ -30,8 +30,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 51f1a0b5b16..1d91c9fd068 100644
--- a/ext-src/php_swoole_pgsql.h
+++ b/ext-src/php_swoole_pgsql.h
@@ -31,8 +31,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 e5c25e4712e..a2aa062c877 100644
--- a/ext-src/php_swoole_sqlite.h
+++ b/ext-src/php_swoole_sqlite.h
@@ -31,8 +31,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/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