From c1910c8f92a5d2a56b97078d5a6b12bcd3997cf8 Mon Sep 17 00:00:00 2001 From: Danack Date: Sat, 4 Jun 2022 17:44:12 +0100 Subject: [PATCH 01/11] Implement PDO driver specific sub-classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC: https://wiki.php.net/rfc/pdo_driver_specific_subclasses Co-authored-by: Máté Kocsis --- ext/pdo/pdo_dbh.c | 114 ++- ext/pdo/pdo_dbh.stub.php | 7 + ext/pdo/pdo_dbh_arginfo.h | 13 +- ext/pdo/php_pdo.h | 8 +- ext/pdo/php_pdo_driver.h | 10 + ext/pdo/php_pdo_int.h | 2 - ext/pdo/tests/pdo_test.inc | 6 +- ext/pdo_dblib/pdo_dblib.c | 11 + ext/pdo_dblib/pdo_dblib.stub.php | 31 + ext/pdo_dblib/pdo_dblib_arginfo.h | 62 ++ ext/pdo_dblib/tests/batch_stmt_ins_exec.phpt | 3 +- .../tests/batch_stmt_ins_sel_up_del.phpt | 4 +- ext/pdo_dblib/tests/batch_stmt_ins_up.phpt | 3 +- ext/pdo_dblib/tests/batch_stmt_rowcount.phpt | 3 +- .../tests/batch_stmt_transaction.phpt | 3 +- ext/pdo_dblib/tests/batch_stmt_try.phpt | 3 +- ext/pdo_dblib/tests/bug_38955.phpt | 4 + ext/pdo_dblib/tests/bug_45876.phpt | 2 + ext/pdo_dblib/tests/bug_47588.phpt | 3 + ext/pdo_dblib/tests/bug_50755.phpt | 3 + ext/pdo_dblib/tests/bug_54648.phpt | 2 + ext/pdo_dblib/tests/bug_67130.phpt | 3 + ext/pdo_dblib/tests/bug_68957.phpt | 3 + ext/pdo_dblib/tests/bug_69592.phpt | 3 + ext/pdo_dblib/tests/bug_69757.phpt | 3 + ext/pdo_dblib/tests/bug_71667.phpt | 3 + ext/pdo_dblib/tests/bug_73396.phpt | 3 + ext/pdo_dblib/tests/config.inc | 65 +- ext/pdo_dblib/tests/datetime2.phpt | 3 + ext/pdo_dblib/tests/datetime_convert.phpt | 3 + ext/pdo_dblib/tests/dbtds.phpt | 3 + ext/pdo_dblib/tests/dbversion.phpt | 3 + .../tests/pdo_dblib_param_str_natl.phpt | 3 + ext/pdo_dblib/tests/pdo_dblib_quote.phpt | 4 +- ext/pdo_dblib/tests/pdodblib_001.phpt | 44 ++ ext/pdo_dblib/tests/pdodblib_002.phpt | 48 ++ .../tests/stringify_uniqueidentifier.phpt | 2 + ext/pdo_dblib/tests/timeout.phpt | 11 +- ext/pdo_dblib/tests/types.phpt | 3 + ext/pdo_firebird/pdo_firebird.c | 11 + ext/pdo_firebird/pdo_firebird.stub.php | 19 + ext/pdo_firebird/pdo_firebird_arginfo.h | 38 + ext/pdo_firebird/tests/autocommit.phpt | 2 + .../tests/autocommit_change_mode.phpt | 1 + ext/pdo_firebird/tests/bug_47415.phpt | 2 + ext/pdo_firebird/tests/bug_48877.phpt | 2 + ext/pdo_firebird/tests/bug_53280.phpt | 2 + ext/pdo_firebird/tests/bug_62024.phpt | 2 + ext/pdo_firebird/tests/bug_64037.phpt | 2 + ext/pdo_firebird/tests/bug_72583.phpt | 2 + ext/pdo_firebird/tests/bug_72931.phpt | 2 + ext/pdo_firebird/tests/bug_73087.phpt | 2 + ext/pdo_firebird/tests/bug_74462.phpt | 2 + ext/pdo_firebird/tests/bug_76488.phpt | 20 +- ext/pdo_firebird/tests/bug_77863.phpt | 1 + ext/pdo_firebird/tests/bug_80521.phpt | 2 + ext/pdo_firebird/tests/bug_aaa.phpt | 2 + ext/pdo_firebird/tests/connect.phpt | 1 + ext/pdo_firebird/tests/ddl.phpt | 1 + ext/pdo_firebird/tests/ddl2.phpt | 2 + ext/pdo_firebird/tests/dialect_1.phpt | 2 + ext/pdo_firebird/tests/error_handle.phpt | 6 +- ext/pdo_firebird/tests/execute.phpt | 2 + ext/pdo_firebird/tests/execute_block.phpt | 21 +- ext/pdo_firebird/tests/gh10908.phpt | 2 + ext/pdo_firebird/tests/gh8576.phpt | 2 + ext/pdo_firebird/tests/ignore_parammarks.phpt | 22 +- ext/pdo_firebird/tests/pdofirebird_001.phpt | 44 ++ ext/pdo_firebird/tests/pdofirebird_002.phpt | 55 ++ ext/pdo_firebird/tests/rowCount.phpt | 2 + ext/pdo_firebird/tests/testdb.inc | 9 +- .../tests/transaction_access_mode.phpt | 2 + .../transaction_isolation_level_attr.phpt | 1 + .../transaction_isolation_level_behavior.phpt | 2 + ext/pdo_mysql/pdo_mysql.c | 29 +- ext/pdo_mysql/pdo_mysql.stub.php | 75 ++ ext/pdo_mysql/pdo_mysql_arginfo.h | 149 ++++ ext/pdo_mysql/tests/inc/mysql_pdo_test.inc | 29 +- ext/pdo_mysql/tests/pdo_mysql_interface.phpt | 1 + ext/pdo_mysql/tests/pdomysql_001.phpt | 47 ++ ext/pdo_mysql/tests/pdomysql_002.phpt | 54 ++ ext/pdo_mysql/tests/pdomysql_003.phpt | 29 + ext/pdo_odbc/pdo_odbc.c | 12 + ext/pdo_odbc/pdo_odbc.stub.php | 22 + ext/pdo_odbc/pdo_odbc_arginfo.h | 48 +- ext/pdo_odbc/tests/pdoodbc_001.phpt | 45 ++ ext/pdo_odbc/tests/pdoodbc_002.phpt | 54 ++ ext/pdo_pgsql/pdo_pgsql.c | 98 +++ ext/pdo_pgsql/pdo_pgsql.stub.php | 50 ++ ext/pdo_pgsql/pdo_pgsql_arginfo.h | 125 ++++ ext/pdo_pgsql/pgsql_driver.c | 84 ++- ext/pdo_pgsql/php_pdo_pgsql_int.h | 10 + ext/pdo_pgsql/tests/pdopgsql_001.phpt | 47 ++ ext/pdo_pgsql/tests/pdopgsql_002.phpt | 56 ++ ext/pdo_pgsql/tests/pdopgsql_003.phpt | 27 + ext/pdo_sqlite/config.m4 | 6 + ext/pdo_sqlite/pdo_sqlite.c | 651 +++++++++++++++++- ext/pdo_sqlite/pdo_sqlite.stub.php | 67 ++ ext/pdo_sqlite/pdo_sqlite_arginfo.h | 111 +++ ext/pdo_sqlite/tests/subclasses/config.inc | 16 + ext/pdo_sqlite/tests/subclasses/gc.phpt | 28 + .../subclasses/pdo_sqlite_constants.phpt | 26 + .../pdo_sqlite_createaggregate.phpt | 32 + .../pdo_sqlite_createaggregate_002.phpt | 25 + .../pdo_sqlite_createcollation.phpt | 39 ++ .../pdo_sqlite_createfunction_with_flags.phpt | 38 + .../tests/subclasses/pdosqlite_001.phpt | 38 + .../tests/subclasses/pdosqlite_002.phpt | 38 + .../tests/subclasses/pdosqlite_003.phpt | 65 ++ .../subclasses/pdosqlite_004_blobopen.phpt | 77 +++ .../tests/subclasses/pdosqlite_005.phpt | 17 + .../tests/subclasses/pdosqlite_006.phpt | 16 + .../tests/subclasses/pdosqlite_007.phpt | 18 + .../pdosqlite_load_extension_failure.phpt | 44 ++ .../tests/subclasses/stream_test.inc | 46 ++ 115 files changed, 3173 insertions(+), 113 deletions(-) create mode 100644 ext/pdo_dblib/pdo_dblib.stub.php create mode 100644 ext/pdo_dblib/pdo_dblib_arginfo.h create mode 100644 ext/pdo_dblib/tests/pdodblib_001.phpt create mode 100644 ext/pdo_dblib/tests/pdodblib_002.phpt create mode 100644 ext/pdo_firebird/pdo_firebird.stub.php create mode 100644 ext/pdo_firebird/pdo_firebird_arginfo.h create mode 100644 ext/pdo_firebird/tests/pdofirebird_001.phpt create mode 100644 ext/pdo_firebird/tests/pdofirebird_002.phpt create mode 100644 ext/pdo_mysql/pdo_mysql.stub.php create mode 100644 ext/pdo_mysql/pdo_mysql_arginfo.h create mode 100644 ext/pdo_mysql/tests/pdomysql_001.phpt create mode 100644 ext/pdo_mysql/tests/pdomysql_002.phpt create mode 100644 ext/pdo_mysql/tests/pdomysql_003.phpt create mode 100644 ext/pdo_odbc/tests/pdoodbc_001.phpt create mode 100644 ext/pdo_odbc/tests/pdoodbc_002.phpt create mode 100644 ext/pdo_pgsql/pdo_pgsql.stub.php create mode 100644 ext/pdo_pgsql/pdo_pgsql_arginfo.h create mode 100644 ext/pdo_pgsql/tests/pdopgsql_001.phpt create mode 100644 ext/pdo_pgsql/tests/pdopgsql_002.phpt create mode 100644 ext/pdo_pgsql/tests/pdopgsql_003.phpt create mode 100644 ext/pdo_sqlite/pdo_sqlite.stub.php create mode 100644 ext/pdo_sqlite/pdo_sqlite_arginfo.h create mode 100644 ext/pdo_sqlite/tests/subclasses/config.inc create mode 100644 ext/pdo_sqlite/tests/subclasses/gc.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdosqlite_004_blobopen.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/stream_test.inc diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 15b9433c7c38..d8e8c6f2e6d4 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -221,10 +221,82 @@ static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */ } /* }}} */ -/* {{{ */ -PHP_METHOD(PDO, __construct) +#define MAX_PDO_SUB_CLASSES 64 +static unsigned int number_of_pdo_driver_class_entries = 0; +static pdo_driver_class_entry *pdo_driver_class_entries[MAX_PDO_SUB_CLASSES]; + +// It would be possible remove this and roll it into the standard driver class entries +// I chose not to do it at this time, as that would break existing PDO extensions +zend_result pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry) +{ + if (number_of_pdo_driver_class_entries >= MAX_PDO_SUB_CLASSES) { + return FAILURE; + } + + pdo_driver_class_entries[number_of_pdo_driver_class_entries] = driver_class_entry; + number_of_pdo_driver_class_entries += 1; + + return SUCCESS; +} + +static bool create_driver_specific_pdo_object(const char *driver_name, zend_class_entry *called_scope, zval *new_object) +{ + zend_class_entry *ce_based_on_driver_name = NULL, *ce_based_on_called_object = NULL; + + for (int i = 0; i < number_of_pdo_driver_class_entries && (ce_based_on_driver_name == NULL || ce_based_on_called_object == NULL); i++) { + pdo_driver_class_entry *driver_class_entry = pdo_driver_class_entries[i]; + + if (strcmp(driver_class_entry->driver_name, driver_name) == 0) { + ce_based_on_driver_name = driver_class_entry->driver_ce; + } + + if (called_scope != pdo_dbh_ce && instanceof_function(called_scope, driver_class_entry->driver_ce)) { + ce_based_on_called_object = called_scope; + } + } + + if (ce_based_on_called_object) { + if (ce_based_on_driver_name) { + if (instanceof_function(ce_based_on_called_object, ce_based_on_driver_name) == false) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, + "%s::connect() cannot be called when connecting to the \"%s\" driver, " + "you must call either %s::connect() or PDO::connect() instead", + ZSTR_VAL(called_scope->name), driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); + return false; + } + + object_init_ex(new_object, ce_based_on_called_object); + return true; + } else { + zend_throw_exception_ex(php_pdo_get_exception(), 0, + "%s::connect() cannot be called when connecting to an unknown driver, " + "you must call PDO::connect() instead", + ZSTR_VAL(called_scope->name)); + return false; + } + } + + if (ce_based_on_driver_name) { + if (called_scope != pdo_dbh_ce) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, + "%s::connect() cannot be called when connecting to the \"%s\" driver, " + "you must call either %s::connect() or PDO::connect() instead", + ZSTR_VAL(called_scope->name), driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); + return false; + } + + // A specific driver implementation was called on PDO + object_init_ex(new_object, ce_based_on_driver_name); + } else { + // No specific DB implementation found + object_init_ex(new_object, called_scope); + } + + return true; +} + +static void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zend_object *object, zend_class_entry *current_scope, zval *new_zval_object) { - zval *object = ZEND_THIS; pdo_dbh_t *dbh = NULL; bool is_persistent = 0; char *data_source; @@ -291,7 +363,17 @@ PHP_METHOD(PDO, __construct) RETURN_THROWS(); } - dbh = Z_PDO_DBH_P(object); + if (new_zval_object != NULL) { + ZEND_ASSERT((driver->driver_name != NULL) && "PDO driver name is null"); + bool result = create_driver_specific_pdo_object(driver->driver_name, current_scope, new_zval_object); + if (!result) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(new_zval_object); + } else { + dbh = php_pdo_dbh_fetch_inner(object); + } /* is this supposed to be a persistent connection ? */ if (options) { @@ -352,7 +434,7 @@ PHP_METHOD(PDO, __construct) if (pdbh) { efree(dbh); /* switch over to the persistent one */ - Z_PDO_OBJECT_P(object)->inner = pdbh; + php_pdo_dbh_fetch_object(object)->inner = pdbh; pdbh->refcount++; dbh = pdbh; } @@ -432,6 +514,19 @@ PHP_METHOD(PDO, __construct) zend_throw_exception(pdo_exception_ce, "Constructor failed", 0); } } + +/* {{{ */ +PHP_METHOD(PDO, __construct) +{ + internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_OBJ(EX(This)), EX(This).value.ce, NULL); +} +/* }}} */ + +/* {{{ */ +PHP_METHOD(PDO, connect) +{ + internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_OBJ(EX(This)), EX(This).value.ce, return_value); +} /* }}} */ static zval *pdo_stmt_instantiate(pdo_dbh_t *dbh, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */ @@ -1334,6 +1429,8 @@ static HashTable *dbh_get_gc(zend_object *object, zval **gc_data, int *gc_count) } static zend_object_handlers pdo_dbh_object_handlers; +static zend_object_handlers pdosqlite_dbh_object_handlers; + static void pdo_dbh_free_storage(zend_object *std); void pdo_dbh_init(int module_number) @@ -1349,6 +1446,13 @@ void pdo_dbh_init(int module_number) pdo_dbh_object_handlers.get_method = dbh_method_get; pdo_dbh_object_handlers.compare = zend_objects_not_comparable; pdo_dbh_object_handlers.get_gc = dbh_get_gc; + + memcpy(&pdosqlite_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + pdosqlite_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std); + pdosqlite_dbh_object_handlers.free_obj = pdo_dbh_free_storage; + pdosqlite_dbh_object_handlers.get_method = dbh_method_get; + pdosqlite_dbh_object_handlers.compare = zend_objects_not_comparable; + pdosqlite_dbh_object_handlers.get_gc = dbh_get_gc; } static void dbh_free(pdo_dbh_t *dbh, bool free_persistent) diff --git a/ext/pdo/pdo_dbh.stub.php b/ext/pdo/pdo_dbh.stub.php index 9b32a62790fd..7fcec0226b0b 100644 --- a/ext/pdo/pdo_dbh.stub.php +++ b/ext/pdo/pdo_dbh.stub.php @@ -166,6 +166,13 @@ class PDO public function __construct(string $dsn, ?string $username = null, #[\SensitiveParameter] ?string $password = null, ?array $options = null) {} + public static function connect( + string $dsn, + ?string $username = null, + #[\SensitiveParameter] ?string $password = null, + ?array $options = null + ): static {} + /** @tentative-return-type */ public function beginTransaction(): bool {} diff --git a/ext/pdo/pdo_dbh_arginfo.h b/ext/pdo/pdo_dbh_arginfo.h index cad4bdef0305..2d2471baff5a 100644 --- a/ext/pdo/pdo_dbh_arginfo.h +++ b/ext/pdo/pdo_dbh_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: acd95f74c8b95515d3177f55682f9ee50dd779e9 */ + * Stub hash: 006be61b2c519e7d9ca997a7f12135eb3e0f3500 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, dsn, IS_STRING, 0) @@ -8,6 +8,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_PDO_connect, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, dsn, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, username, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, password, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_beginTransaction, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -60,6 +67,7 @@ ZEND_END_ARG_INFO() ZEND_METHOD(PDO, __construct); +ZEND_METHOD(PDO, connect); ZEND_METHOD(PDO, beginTransaction); ZEND_METHOD(PDO, commit); ZEND_METHOD(PDO, errorCode); @@ -78,6 +86,7 @@ ZEND_METHOD(PDO, setAttribute); static const zend_function_entry class_PDO_methods[] = { ZEND_ME(PDO, __construct, arginfo_class_PDO___construct, ZEND_ACC_PUBLIC) + ZEND_ME(PDO, connect, arginfo_class_PDO_connect, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(PDO, beginTransaction, arginfo_class_PDO_beginTransaction, ZEND_ACC_PUBLIC) ZEND_ME(PDO, commit, arginfo_class_PDO_commit, ZEND_ACC_PUBLIC) ZEND_ME(PDO, errorCode, arginfo_class_PDO_errorCode, ZEND_ACC_PUBLIC) @@ -557,5 +566,7 @@ static zend_class_entry *register_class_PDO(void) zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "connect", sizeof("connect") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + return class_entry; } diff --git a/ext/pdo/php_pdo.h b/ext/pdo/php_pdo.h index 023372ebc8e6..4de505a391b0 100644 --- a/ext/pdo/php_pdo.h +++ b/ext/pdo/php_pdo.h @@ -19,9 +19,12 @@ #include "zend.h" -extern zend_module_entry pdo_module_entry; +PHPAPI extern zend_module_entry pdo_module_entry; #define phpext_pdo_ptr &pdo_module_entry +PHPAPI extern zend_class_entry *pdo_dbh_ce; +PHPAPI extern zend_object *pdo_dbh_new(zend_class_entry *ce); + #include "php_version.h" #define PHP_PDO_VERSION PHP_VERSION @@ -50,9 +53,6 @@ PHP_MINFO_FUNCTION(pdo); #define REGISTER_PDO_CLASS_CONST_LONG(const_name, value) \ zend_declare_class_constant_long(php_pdo_get_dbh_ce(), const_name, sizeof(const_name)-1, (zend_long)value); -#define REGISTER_PDO_CLASS_CONST_STRING(const_name, value) \ - zend_declare_class_constant_stringl(php_pdo_get_dbh_ce(), const_name, sizeof(const_name)-1, value, sizeof(value)-1); - #define LONG_CONST(c) (zend_long) c #define PDO_CONSTRUCT_CHECK \ diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index c83228462775..71c4ff989ccc 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -215,6 +215,16 @@ typedef struct { } pdo_driver_t; +// NOTE - This separate struct, could be rolled it into pdo_driver_t +// I chose not to, as that would cause BC break and require a lot of +// downstream work. +typedef struct { + char *driver_name; + zend_class_entry *driver_ce; +} pdo_driver_class_entry; + +PHPAPI zend_result pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry); + /* {{{ methods for a database handle */ /* close or otherwise disconnect the database */ diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index aa83c6603b5a..a276a7972e74 100644 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -28,9 +28,7 @@ int php_pdo_list_entry(void); void pdo_dbh_init(int module_number); void pdo_stmt_init(void); -extern zend_object *pdo_dbh_new(zend_class_entry *ce); extern const zend_function_entry pdo_dbh_functions[]; -extern zend_class_entry *pdo_dbh_ce; extern ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor); extern zend_object *pdo_dbstmt_new(zend_class_entry *ce); diff --git a/ext/pdo/tests/pdo_test.inc b/ext/pdo/tests/pdo_test.inc index 22f653e23826..5138452ff9c5 100644 --- a/ext/pdo/tests/pdo_test.inc +++ b/ext/pdo/tests/pdo_test.inc @@ -18,7 +18,7 @@ if (getenv('PDOTEST_DSN') === false) { class PDOTest { // create an instance of the PDO driver, based on // the current environment - static function factory($classname = 'PDO') { + static function factory($classname = PDO::class) { $dsn = getenv('PDOTEST_DSN'); $user = getenv('PDOTEST_USER'); $pass = getenv('PDOTEST_PASS'); @@ -54,12 +54,12 @@ class PDOTest { } } - static function test_factory($file) { + static function test_factory($file, $classname = PDO::class) { $config = self::get_config($file); foreach ($config['ENV'] as $k => $v) { putenv("$k=$v"); } - return self::factory(); + return self::factory($classname); } static function get_config($file) { diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index 3230f9151bf4..04d504b0b8ac 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -27,10 +27,14 @@ #include "php_pdo_dblib.h" #include "php_pdo_dblib_int.h" #include "zend_exceptions.h" +#include "pdo_dblib_arginfo.h" ZEND_DECLARE_MODULE_GLOBALS(dblib) static PHP_GINIT_FUNCTION(dblib); +zend_class_entry *PdoDblib_ce; +static pdo_driver_class_entry PdoDblib_pdo_driver_class_entry; + static const zend_module_dep pdo_dblib_deps[] = { ZEND_MOD_REQUIRED("pdo") ZEND_MOD_END @@ -201,6 +205,13 @@ PHP_MINIT_FUNCTION(pdo_dblib) return FAILURE; } + PdoDblib_ce = register_class_PdoDblib(pdo_dbh_ce); + PdoDblib_ce->create_object = pdo_dbh_new; + + PdoDblib_pdo_driver_class_entry.driver_name = "dblib"; + PdoDblib_pdo_driver_class_entry.driver_ce = PdoDblib_ce; + pdo_register_driver_specific_class(&PdoDblib_pdo_driver_class_entry); + if (FAILURE == php_pdo_register_driver(&pdo_dblib_driver)) { return FAILURE; } diff --git a/ext/pdo_dblib/pdo_dblib.stub.php b/ext/pdo_dblib/pdo_dblib.stub.php new file mode 100644 index 000000000000..569a1b47b016 --- /dev/null +++ b/ext/pdo_dblib/pdo_dblib.stub.php @@ -0,0 +1,31 @@ +ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + zval const_ATTR_CONNECTION_TIMEOUT_value; + ZVAL_LONG(&const_ATTR_CONNECTION_TIMEOUT_value, PDO_DBLIB_ATTR_CONNECTION_TIMEOUT); + zend_string *const_ATTR_CONNECTION_TIMEOUT_name = zend_string_init_interned("ATTR_CONNECTION_TIMEOUT", sizeof("ATTR_CONNECTION_TIMEOUT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_CONNECTION_TIMEOUT_name, &const_ATTR_CONNECTION_TIMEOUT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_CONNECTION_TIMEOUT_name); + + zval const_ATTR_QUERY_TIMEOUT_value; + ZVAL_LONG(&const_ATTR_QUERY_TIMEOUT_value, PDO_DBLIB_ATTR_QUERY_TIMEOUT); + zend_string *const_ATTR_QUERY_TIMEOUT_name = zend_string_init_interned("ATTR_QUERY_TIMEOUT", sizeof("ATTR_QUERY_TIMEOUT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_QUERY_TIMEOUT_name, &const_ATTR_QUERY_TIMEOUT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_QUERY_TIMEOUT_name); + + zval const_ATTR_STRINGIFY_UNIQUEIDENTIFIER_value; + ZVAL_LONG(&const_ATTR_STRINGIFY_UNIQUEIDENTIFIER_value, PDO_DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER); + zend_string *const_ATTR_STRINGIFY_UNIQUEIDENTIFIER_name = zend_string_init_interned("ATTR_STRINGIFY_UNIQUEIDENTIFIER", sizeof("ATTR_STRINGIFY_UNIQUEIDENTIFIER") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_STRINGIFY_UNIQUEIDENTIFIER_name, &const_ATTR_STRINGIFY_UNIQUEIDENTIFIER_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_STRINGIFY_UNIQUEIDENTIFIER_name); + + zval const_ATTR_VERSION_value; + ZVAL_LONG(&const_ATTR_VERSION_value, PDO_DBLIB_ATTR_VERSION); + zend_string *const_ATTR_VERSION_name = zend_string_init_interned("ATTR_VERSION", sizeof("ATTR_VERSION") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_VERSION_name, &const_ATTR_VERSION_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_VERSION_name); + + zval const_ATTR_TDS_VERSION_value; + ZVAL_LONG(&const_ATTR_TDS_VERSION_value, PDO_DBLIB_ATTR_TDS_VERSION); + zend_string *const_ATTR_TDS_VERSION_name = zend_string_init_interned("ATTR_TDS_VERSION", sizeof("ATTR_TDS_VERSION") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_TDS_VERSION_name, &const_ATTR_TDS_VERSION_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_TDS_VERSION_name); + + zval const_ATTR_SKIP_EMPTY_ROWSETS_value; + ZVAL_LONG(&const_ATTR_SKIP_EMPTY_ROWSETS_value, PDO_DBLIB_ATTR_SKIP_EMPTY_ROWSETS); + zend_string *const_ATTR_SKIP_EMPTY_ROWSETS_name = zend_string_init_interned("ATTR_SKIP_EMPTY_ROWSETS", sizeof("ATTR_SKIP_EMPTY_ROWSETS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_SKIP_EMPTY_ROWSETS_name, &const_ATTR_SKIP_EMPTY_ROWSETS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_SKIP_EMPTY_ROWSETS_name); + + zval const_ATTR_DATETIME_CONVERT_value; + ZVAL_LONG(&const_ATTR_DATETIME_CONVERT_value, PDO_DBLIB_ATTR_DATETIME_CONVERT); + zend_string *const_ATTR_DATETIME_CONVERT_name = zend_string_init_interned("ATTR_DATETIME_CONVERT", sizeof("ATTR_DATETIME_CONVERT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_DATETIME_CONVERT_name, &const_ATTR_DATETIME_CONVERT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_DATETIME_CONVERT_name); + + return class_entry; +} diff --git a/ext/pdo_dblib/tests/batch_stmt_ins_exec.phpt b/ext/pdo_dblib/tests/batch_stmt_ins_exec.phpt index 50abb5d1ebaf..5b5c35252b0f 100644 --- a/ext/pdo_dblib/tests/batch_stmt_ins_exec.phpt +++ b/ext/pdo_dblib/tests/batch_stmt_ins_exec.phpt @@ -5,13 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- query("create table #test_batch_stmt_ins_exec(id int); "); $db->query( diff --git a/ext/pdo_dblib/tests/batch_stmt_ins_sel_up_del.phpt b/ext/pdo_dblib/tests/batch_stmt_ins_sel_up_del.phpt index c871f681be75..80bc8ab533f0 100644 --- a/ext/pdo_dblib/tests/batch_stmt_ins_sel_up_del.phpt +++ b/ext/pdo_dblib/tests/batch_stmt_ins_sel_up_del.phpt @@ -5,13 +5,15 @@ pdo_dblib --SKIPIF-- --FILE-- query( "create table #test_batch_stmt_ins_sel_up_del(id int);" . "insert into #test_batch_stmt_ins_sel_up_del values(1), (2), (3);" . diff --git a/ext/pdo_dblib/tests/batch_stmt_ins_up.phpt b/ext/pdo_dblib/tests/batch_stmt_ins_up.phpt index bbe8cb156591..afc87dd05d7c 100644 --- a/ext/pdo_dblib/tests/batch_stmt_ins_up.phpt +++ b/ext/pdo_dblib/tests/batch_stmt_ins_up.phpt @@ -5,13 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- query( "create table #test_batch_stmt_ins_up(id int);" . "insert into #test_batch_stmt_ins_up values(1), (2), (3);" . diff --git a/ext/pdo_dblib/tests/batch_stmt_rowcount.phpt b/ext/pdo_dblib/tests/batch_stmt_rowcount.phpt index fa101c862268..03f8456c7876 100644 --- a/ext/pdo_dblib/tests/batch_stmt_rowcount.phpt +++ b/ext/pdo_dblib/tests/batch_stmt_rowcount.phpt @@ -5,13 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- query( "create table #test_batch_stmt_rowcount(id int); " . "set rowcount 2; " . diff --git a/ext/pdo_dblib/tests/batch_stmt_transaction.phpt b/ext/pdo_dblib/tests/batch_stmt_transaction.phpt index 2d9fbc7a4c7b..2fab8dcda742 100644 --- a/ext/pdo_dblib/tests/batch_stmt_transaction.phpt +++ b/ext/pdo_dblib/tests/batch_stmt_transaction.phpt @@ -5,13 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- query( "create table #test_batch_stmt_transaction(id int);" . "insert into #test_batch_stmt_transaction values(1), (2), (3);" . diff --git a/ext/pdo_dblib/tests/batch_stmt_try.phpt b/ext/pdo_dblib/tests/batch_stmt_try.phpt index 30e2bf58bebc..9e735ff87715 100644 --- a/ext/pdo_dblib/tests/batch_stmt_try.phpt +++ b/ext/pdo_dblib/tests/batch_stmt_try.phpt @@ -5,13 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- query( "create table #test_batch_stmt_try(id int);" . "insert into #test_batch_stmt_try values(1), (2), (3);" . diff --git a/ext/pdo_dblib/tests/bug_38955.phpt b/ext/pdo_dblib/tests/bug_38955.phpt index 1ec340a8c051..cd244ba1ec33 100644 --- a/ext/pdo_dblib/tests/bug_38955.phpt +++ b/ext/pdo_dblib/tests/bug_38955.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- query("CREATE table test38955(val int)"); $db->beginTransaction(); @@ -34,6 +37,7 @@ var_dump($rows); --CLEAN-- exec("DROP TABLE IF EXISTS test38955"); ?> --EXPECT-- diff --git a/ext/pdo_dblib/tests/bug_45876.phpt b/ext/pdo_dblib/tests/bug_45876.phpt index 13db577f9048..956f109095c3 100644 --- a/ext/pdo_dblib/tests/bug_45876.phpt +++ b/ext/pdo_dblib/tests/bug_45876.phpt @@ -5,6 +5,7 @@ pdo_dblib --SKIPIF-- --CONFLICTS-- all @@ -12,6 +13,7 @@ all prepare("select top 1 ic1.* from information_schema.columns ic1"); $stmt->execute(); var_dump($stmt->getColumnMeta(0)); diff --git a/ext/pdo_dblib/tests/bug_47588.phpt b/ext/pdo_dblib/tests/bug_47588.phpt index e4d46c23f3e3..218088b1e11f 100644 --- a/ext/pdo_dblib/tests/bug_47588.phpt +++ b/ext/pdo_dblib/tests/bug_47588.phpt @@ -5,11 +5,13 @@ pdo_dblib --SKIPIF-- --FILE-- query('CREATE TABLE "Test Table47588" ("My Field" int, "Another Field" varchar(32) not null default \'test_string\')'); $db->query('INSERT INTO "Test Table47588" ("My Field") values(1), (2), (3)'); $rs = $db->query('SELECT * FROM "Test Table47588"'); @@ -19,6 +21,7 @@ echo "Done.\n"; --CLEAN-- exec('DROP TABLE IF EXISTS "Test Table47588"'); ?> --EXPECT-- diff --git a/ext/pdo_dblib/tests/bug_50755.phpt b/ext/pdo_dblib/tests/bug_50755.phpt index 8222a1ee39c0..86bd64a40781 100644 --- a/ext/pdo_dblib/tests/bug_50755.phpt +++ b/ext/pdo_dblib/tests/bug_50755.phpt @@ -6,6 +6,7 @@ pdo_dblib --CONFLICTS-- all @@ -13,6 +14,8 @@ all prepare("select * from information_schema.columns ic1 diff --git a/ext/pdo_dblib/tests/bug_54648.phpt b/ext/pdo_dblib/tests/bug_54648.phpt index b88e4ad9c526..9128b8c25404 100644 --- a/ext/pdo_dblib/tests/bug_54648.phpt +++ b/ext/pdo_dblib/tests/bug_54648.phpt @@ -5,10 +5,12 @@ pdo_dblib --SKIPIF-- --FILE-- query('set dateformat ymd'); $rs = $db->query("select cast('1950-01-18 23:00:00' as smalldatetime) as sdt, cast('2030-01-01 23:59:59' as datetime) as dt"); var_dump($rs->fetchAll(PDO::FETCH_ASSOC)); diff --git a/ext/pdo_dblib/tests/bug_67130.phpt b/ext/pdo_dblib/tests/bug_67130.phpt index 936f5229567f..09a8f1e8f0e1 100644 --- a/ext/pdo_dblib/tests/bug_67130.phpt +++ b/ext/pdo_dblib/tests/bug_67130.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- query('SELECT 1; SELECT 2; SELECT 3;'); var_dump($stmt->fetch()); var_dump($stmt->fetch()); diff --git a/ext/pdo_dblib/tests/bug_68957.phpt b/ext/pdo_dblib/tests/bug_68957.phpt index a7d30eb49384..aee046111573 100644 --- a/ext/pdo_dblib/tests/bug_68957.phpt +++ b/ext/pdo_dblib/tests/bug_68957.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- query($query); $stmt->nextRowset(); // Added line diff --git a/ext/pdo_dblib/tests/bug_69592.phpt b/ext/pdo_dblib/tests/bug_69592.phpt index 75dedf8a77d0..d7114e8fa09d 100644 --- a/ext/pdo_dblib/tests/bug_69592.phpt +++ b/ext/pdo_dblib/tests/bug_69592.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- --FILE-- --FILE-- prepare("SELECT 1, 2 AS named, 3"); $stmt->execute(); var_dump($stmt->fetchAll()); diff --git a/ext/pdo_dblib/tests/bug_73396.phpt b/ext/pdo_dblib/tests/bug_73396.phpt index 1bd439782ffb..b2f96f8ec1c9 100644 --- a/ext/pdo_dblib/tests/bug_73396.phpt +++ b/ext/pdo_dblib/tests/bug_73396.phpt @@ -5,12 +5,15 @@ pdo_dblib --SKIPIF-- getAttribute(PDO::DBLIB_ATTR_TDS_VERSION), ['4.2', '4.6', '5.0', '6.0', '7.0'])) die('skip bigint type is unsupported by active TDS version'); ?> --FILE-- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); -} catch (PDOException $e) { - die('skip ' . $e->getMessage()); +} + +function getDbConnection(string $class = PDO::class, ?array $attributes = null) { + [$dsn, $user, $pass] = getCredentials(); + + try { + $db = new $class($dsn, $user, $pass, $attributes); + if ($attributes === null) { + setAttributes($db); + } + } catch (PDOException $e) { + die('skip ' . $e->getMessage()); + } + + return $db; +} + +function connectToDb() { + [$dsn, $user, $pass] = getCredentials(); + + try { + $db = PDO::connect($dsn, $user, $pass); + setAttributes($db); + } catch (PDOException $e) { + die('skip ' . $e->getMessage()); + } + + return $db; } ?> diff --git a/ext/pdo_dblib/tests/datetime2.phpt b/ext/pdo_dblib/tests/datetime2.phpt index 9ee39b09ac70..e4f65048dd89 100644 --- a/ext/pdo_dblib/tests/datetime2.phpt +++ b/ext/pdo_dblib/tests/datetime2.phpt @@ -5,12 +5,15 @@ pdo_dblib --SKIPIF-- getAttribute(PDO::DBLIB_ATTR_TDS_VERSION), ['4.2', '4.6', '5.0', '6.0', '7.0', '7.1', '7.2'])) die('skip feature unsupported by this TDS version'); ?> --FILE-- getAttribute(PDO::DBLIB_ATTR_DATETIME_CONVERT)); diff --git a/ext/pdo_dblib/tests/datetime_convert.phpt b/ext/pdo_dblib/tests/datetime_convert.phpt index a025dc460add..ec858cf351c5 100644 --- a/ext/pdo_dblib/tests/datetime_convert.phpt +++ b/ext/pdo_dblib/tests/datetime_convert.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- getAttribute(PDO::DBLIB_ATTR_DATETIME_CONVERT)); diff --git a/ext/pdo_dblib/tests/dbtds.phpt b/ext/pdo_dblib/tests/dbtds.phpt index cec305e276ad..aa05606a9326 100644 --- a/ext/pdo_dblib/tests/dbtds.phpt +++ b/ext/pdo_dblib/tests/dbtds.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- getAttribute(PDO::DBLIB_ATTR_TDS_VERSION); var_dump((is_string($version) && strlen($version)) || $version === false); diff --git a/ext/pdo_dblib/tests/dbversion.phpt b/ext/pdo_dblib/tests/dbversion.phpt index 53c2bb6c006a..44dd8f4a86dc 100644 --- a/ext/pdo_dblib/tests/dbversion.phpt +++ b/ext/pdo_dblib/tests/dbversion.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- getAttribute(PDO::DBLIB_ATTR_VERSION); var_dump(is_string($version) && strlen($version)); diff --git a/ext/pdo_dblib/tests/pdo_dblib_param_str_natl.phpt b/ext/pdo_dblib/tests/pdo_dblib_param_str_natl.phpt index 6a4da2bd414d..f0162138062c 100644 --- a/ext/pdo_dblib/tests/pdo_dblib_param_str_natl.phpt +++ b/ext/pdo_dblib/tests/pdo_dblib_param_str_natl.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- prepare('SELECT :value'); $stmt->bindValue(':value', 'foo', PDO::PARAM_STR | PDO::PARAM_STR_NATL); $stmt->execute(); diff --git a/ext/pdo_dblib/tests/pdo_dblib_quote.phpt b/ext/pdo_dblib/tests/pdo_dblib_quote.phpt index 1855841d4a45..05d1bbf82dd8 100644 --- a/ext/pdo_dblib/tests/pdo_dblib_quote.phpt +++ b/ext/pdo_dblib/tests/pdo_dblib_quote.phpt @@ -5,10 +5,12 @@ pdo_dblib --SKIPIF-- --FILE-- quote(true, PDO::PARAM_BOOL)); var_dump($db->quote(false, PDO::PARAM_BOOL)); var_dump($db->quote(42, PDO::PARAM_INT)); @@ -26,7 +28,7 @@ var_dump($db->quote('foo', PDO::PARAM_STR | PDO::PARAM_STR_CHAR)); var_dump($db->quote('über', PDO::PARAM_STR)); var_dump($db->quote('über', PDO::PARAM_STR | PDO::PARAM_STR_NATL)); -$db = new PDO($dsn, $user, $pass, [PDO::ATTR_DEFAULT_STR_PARAM => PDO::PARAM_STR_NATL]); +$db = getDbConnection(PDO::class, [PDO::ATTR_DEFAULT_STR_PARAM => PDO::PARAM_STR_NATL]); var_dump($db->getAttribute(PDO::ATTR_DEFAULT_STR_PARAM) === PDO::PARAM_STR_NATL); ?> diff --git a/ext/pdo_dblib/tests/pdodblib_001.phpt b/ext/pdo_dblib/tests/pdodblib_001.phpt new file mode 100644 index 000000000000..06b91b933651 --- /dev/null +++ b/ext/pdo_dblib/tests/pdodblib_001.phpt @@ -0,0 +1,44 @@ +--TEST-- +PdoDblib basic +--EXTENSIONS-- +pdo_dblib +--SKIPIF-- + +--FILE-- +query("create table #pdo_dblib_001(name varchar(32)); "); +$db->query("insert into #pdo_dblib_001 values('PHP'), ('PHP6');"); + +foreach ($db->query('SELECT name FROM #pdo_dblib_001') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +query('DROP TABLE IF EXISTS #pdo_dblib_001'); +?> +--EXPECT-- +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_dblib/tests/pdodblib_002.phpt b/ext/pdo_dblib/tests/pdodblib_002.phpt new file mode 100644 index 000000000000..825abfaeb7d8 --- /dev/null +++ b/ext/pdo_dblib/tests/pdodblib_002.phpt @@ -0,0 +1,48 @@ +--TEST-- +PdoDblib create through PDO::connect +--EXTENSIONS-- +pdo_dblib +--SKIPIF-- + +--FILE-- +query("create table #pdo_dblib_002(name varchar(32))"); +$db->query("insert into #pdo_dblib_002 values('PHP'), ('PHP6')"); + +foreach ($db->query('SELECT name FROM #pdo_dblib_002') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +query('DROP TABLE IF EXISTS #pdo_dblib_002'); +?> +--EXPECT-- +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_dblib/tests/stringify_uniqueidentifier.phpt b/ext/pdo_dblib/tests/stringify_uniqueidentifier.phpt index f83a9e643f17..86c94877c63c 100644 --- a/ext/pdo_dblib/tests/stringify_uniqueidentifier.phpt +++ b/ext/pdo_dblib/tests/stringify_uniqueidentifier.phpt @@ -5,12 +5,14 @@ pdo_dblib --SKIPIF-- getAttribute(PDO::DBLIB_ATTR_TDS_VERSION), ['4.2', '4.6'])) die('skip feature unsupported by this TDS version'); ?> --FILE-- --FILE-- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); $db->setAttribute(PDO::ATTR_TIMEOUT, 1); $stmt = $db->prepare($sql); @@ -28,7 +31,7 @@ if (!$stmt->execute()) { } // pdo_dblib-specific timeout attribute, set after instance created, will control query timeout, causing this query to fail -$db = new PDO($dsn, $user, $pass); +$db = getDbConnection(); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); $db->setAttribute(PDO::DBLIB_ATTR_QUERY_TIMEOUT, 1); $stmt = $db->prepare($sql); @@ -42,7 +45,7 @@ if (!$stmt->execute()) { } // regular timeout attribute will affect query timeout, causing this query to fail -$db = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT, PDO::ATTR_TIMEOUT => 1]); +$db = getDbConnection(PDO::class, [PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT, PDO::ATTR_TIMEOUT => 1]); $stmt = $db->prepare($sql); if (!$stmt->execute()) { echo "OK\n"; @@ -54,7 +57,7 @@ if (!$stmt->execute()) { } // pdo_dblib-specific timeout attribute will control query timeout, causing this query to fail -$db = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT, PDO::DBLIB_ATTR_QUERY_TIMEOUT => 1]); +$db = getDbConnection(PDO::class, [PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT, PDO::DBLIB_ATTR_QUERY_TIMEOUT => 1]); $stmt = $db->prepare($sql); if (!$stmt->execute()) { echo "OK\n"; diff --git a/ext/pdo_dblib/tests/types.phpt b/ext/pdo_dblib/tests/types.phpt index 951e3b3e59af..a58a969c6f1b 100644 --- a/ext/pdo_dblib/tests/types.phpt +++ b/ext/pdo_dblib/tests/types.phpt @@ -5,11 +5,14 @@ pdo_dblib --SKIPIF-- --FILE-- create_object = pdo_dbh_new; + + PdoFirebird_pdo_driver_class_entry.driver_name = "firebird"; + PdoFirebird_pdo_driver_class_entry.driver_ce = PdoFirebird_ce; + pdo_register_driver_specific_class(&PdoFirebird_pdo_driver_class_entry); + #ifdef ZEND_SIGNALS /* firebird replaces some signals at runtime, suppress warnings. */ SIGG(check) = 0; diff --git a/ext/pdo_firebird/pdo_firebird.stub.php b/ext/pdo_firebird/pdo_firebird.stub.php new file mode 100644 index 000000000000..e8804277a531 --- /dev/null +++ b/ext/pdo_firebird/pdo_firebird.stub.php @@ -0,0 +1,19 @@ +ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + zval const_ATTR_DATE_FORMAT_value; + ZVAL_LONG(&const_ATTR_DATE_FORMAT_value, PDO_FB_ATTR_DATE_FORMAT); + zend_string *const_ATTR_DATE_FORMAT_name = zend_string_init_interned("ATTR_DATE_FORMAT", sizeof("ATTR_DATE_FORMAT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_DATE_FORMAT_name, &const_ATTR_DATE_FORMAT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_DATE_FORMAT_name); + + zval const_ATTR_TIME_FORMAT_value; + ZVAL_LONG(&const_ATTR_TIME_FORMAT_value, PDO_FB_ATTR_TIME_FORMAT); + zend_string *const_ATTR_TIME_FORMAT_name = zend_string_init_interned("ATTR_TIME_FORMAT", sizeof("ATTR_TIME_FORMAT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_TIME_FORMAT_name, &const_ATTR_TIME_FORMAT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_TIME_FORMAT_name); + + zval const_ATTR_TIMESTAMP_FORMAT_value; + ZVAL_LONG(&const_ATTR_TIMESTAMP_FORMAT_value, PDO_FB_ATTR_TIMESTAMP_FORMAT); + zend_string *const_ATTR_TIMESTAMP_FORMAT_name = zend_string_init_interned("ATTR_TIMESTAMP_FORMAT", sizeof("ATTR_TIMESTAMP_FORMAT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_TIMESTAMP_FORMAT_name, &const_ATTR_TIMESTAMP_FORMAT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_TIMESTAMP_FORMAT_name); + + return class_entry; +} diff --git a/ext/pdo_firebird/tests/autocommit.phpt b/ext/pdo_firebird/tests/autocommit.phpt index 5ba61e11f7a8..5b431859bf85 100644 --- a/ext/pdo_firebird/tests/autocommit.phpt +++ b/ext/pdo_firebird/tests/autocommit.phpt @@ -17,6 +17,7 @@ $table = "autocommit_pdo_firebird"; echo "========== in auto commit mode ==========\n"; echo "auto commit mode ON\n"; +$dbh = getDbConnection(); $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true); echo "create table and insert\n"; @@ -53,6 +54,7 @@ echo "done!"; --CLEAN-- exec("DROP TABLE autocommit_pdo_firebird"); ?> --EXPECTF-- diff --git a/ext/pdo_firebird/tests/autocommit_change_mode.phpt b/ext/pdo_firebird/tests/autocommit_change_mode.phpt index b8d4e542d9c1..0ccc38f62392 100644 --- a/ext/pdo_firebird/tests/autocommit_change_mode.phpt +++ b/ext/pdo_firebird/tests/autocommit_change_mode.phpt @@ -10,6 +10,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 --FILE-- setAttribute(PDO::ATTR_AUTOCOMMIT, true); echo "========== not in manually transaction ==========\n"; diff --git a/ext/pdo_firebird/tests/bug_47415.phpt b/ext/pdo_firebird/tests/bug_47415.phpt index 6a7fc6ff7f77..0889403f419b 100644 --- a/ext/pdo_firebird/tests/bug_47415.phpt +++ b/ext/pdo_firebird/tests/bug_47415.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $dbh->exec('CREATE TABLE test47415 (idx int NOT NULL PRIMARY KEY, txt VARCHAR(20))'); @@ -36,6 +37,7 @@ unset($dbh); --CLEAN-- exec("DROP TABLE test47415"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_48877.phpt b/ext/pdo_firebird/tests/bug_48877.phpt index f7fa25ba1c83..04e14a7ce312 100644 --- a/ext/pdo_firebird/tests/bug_48877.phpt +++ b/ext/pdo_firebird/tests/bug_48877.phpt @@ -14,6 +14,7 @@ require("testdb.inc"); $value = '2'; +$dbh = getDbConnection(); $dbh->exec('CREATE TABLE test48877 (A integer)'); $dbh->exec("INSERT INTO test48877 VALUES ('1')"); $dbh->exec("INSERT INTO test48877 VALUES ('2')"); @@ -39,6 +40,7 @@ unset($dbh); --CLEAN-- exec("DROP TABLE test48877"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_53280.phpt b/ext/pdo_firebird/tests/bug_53280.phpt index b1da6789ba84..6accc81e585c 100644 --- a/ext/pdo_firebird/tests/bug_53280.phpt +++ b/ext/pdo_firebird/tests/bug_53280.phpt @@ -12,6 +12,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 require("testdb.inc"); +$dbh = getDbConnection(); $dbh->exec('CREATE TABLE test53280(A VARCHAR(30), B VARCHAR(30), C VARCHAR(30))'); $dbh->exec("INSERT INTO test53280 VALUES ('A', 'B', 'C')"); @@ -37,6 +38,7 @@ unset($dbh); --CLEAN-- exec("DROP TABLE test53280"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_62024.phpt b/ext/pdo_firebird/tests/bug_62024.phpt index 2f8fa09a9363..8cbeab61b85a 100644 --- a/ext/pdo_firebird/tests/bug_62024.phpt +++ b/ext/pdo_firebird/tests/bug_62024.phpt @@ -12,6 +12,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 require("testdb.inc"); +$dbh = getDbConnection(); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $dbh->exec("CREATE TABLE test62024 (ID INTEGER NOT NULL, TEXT VARCHAR(10))"); @@ -41,6 +42,7 @@ unset($dbh); --CLEAN-- exec("DROP TABLE test62024"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_64037.phpt b/ext/pdo_firebird/tests/bug_64037.phpt index 545e6feaf694..c7b1a327c15c 100644 --- a/ext/pdo_firebird/tests/bug_64037.phpt +++ b/ext/pdo_firebird/tests/bug_64037.phpt @@ -12,6 +12,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 require("testdb.inc"); +$dbh = getDbConnection(); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $dbh->exec("CREATE TABLE test64037 (ID INTEGER NOT NULL, TEXT VARCHAR(10), COST NUMERIC(15, 2))"); $dbh->exec("INSERT INTO test64037 (ID, TEXT, COST) VALUES (1, 'test', -1.0)"); @@ -37,6 +38,7 @@ unset($dbh); --CLEAN-- exec("DROP TABLE test64037"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_72583.phpt b/ext/pdo_firebird/tests/bug_72583.phpt index 0f72ce1b14ff..b86f40c7e228 100644 --- a/ext/pdo_firebird/tests/bug_72583.phpt +++ b/ext/pdo_firebird/tests/bug_72583.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec('recreate table test72583 (aint integer, asmi smallint)'); $dbh->exec('insert into test72583 values (1, -1)'); $S = $dbh->prepare('select aint, asmi from test72583'); @@ -23,6 +24,7 @@ unset($dbh); --CLEAN-- exec("DROP TABLE test72583"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_72931.phpt b/ext/pdo_firebird/tests/bug_72931.phpt index 9cda4b7ef09a..c54779973206 100644 --- a/ext/pdo_firebird/tests/bug_72931.phpt +++ b/ext/pdo_firebird/tests/bug_72931.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec('recreate table test72931 (id integer)'); $S = $dbh->prepare('insert into test72931 (id) values (1) returning id'); $S->execute(); @@ -22,6 +23,7 @@ unset($dbh); --CLEAN-- exec("DROP TABLE test72931"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_73087.phpt b/ext/pdo_firebird/tests/bug_73087.phpt index 5d5493ddb2a5..182cf920737e 100644 --- a/ext/pdo_firebird/tests/bug_73087.phpt +++ b/ext/pdo_firebird/tests/bug_73087.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec('recreate table test73087 (id integer not null, content blob sub_type 1 segment size 80)'); $S = $dbh->prepare('insert into test73087 (id, content) values (:id, :content)'); for ($I = 1; $I < 10; $I++) { @@ -29,6 +30,7 @@ echo 'OK'; --CLEAN-- exec("DROP TABLE test73087"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_74462.phpt b/ext/pdo_firebird/tests/bug_74462.phpt index 177fb08b35fa..e63ca1f6a7c7 100644 --- a/ext/pdo_firebird/tests/bug_74462.phpt +++ b/ext/pdo_firebird/tests/bug_74462.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec('recreate table test74462 (id integer not null, abool boolean)'); $dbh->exec('insert into test74462 (id, abool) values (1, true)'); $dbh->exec('insert into test74462 (id, abool) values (2, false)'); @@ -24,6 +25,7 @@ var_dump($D); --CLEAN-- exec("DROP TABLE test74462"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_76488.phpt b/ext/pdo_firebird/tests/bug_76488.phpt index fcb60c9ad7f5..4427e8c972bc 100644 --- a/ext/pdo_firebird/tests/bug_76488.phpt +++ b/ext/pdo_firebird/tests/bug_76488.phpt @@ -22,15 +22,17 @@ select n, from r '; - for ($i = 0; $i < 10; $i++) { - $sth = $dbh->prepare($sql); - $sth->execute(); - $rows = $sth->fetchAll(); - unset($rows); - unset($sth); - } - unset($dbh); - echo "OK"; +$dbh = getDbConnection(); + +for ($i = 0; $i < 10; $i++) { + $sth = $dbh->prepare($sql); + $sth->execute(); + $rows = $sth->fetchAll(); + unset($rows); + unset($sth); +} +unset($dbh); +echo "OK"; ?> --EXPECT-- OK diff --git a/ext/pdo_firebird/tests/bug_77863.phpt b/ext/pdo_firebird/tests/bug_77863.phpt index 724e9ec89ee5..8a59e7a7a030 100644 --- a/ext/pdo_firebird/tests/bug_77863.phpt +++ b/ext/pdo_firebird/tests/bug_77863.phpt @@ -24,6 +24,7 @@ select trim(s) as s from t where b is not distinct from :p SQL; try { + $dbh = getDbConnection(); $query = $dbh->prepare($sql); // PDO::PARAM_BOOL diff --git a/ext/pdo_firebird/tests/bug_80521.phpt b/ext/pdo_firebird/tests/bug_80521.phpt index fe233f334a7a..cc97b4855e72 100644 --- a/ext/pdo_firebird/tests/bug_80521.phpt +++ b/ext/pdo_firebird/tests/bug_80521.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec("CREATE TABLE bug80521 (foo INTEGER)"); var_dump($dbh->prepare("SELECT foo FROM bug80521 WHERE foo = :foo_bar")); ?> @@ -22,6 +23,7 @@ object(PDOStatement)#%d (1) { --CLEAN-- exec("DROP TABLE bug80521"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/bug_aaa.phpt b/ext/pdo_firebird/tests/bug_aaa.phpt index dba8ced3814c..6e4e40db0209 100644 --- a/ext/pdo_firebird/tests/bug_aaa.phpt +++ b/ext/pdo_firebird/tests/bug_aaa.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $dbh->exec('create table test_aaa (id integer)'); $S = $dbh->prepare('insert into test_aaa (id) values (:id) returning id'); @@ -23,6 +24,7 @@ echo 'OK'; --CLEAN-- exec("DROP TABLE test_aaa"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/connect.phpt b/ext/pdo_firebird/tests/connect.phpt index 8b6301d64741..cd92c36d98ec 100644 --- a/ext/pdo_firebird/tests/connect.phpt +++ b/ext/pdo_firebird/tests/connect.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); @$dbh->exec('DROP TABLE test_ddl'); @$dbh->exec('DROP GENERATOR gen_test_ddl_id'); diff --git a/ext/pdo_firebird/tests/ddl2.phpt b/ext/pdo_firebird/tests/ddl2.phpt index f258fb6f930c..1ca78bfb1fbe 100644 --- a/ext/pdo_firebird/tests/ddl2.phpt +++ b/ext/pdo_firebird/tests/ddl2.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec("CREATE TABLE test_ddl2 (val int)"); $dbh->beginTransaction(); @@ -27,6 +28,7 @@ echo "done\n"; --CLEAN-- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); @$dbh->exec('DROP TABLE test_ddl2'); @$dbh->exec('DROP TABLE test_ddl2_2'); diff --git a/ext/pdo_firebird/tests/dialect_1.phpt b/ext/pdo_firebird/tests/dialect_1.phpt index 475144bf6b7d..8df6f84d692f 100644 --- a/ext/pdo_firebird/tests/dialect_1.phpt +++ b/ext/pdo_firebird/tests/dialect_1.phpt @@ -15,6 +15,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $dbh->setAttribute(PDO::FB_ATTR_TIMESTAMP_FORMAT, '%Y-%m-%d %H:%M:%S'); @@ -56,6 +57,7 @@ echo "done\n"; --CLEAN-- exec('DROP TABLE test_dialect_1'); unset($dbh); --EXPECT-- diff --git a/ext/pdo_firebird/tests/error_handle.phpt b/ext/pdo_firebird/tests/error_handle.phpt index d070d61a1e70..b042263f6d19 100644 --- a/ext/pdo_firebird/tests/error_handle.phpt +++ b/ext/pdo_firebird/tests/error_handle.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $table = 'error_handle'; @@ -30,12 +31,13 @@ unset($dbh); --CLEAN-- exec('DROP TABLE error_handle'); unset($dbh); ?> --EXPECTF-- dbh error -Warning: PDO::query(): SQLSTATE[22018]: Invalid character value for cast specification: -413 conversion error from string "str" in %s on line 10 +Warning: PDO::query(): SQLSTATE[22018]: Invalid character value for cast specification: -413 conversion error from string "str" in %s on line %d stmt error -Warning: PDOStatement::execute(): SQLSTATE[22018]: Invalid character value for cast specification: -413 conversion error from string "str" in %s on line 16 +Warning: PDOStatement::execute(): SQLSTATE[22018]: Invalid character value for cast specification: -413 conversion error from string "str" in %s on line %d diff --git a/ext/pdo_firebird/tests/execute.phpt b/ext/pdo_firebird/tests/execute.phpt index c5a772597748..3d9976f0f955 100644 --- a/ext/pdo_firebird/tests/execute.phpt +++ b/ext/pdo_firebird/tests/execute.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 getAttribute(PDO::ATTR_CONNECTION_STATUS)); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); @@ -47,6 +48,7 @@ echo "done\n"; --CLEAN-- exec('DROP TABLE test_execute'); unset($dbh); --EXPECT-- diff --git a/ext/pdo_firebird/tests/execute_block.phpt b/ext/pdo_firebird/tests/execute_block.phpt index 1b5a3e240eb4..f8a3e80c859a 100644 --- a/ext/pdo_firebird/tests/execute_block.phpt +++ b/ext/pdo_firebird/tests/execute_block.phpt @@ -10,9 +10,10 @@ A bug in firebird causes a memory leak when calling `isc_attach_database()`. See https://github.com/FirebirdSQL/firebird/issues/7849 --FILE-- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); +$dbh = getDbConnection(); +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $sql = ' execute block (a int = :e, b int = :d) @@ -29,16 +30,16 @@ begin suspend; end '; - $query = $dbh->prepare($sql); - $query->execute(['d' => 1, 'e' => 2]); - $row = $query->fetch(\PDO::FETCH_OBJ); - var_dump($row->N); - var_dump($row->M); - unset($query); - unset($dbh); - echo "done\n"; +$query = $dbh->prepare($sql); +$query->execute(['d' => 1, 'e' => 2]); +$row = $query->fetch(\PDO::FETCH_OBJ); +var_dump($row->N); +var_dump($row->M); +unset($query); +unset($dbh); +echo "done\n"; ?> --EXPECT-- int(13) diff --git a/ext/pdo_firebird/tests/gh10908.phpt b/ext/pdo_firebird/tests/gh10908.phpt index 97f3327db698..e94f8eeed140 100644 --- a/ext/pdo_firebird/tests/gh10908.phpt +++ b/ext/pdo_firebird/tests/gh10908.phpt @@ -29,6 +29,7 @@ CREATE TABLE gh10908( MYBOOL BOOLEAN ); EOT; +$dbh = getDbConnection(); $dbh->exec($sql); $dbh->exec("INSERT INTO gh10908 VALUES(1, 'ABC', 12.34, 1.0, 2.0, '2023-03-24 17:39', '2023-03-24', '17:39', 'abcdefg', 32767, 200000, 'azertyuiop', false);"); @@ -57,6 +58,7 @@ echo "Did not crash\n"; --CLEAN-- exec("DROP TABLE gh10908"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/gh8576.phpt b/ext/pdo_firebird/tests/gh8576.phpt index 27f83f6ebf00..432cf605f7a4 100644 --- a/ext/pdo_firebird/tests/gh8576.phpt +++ b/ext/pdo_firebird/tests/gh8576.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec("CREATE TABLE gh8576 (name CHAR(1) CHARACTER SET UTF8)"); $dbh->exec("INSERT INTO gh8576 VALUES ('A')"); $stmt = $dbh->query("SELECT * FROM gh8576"); @@ -29,6 +30,7 @@ array(1) { --CLEAN-- exec("DROP TABLE gh8576"); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/ignore_parammarks.phpt b/ext/pdo_firebird/tests/ignore_parammarks.phpt index 9478eb024c53..ceb1b0ff57f3 100644 --- a/ext/pdo_firebird/tests/ignore_parammarks.phpt +++ b/ext/pdo_firebird/tests/ignore_parammarks.phpt @@ -10,11 +10,12 @@ A bug in firebird causes a memory leak when calling `isc_attach_database()`. See https://github.com/FirebirdSQL/firebird/issues/7849 --FILE-- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); +$dbh = getDbConnection(); +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); - $sql = ' +$sql = ' select 1 as n -- :f from rdb$database @@ -31,15 +32,14 @@ select 1 as n from rdb$database where 1=:d /* and :f = 5 */ and 2=:e '; - $query = $dbh->prepare($sql); - $query->execute(['d' => 1, 'e' => 2]); - $row = $query->fetch(\PDO::FETCH_OBJ); - var_dump($row->N); - unset($query); - - unset($dbh); - echo "done\n"; +$query = $dbh->prepare($sql); +$query->execute(['d' => 1, 'e' => 2]); +$row = $query->fetch(\PDO::FETCH_OBJ); +var_dump($row->N); +unset($query); +unset($dbh); +echo "done\n"; ?> --EXPECT-- int(1) diff --git a/ext/pdo_firebird/tests/pdofirebird_001.phpt b/ext/pdo_firebird/tests/pdofirebird_001.phpt new file mode 100644 index 000000000000..d34bfa1943f9 --- /dev/null +++ b/ext/pdo_firebird/tests/pdofirebird_001.phpt @@ -0,0 +1,44 @@ +--TEST-- +PDO_firebird subclass basic +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--FILE-- +query('RECREATE TABLE pdofirebird_001 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); +$db->query("INSERT INTO pdofirebird_001 VALUES (1, 'PHP')"); +$db->query("INSERT INTO pdofirebird_001 VALUES (2, 'PHP6')"); + +foreach ($db->query('SELECT name FROM pdofirebird_001') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +exec("DROP TABLE pdofirebird_001"); +unset($dbh); +?> +--EXPECT-- +array(2) { + ["NAME"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["NAME"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_firebird/tests/pdofirebird_002.phpt b/ext/pdo_firebird/tests/pdofirebird_002.phpt new file mode 100644 index 000000000000..cf3836ec8b43 --- /dev/null +++ b/ext/pdo_firebird/tests/pdofirebird_002.phpt @@ -0,0 +1,55 @@ +--TEST-- +PDO_firebird connect through PDO::connect +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--FILE-- +query('RECREATE TABLE pdofirebird_002 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); + +$db->exec("INSERT INTO pdofirebird_002 VALUES(1, 'A')"); +$db->exec("INSERT INTO pdofirebird_002 VALUES(2, 'B')"); +$db->exec("INSERT INTO pdofirebird_002 VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM pdofirebird_002') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +exec("DROP TABLE pdofirebird_002"); +unset($dbh); +?> +--EXPECT-- +array(2) { + ["NAME"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["NAME"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["NAME"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_firebird/tests/rowCount.phpt b/ext/pdo_firebird/tests/rowCount.phpt index d435cc10a249..e7e8dcd6c3c2 100644 --- a/ext/pdo_firebird/tests/rowCount.phpt +++ b/ext/pdo_firebird/tests/rowCount.phpt @@ -12,6 +12,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 require("testdb.inc"); +$dbh = getDbConnection(); $dbh->exec('CREATE TABLE test_rowcount (A VARCHAR(10))'); $dbh->exec("INSERT INTO test_rowcount VALUES ('A')"); $dbh->exec("INSERT INTO test_rowcount VALUES ('A')"); @@ -41,6 +42,7 @@ unset($dbh); --CLEAN-- exec('DROP TABLE test_rowcount'); unset($dbh); --EXPECT-- diff --git a/ext/pdo_firebird/tests/testdb.inc b/ext/pdo_firebird/tests/testdb.inc index 23008fa0d99d..0ccf9ab81d37 100644 --- a/ext/pdo_firebird/tests/testdb.inc +++ b/ext/pdo_firebird/tests/testdb.inc @@ -17,5 +17,12 @@ if(!PDO_FIREBIRD_TEST_DSN) die('Error: PDO_FIREBIRD_TEST_DSN must be set'); } -$dbh = new PDO(PDO_FIREBIRD_TEST_DSN, PDO_FIREBIRD_TEST_USER, PDO_FIREBIRD_TEST_PASS) or die; +function getDbConnection($class = PDO::class): PDO { + return new $class(PDO_FIREBIRD_TEST_DSN, PDO_FIREBIRD_TEST_USER, PDO_FIREBIRD_TEST_PASS); +} + +function connectToDb(): PdoFirebird { + return PdoFirebird::connect(PDO_FIREBIRD_TEST_DSN, PDO_FIREBIRD_TEST_USER, PDO_FIREBIRD_TEST_PASS); +} + ?> diff --git a/ext/pdo_firebird/tests/transaction_access_mode.phpt b/ext/pdo_firebird/tests/transaction_access_mode.phpt index 3f5c9bcca701..7614feccc7ae 100644 --- a/ext/pdo_firebird/tests/transaction_access_mode.phpt +++ b/ext/pdo_firebird/tests/transaction_access_mode.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec('DROP TABLE transaction_access_mode'); unset($dbh); ?> diff --git a/ext/pdo_firebird/tests/transaction_isolation_level_attr.phpt b/ext/pdo_firebird/tests/transaction_isolation_level_attr.phpt index 3f3ccb402ec9..5b6852286aad 100644 --- a/ext/pdo_firebird/tests/transaction_isolation_level_attr.phpt +++ b/ext/pdo_firebird/tests/transaction_isolation_level_attr.phpt @@ -11,6 +11,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 exec('DROP TABLE txn_isolation_level_behavior'); unset($dbh); ?> diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c index 7aa39ca35a6b..4768a861fe12 100644 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -26,6 +26,10 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_mysql.h" #include "php_pdo_mysql_int.h" +#include "pdo_mysql_arginfo.h" + +zend_class_entry *pdomysql_ce; +static pdo_driver_class_entry pdomysql_pdo_driver_class_entry; #ifdef COMPILE_DL_PDO_MYSQL #ifdef ZTS @@ -81,6 +85,20 @@ static const MYSQLND_REVERSE_API pdo_mysql_reverse_api = { }; #endif +/* proto string PDO::mysqlGetWarningCount() + * Returns the number of SQL warnings during the execution of the last statement + */ +PHP_METHOD(PdoMysql, getWarningCount) +{ + pdo_dbh_t *dbh; + pdo_mysql_db_handle *H; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_mysql_db_handle *)dbh->driver_data; + RETURN_LONG(mysql_warning_count(H->server)); +} /* {{{ PHP_INI_BEGIN */ PHP_INI_BEGIN() @@ -118,7 +136,7 @@ static PHP_MINIT_FUNCTION(pdo_mysql) REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CAPATH", (zend_long)PDO_MYSQL_ATTR_SSL_CAPATH); REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CIPHER", (zend_long)PDO_MYSQL_ATTR_SSL_CIPHER); #if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND) - REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SERVER_PUBLIC_KEY", (zend_long)PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SERVER_PUBLIC_KEY", (zend_long)PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY); #endif REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_MULTI_STATEMENTS", (zend_long)PDO_MYSQL_ATTR_MULTI_STATEMENTS); #ifdef PDO_USE_MYSQLND @@ -132,6 +150,15 @@ static PHP_MINIT_FUNCTION(pdo_mysql) mysqlnd_reverse_api_register_api(&pdo_mysql_reverse_api); #endif + pdomysql_ce = register_class_PdoMysql(pdo_dbh_ce); + pdomysql_ce->create_object = pdo_dbh_new; + + pdomysql_pdo_driver_class_entry.driver_name = "mysql"; + pdomysql_pdo_driver_class_entry.driver_ce = pdomysql_ce; + if (pdo_register_driver_specific_class(&pdomysql_pdo_driver_class_entry) == FAILURE) { + return FAILURE; + } + return php_pdo_register_driver(&pdo_mysql_driver); } /* }}} */ diff --git a/ext/pdo_mysql/pdo_mysql.stub.php b/ext/pdo_mysql/pdo_mysql.stub.php new file mode 100644 index 000000000000..a1b9a13a80b6 --- /dev/null +++ b/ext/pdo_mysql/pdo_mysql.stub.php @@ -0,0 +1,75 @@ + 50605 || defined(PDO_USE_MYSQLND) + /** @cvalue PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY */ + public const int ATTR_SERVER_PUBLIC_KEY = UNKNOWN; +#endif + + /** @cvalue PDO_MYSQL_ATTR_MULTI_STATEMENTS */ + public const int ATTR_MULTI_STATEMENTS = UNKNOWN; + + /** @cvalue PDO_MYSQL_ATTR_SSL_VERIFY_SERVER_CERT */ + public const int ATTR_SSL_VERIFY_SERVER_CERT = UNKNOWN; + +#if MYSQL_VERSION_ID >= 80021 || defined(PDO_USE_MYSQLND) + /** @cvalue PDO_MYSQL_ATTR_LOCAL_INFILE_DIRECTORY */ + public const int ATTR_LOCAL_INFILE_DIRECTORY = UNKNOWN; +#endif + + public function getWarningCount(): int {} +} diff --git a/ext/pdo_mysql/pdo_mysql_arginfo.h b/ext/pdo_mysql/pdo_mysql_arginfo.h new file mode 100644 index 000000000000..f3ef371147f0 --- /dev/null +++ b/ext/pdo_mysql/pdo_mysql_arginfo.h @@ -0,0 +1,149 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 3e797a44fc026ab43bf4bec26cf6fe492116cb25 */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_PdoMysql_getWarningCount, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + + +ZEND_METHOD(PdoMysql, getWarningCount); + + +static const zend_function_entry class_PdoMysql_methods[] = { + ZEND_ME(PdoMysql, getWarningCount, arginfo_class_PdoMysql_getWarningCount, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_class_entry *register_class_PdoMysql(zend_class_entry *class_entry_PDO) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "PdoMysql", class_PdoMysql_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_PDO); + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + zval const_ATTR_USE_BUFFERED_QUERY_value; + ZVAL_LONG(&const_ATTR_USE_BUFFERED_QUERY_value, PDO_MYSQL_ATTR_USE_BUFFERED_QUERY); + zend_string *const_ATTR_USE_BUFFERED_QUERY_name = zend_string_init_interned("ATTR_USE_BUFFERED_QUERY", sizeof("ATTR_USE_BUFFERED_QUERY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_USE_BUFFERED_QUERY_name, &const_ATTR_USE_BUFFERED_QUERY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_USE_BUFFERED_QUERY_name); + + zval const_ATTR_LOCAL_INFILE_value; + ZVAL_LONG(&const_ATTR_LOCAL_INFILE_value, PDO_MYSQL_ATTR_LOCAL_INFILE); + zend_string *const_ATTR_LOCAL_INFILE_name = zend_string_init_interned("ATTR_LOCAL_INFILE", sizeof("ATTR_LOCAL_INFILE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_LOCAL_INFILE_name, &const_ATTR_LOCAL_INFILE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_LOCAL_INFILE_name); + + zval const_ATTR_INIT_COMMAND_value; + ZVAL_LONG(&const_ATTR_INIT_COMMAND_value, PDO_MYSQL_ATTR_INIT_COMMAND); + zend_string *const_ATTR_INIT_COMMAND_name = zend_string_init_interned("ATTR_INIT_COMMAND", sizeof("ATTR_INIT_COMMAND") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_INIT_COMMAND_name, &const_ATTR_INIT_COMMAND_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_INIT_COMMAND_name); +#if !defined(PDO_USE_MYSQLND) + + zval const_ATTR_MAX_BUFFER_SIZE_value; + ZVAL_LONG(&const_ATTR_MAX_BUFFER_SIZE_value, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE); + zend_string *const_ATTR_MAX_BUFFER_SIZE_name = zend_string_init_interned("ATTR_MAX_BUFFER_SIZE", sizeof("ATTR_MAX_BUFFER_SIZE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_MAX_BUFFER_SIZE_name, &const_ATTR_MAX_BUFFER_SIZE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_MAX_BUFFER_SIZE_name); +#endif +#if !defined(PDO_USE_MYSQLND) + + zval const_ATTR_READ_DEFAULT_FILE_value; + ZVAL_LONG(&const_ATTR_READ_DEFAULT_FILE_value, PDO_MYSQL_ATTR_READ_DEFAULT_FILE); + zend_string *const_ATTR_READ_DEFAULT_FILE_name = zend_string_init_interned("ATTR_READ_DEFAULT_FILE", sizeof("ATTR_READ_DEFAULT_FILE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_READ_DEFAULT_FILE_name, &const_ATTR_READ_DEFAULT_FILE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_READ_DEFAULT_FILE_name); +#endif +#if !defined(PDO_USE_MYSQLND) + + zval const_ATTR_READ_DEFAULT_GROUP_value; + ZVAL_LONG(&const_ATTR_READ_DEFAULT_GROUP_value, PDO_MYSQL_ATTR_READ_DEFAULT_GROUP); + zend_string *const_ATTR_READ_DEFAULT_GROUP_name = zend_string_init_interned("ATTR_READ_DEFAULT_GROUP", sizeof("ATTR_READ_DEFAULT_GROUP") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_READ_DEFAULT_GROUP_name, &const_ATTR_READ_DEFAULT_GROUP_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_READ_DEFAULT_GROUP_name); +#endif + + zval const_ATTR_COMPRESS_value; + ZVAL_LONG(&const_ATTR_COMPRESS_value, PDO_MYSQL_ATTR_COMPRESS); + zend_string *const_ATTR_COMPRESS_name = zend_string_init_interned("ATTR_COMPRESS", sizeof("ATTR_COMPRESS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_COMPRESS_name, &const_ATTR_COMPRESS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_COMPRESS_name); + + zval const_ATTR_DIRECT_QUERY_value; + ZVAL_LONG(&const_ATTR_DIRECT_QUERY_value, PDO_MYSQL_ATTR_DIRECT_QUERY); + zend_string *const_ATTR_DIRECT_QUERY_name = zend_string_init_interned("ATTR_DIRECT_QUERY", sizeof("ATTR_DIRECT_QUERY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_DIRECT_QUERY_name, &const_ATTR_DIRECT_QUERY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_DIRECT_QUERY_name); + + zval const_ATTR_FOUND_ROWS_value; + ZVAL_LONG(&const_ATTR_FOUND_ROWS_value, PDO_MYSQL_ATTR_FOUND_ROWS); + zend_string *const_ATTR_FOUND_ROWS_name = zend_string_init_interned("ATTR_FOUND_ROWS", sizeof("ATTR_FOUND_ROWS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_FOUND_ROWS_name, &const_ATTR_FOUND_ROWS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_FOUND_ROWS_name); + + zval const_ATTR_IGNORE_SPACE_value; + ZVAL_LONG(&const_ATTR_IGNORE_SPACE_value, PDO_MYSQL_ATTR_IGNORE_SPACE); + zend_string *const_ATTR_IGNORE_SPACE_name = zend_string_init_interned("ATTR_IGNORE_SPACE", sizeof("ATTR_IGNORE_SPACE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_IGNORE_SPACE_name, &const_ATTR_IGNORE_SPACE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_IGNORE_SPACE_name); + + zval const_ATTR_SSL_KEY_value; + ZVAL_LONG(&const_ATTR_SSL_KEY_value, PDO_MYSQL_ATTR_SSL_KEY); + zend_string *const_ATTR_SSL_KEY_name = zend_string_init_interned("ATTR_SSL_KEY", sizeof("ATTR_SSL_KEY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_SSL_KEY_name, &const_ATTR_SSL_KEY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_SSL_KEY_name); + + zval const_ATTR_SSL_CERT_value; + ZVAL_LONG(&const_ATTR_SSL_CERT_value, PDO_MYSQL_ATTR_SSL_CERT); + zend_string *const_ATTR_SSL_CERT_name = zend_string_init_interned("ATTR_SSL_CERT", sizeof("ATTR_SSL_CERT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_SSL_CERT_name, &const_ATTR_SSL_CERT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_SSL_CERT_name); + + zval const_ATTR_SSL_CA_value; + ZVAL_LONG(&const_ATTR_SSL_CA_value, PDO_MYSQL_ATTR_SSL_CA); + zend_string *const_ATTR_SSL_CA_name = zend_string_init_interned("ATTR_SSL_CA", sizeof("ATTR_SSL_CA") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_SSL_CA_name, &const_ATTR_SSL_CA_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_SSL_CA_name); + + zval const_ATTR_SSL_CAPATH_value; + ZVAL_LONG(&const_ATTR_SSL_CAPATH_value, PDO_MYSQL_ATTR_SSL_CAPATH); + zend_string *const_ATTR_SSL_CAPATH_name = zend_string_init_interned("ATTR_SSL_CAPATH", sizeof("ATTR_SSL_CAPATH") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_SSL_CAPATH_name, &const_ATTR_SSL_CAPATH_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_SSL_CAPATH_name); + + zval const_ATTR_SSL_CIPHER_value; + ZVAL_LONG(&const_ATTR_SSL_CIPHER_value, PDO_MYSQL_ATTR_SSL_CIPHER); + zend_string *const_ATTR_SSL_CIPHER_name = zend_string_init_interned("ATTR_SSL_CIPHER", sizeof("ATTR_SSL_CIPHER") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_SSL_CIPHER_name, &const_ATTR_SSL_CIPHER_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_SSL_CIPHER_name); +#if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND) + + zval const_ATTR_SERVER_PUBLIC_KEY_value; + ZVAL_LONG(&const_ATTR_SERVER_PUBLIC_KEY_value, PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY); + zend_string *const_ATTR_SERVER_PUBLIC_KEY_name = zend_string_init_interned("ATTR_SERVER_PUBLIC_KEY", sizeof("ATTR_SERVER_PUBLIC_KEY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_SERVER_PUBLIC_KEY_name, &const_ATTR_SERVER_PUBLIC_KEY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_SERVER_PUBLIC_KEY_name); +#endif + + zval const_ATTR_MULTI_STATEMENTS_value; + ZVAL_LONG(&const_ATTR_MULTI_STATEMENTS_value, PDO_MYSQL_ATTR_MULTI_STATEMENTS); + zend_string *const_ATTR_MULTI_STATEMENTS_name = zend_string_init_interned("ATTR_MULTI_STATEMENTS", sizeof("ATTR_MULTI_STATEMENTS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_MULTI_STATEMENTS_name, &const_ATTR_MULTI_STATEMENTS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_MULTI_STATEMENTS_name); + + zval const_ATTR_SSL_VERIFY_SERVER_CERT_value; + ZVAL_LONG(&const_ATTR_SSL_VERIFY_SERVER_CERT_value, PDO_MYSQL_ATTR_SSL_VERIFY_SERVER_CERT); + zend_string *const_ATTR_SSL_VERIFY_SERVER_CERT_name = zend_string_init_interned("ATTR_SSL_VERIFY_SERVER_CERT", sizeof("ATTR_SSL_VERIFY_SERVER_CERT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_SSL_VERIFY_SERVER_CERT_name, &const_ATTR_SSL_VERIFY_SERVER_CERT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_SSL_VERIFY_SERVER_CERT_name); +#if MYSQL_VERSION_ID >= 80021 || defined(PDO_USE_MYSQLND) + + zval const_ATTR_LOCAL_INFILE_DIRECTORY_value; + ZVAL_LONG(&const_ATTR_LOCAL_INFILE_DIRECTORY_value, PDO_MYSQL_ATTR_LOCAL_INFILE_DIRECTORY); + zend_string *const_ATTR_LOCAL_INFILE_DIRECTORY_name = zend_string_init_interned("ATTR_LOCAL_INFILE_DIRECTORY", sizeof("ATTR_LOCAL_INFILE_DIRECTORY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_LOCAL_INFILE_DIRECTORY_name, &const_ATTR_LOCAL_INFILE_DIRECTORY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_LOCAL_INFILE_DIRECTORY_name); +#endif + + return class_entry; +} diff --git a/ext/pdo_mysql/tests/inc/mysql_pdo_test.inc b/ext/pdo_mysql/tests/inc/mysql_pdo_test.inc index 984eab060945..a783d812ea43 100644 --- a/ext/pdo_mysql/tests/inc/mysql_pdo_test.inc +++ b/ext/pdo_mysql/tests/inc/mysql_pdo_test.inc @@ -8,7 +8,7 @@ foreach ($env as $k => $v) { class MySQLPDOTest extends PDOTest { - static function factory($classname = 'PDO', $mydsn = null, $myAttr = null) { + static function factory($classname = PDO::class, $mydsn = null, $myAttr = null) { $dsn = self::getDSN($mydsn); $user = PDO_MYSQL_TEST_USER; $pass = PDO_MYSQL_TEST_PASS; @@ -35,6 +35,33 @@ class MySQLPDOTest extends PDOTest { return self::factory('PDO', null, $attr); } + static function connect($drop_test_tables = false, $myattr = null, $mydsn = null) { + $dsn = self::getDSN($mydsn); + $user = PDO_MYSQL_TEST_USER; + $pass = PDO_MYSQL_TEST_PASS; + $attr = getenv('PDOTEST_ATTR'); + + if (is_string($attr) && strlen($attr)) { + $attr = unserialize($attr); + } else { + $attr = null; + } + if ($user === false) + $user = NULL; + if ($pass === false) + $pass = NULL; + + $db = PDO::connect($dsn, $user, $pass, $attr); + if (!$db) { + die("Could not create PDO object (DSN=$dsn, user=$user)\n"); + } + + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + + return $db; + } + static function createTestTable($table, $db, $engine = null) { if (!$engine) $engine = PDO_MYSQL_TEST_ENGINE; diff --git a/ext/pdo_mysql/tests/pdo_mysql_interface.phpt b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt index 4f7a2316a148..5d7fd83c2d8e 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_interface.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt @@ -14,6 +14,7 @@ MySQLPDOTest::skipNotTransactionalEngine(); $expected = [ '__construct' => true, + 'connect' => true, 'prepare' => true, 'beginTransaction' => true, 'commit' => true, diff --git a/ext/pdo_mysql/tests/pdomysql_001.phpt b/ext/pdo_mysql/tests/pdomysql_001.phpt new file mode 100644 index 000000000000..37a564403198 --- /dev/null +++ b/ext/pdo_mysql/tests/pdomysql_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +PDO_mysql subclass basic +--EXTENSIONS-- +pdo_mysql +--SKIPIF-- + +--FILE-- +query('CREATE TABLE IF NOT EXISTS pdomysql_001 (id INT, name TEXT)'); + +$db->query('INSERT INTO pdomysql_001 VALUES (NULL, "PHP")'); +$db->query('INSERT INTO pdomysql_001 VALUES (NULL, "PHP6")'); + +foreach ($db->query('SELECT name FROM pdomysql_001') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +exec('DROP TABLE IF EXISTS pdomysql_001'); +?> +--EXPECT-- +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_mysql/tests/pdomysql_002.phpt b/ext/pdo_mysql/tests/pdomysql_002.phpt new file mode 100644 index 000000000000..1be1739e1ee0 --- /dev/null +++ b/ext/pdo_mysql/tests/pdomysql_002.phpt @@ -0,0 +1,54 @@ +--TEST-- +PDO_mysql connect through PDO::connect +--EXTENSIONS-- +pdo_mysql +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE IF NOT EXISTS pdomysql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO pdomysql_002 VALUES(1, 'A'), (2, 'B'), (3, 'C')"); + +foreach ($db->query('SELECT name FROM pdomysql_002') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +query('DROP TABLE pdomysql_002'); +?> +--EXPECT-- +array(2) { + ["name"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["name"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["name"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_mysql/tests/pdomysql_003.phpt b/ext/pdo_mysql/tests/pdomysql_003.phpt new file mode 100644 index 000000000000..4ba538b969dd --- /dev/null +++ b/ext/pdo_mysql/tests/pdomysql_003.phpt @@ -0,0 +1,29 @@ +--TEST-- +PDO_mysql getWarningCount +--EXTENSIONS-- +pdo_mysql +--SKIPIF-- + +--FILE-- +query($q); + printf("Query %s produced %d warnings\n", $q, $db->getWarningCount()); +}; +$assertWarnings($db, 'SELECT 1 = 1', 0); +$assertWarnings($db, 'SELECT 1 = "A"', 1); + +--EXPECT-- +Query SELECT 1 = 1 produced 0 warnings +Query SELECT 1 = "A" produced 1 warnings diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c index 8e090a397f06..1bb30e9a0aa4 100644 --- a/ext/pdo_odbc/pdo_odbc.c +++ b/ext/pdo_odbc/pdo_odbc.c @@ -27,6 +27,9 @@ #include "php_pdo_odbc_int.h" #include "pdo_odbc_arginfo.h" +zend_class_entry *pdoodbc_ce; +static pdo_driver_class_entry pdoodbc_pdo_driver_class_entry; + /* {{{ pdo_odbc_deps[] */ static const zend_module_dep pdo_odbc_deps[] = { ZEND_MOD_REQUIRED("pdo") @@ -105,6 +108,15 @@ PHP_MINIT_FUNCTION(pdo_odbc) REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_DRIVER", SQL_CUR_USE_DRIVER); REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_ODBC", SQL_CUR_USE_ODBC); + pdoodbc_ce = register_class_PdoOdbc(pdo_dbh_ce); + pdoodbc_ce->create_object = pdo_dbh_new; + + pdoodbc_pdo_driver_class_entry.driver_name = "odbc"; + pdoodbc_pdo_driver_class_entry.driver_ce = pdoodbc_ce; + if (pdo_register_driver_specific_class(&pdoodbc_pdo_driver_class_entry) == FAILURE) { + return FAILURE; + } + return SUCCESS; } /* }}} */ diff --git a/ext/pdo_odbc/pdo_odbc.stub.php b/ext/pdo_odbc/pdo_odbc.stub.php index 745be283375d..c83d6f29f06f 100644 --- a/ext/pdo_odbc/pdo_odbc.stub.php +++ b/ext/pdo_odbc/pdo_odbc.stub.php @@ -7,3 +7,25 @@ * @cvalue PDO_ODBC_TYPE */ const PDO_ODBC_TYPE = UNKNOWN; + +/** + * @strict-properties + * @not-serializable + */ +class PdoOdbc extends PDO +{ + /** @cvalue PDO_ODBC_ATTR_USE_CURSOR_LIBRARY */ + public const int ATTR_USE_CURSOR_LIBRARY = UNKNOWN; + + /** @cvalue PDO_ODBC_ATTR_ASSUME_UTF8 */ + public const int ATTR_ASSUME_UTF8 = UNKNOWN; + + /** @cvalue SQL_CUR_USE_IF_NEEDED */ + public const int SQL_USE_IF_NEEDED = UNKNOWN; + + /** @cvalue SQL_CUR_USE_DRIVER */ + public const int SQL_USE_DRIVER = UNKNOWN; + + /** @cvalue SQL_CUR_USE_ODBC */ + public const int SQL_USE_ODBC = UNKNOWN; +} diff --git a/ext/pdo_odbc/pdo_odbc_arginfo.h b/ext/pdo_odbc/pdo_odbc_arginfo.h index 8a26d600d72f..4816748c6474 100644 --- a/ext/pdo_odbc/pdo_odbc_arginfo.h +++ b/ext/pdo_odbc/pdo_odbc_arginfo.h @@ -1,9 +1,55 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7927099133bf60cab0464aae8c9de8f7ef2732bd */ + * Stub hash: 3cc19acf5943c09a011f15d9ef6d0f443d4b8a71 */ + +static const zend_function_entry class_PdoOdbc_methods[] = { + ZEND_FE_END +}; + static void register_pdo_odbc_symbols(int module_number) { REGISTER_STRING_CONSTANT("PDO_ODBC_TYPE", PDO_ODBC_TYPE, CONST_PERSISTENT); } + +static zend_class_entry *register_class_PdoOdbc(zend_class_entry *class_entry_PDO) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "PdoOdbc", class_PdoOdbc_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_PDO); + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + zval const_ATTR_USE_CURSOR_LIBRARY_value; + ZVAL_LONG(&const_ATTR_USE_CURSOR_LIBRARY_value, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY); + zend_string *const_ATTR_USE_CURSOR_LIBRARY_name = zend_string_init_interned("ATTR_USE_CURSOR_LIBRARY", sizeof("ATTR_USE_CURSOR_LIBRARY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_USE_CURSOR_LIBRARY_name, &const_ATTR_USE_CURSOR_LIBRARY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_USE_CURSOR_LIBRARY_name); + + zval const_ATTR_ASSUME_UTF8_value; + ZVAL_LONG(&const_ATTR_ASSUME_UTF8_value, PDO_ODBC_ATTR_ASSUME_UTF8); + zend_string *const_ATTR_ASSUME_UTF8_name = zend_string_init_interned("ATTR_ASSUME_UTF8", sizeof("ATTR_ASSUME_UTF8") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_ASSUME_UTF8_name, &const_ATTR_ASSUME_UTF8_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_ASSUME_UTF8_name); + + zval const_SQL_USE_IF_NEEDED_value; + ZVAL_LONG(&const_SQL_USE_IF_NEEDED_value, SQL_CUR_USE_IF_NEEDED); + zend_string *const_SQL_USE_IF_NEEDED_name = zend_string_init_interned("SQL_USE_IF_NEEDED", sizeof("SQL_USE_IF_NEEDED") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_SQL_USE_IF_NEEDED_name, &const_SQL_USE_IF_NEEDED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_SQL_USE_IF_NEEDED_name); + + zval const_SQL_USE_DRIVER_value; + ZVAL_LONG(&const_SQL_USE_DRIVER_value, SQL_CUR_USE_DRIVER); + zend_string *const_SQL_USE_DRIVER_name = zend_string_init_interned("SQL_USE_DRIVER", sizeof("SQL_USE_DRIVER") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_SQL_USE_DRIVER_name, &const_SQL_USE_DRIVER_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_SQL_USE_DRIVER_name); + + zval const_SQL_USE_ODBC_value; + ZVAL_LONG(&const_SQL_USE_ODBC_value, SQL_CUR_USE_ODBC); + zend_string *const_SQL_USE_ODBC_name = zend_string_init_interned("SQL_USE_ODBC", sizeof("SQL_USE_ODBC") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_SQL_USE_ODBC_name, &const_SQL_USE_ODBC_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_SQL_USE_ODBC_name); + + return class_entry; +} diff --git a/ext/pdo_odbc/tests/pdoodbc_001.phpt b/ext/pdo_odbc/tests/pdoodbc_001.phpt new file mode 100644 index 000000000000..70985dcb6d3a --- /dev/null +++ b/ext/pdo_odbc/tests/pdoodbc_001.phpt @@ -0,0 +1,45 @@ +--TEST-- +PDO_odbc subclass basic +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--FILE-- +query("CREATE TABLE pdoodbc_001 (id INT, name TEXT)"); +$db->query("INSERT INTO pdoodbc_001 VALUES (NULL, 'PHP'), (NULL, 'PHP6')"); + +foreach ($db->query('SELECT name FROM pdoodbc_001') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +exec("DROP TABLE IF EXISTS pdoodbc_001"); +?> +--EXPECT-- +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_odbc/tests/pdoodbc_002.phpt b/ext/pdo_odbc/tests/pdoodbc_002.phpt new file mode 100644 index 000000000000..fd3547745026 --- /dev/null +++ b/ext/pdo_odbc/tests/pdoodbc_002.phpt @@ -0,0 +1,54 @@ +--TEST-- +PDO_mysql connect through PDO::connect +--EXTENSIONS-- +PDO_odbc +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdoodbc_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO pdoodbc_002 VALUES(1, 'A'), (2, 'B'), (3, 'C')"); + +foreach ($db->query('SELECT name FROM pdoodbc_002') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +exec("DROP TABLE IF EXISTS pdoodbc_002"); +?> +--EXPECT-- +array(2) { + ["name"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["name"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["name"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 582915a5a34f..72c6106cdfa5 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -22,9 +22,14 @@ #include "php_ini.h" #include "ext/standard/info.h" #include "pdo/php_pdo.h" +#include "pdo/php_pdo_int.h" #include "pdo/php_pdo_driver.h" #include "php_pdo_pgsql.h" #include "php_pdo_pgsql_int.h" +#include "pdo_pgsql_arginfo.h" + +zend_class_entry *PdoPgsql_ce; +static pdo_driver_class_entry PdoPgsql_pdo_driver_class_entry; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_pgsql_deps[] = { @@ -53,6 +58,90 @@ zend_module_entry pdo_pgsql_module_entry = { ZEND_GET_MODULE(pdo_pgsql) #endif +/* Escape an identifier for insertion into a text field */ +PHP_METHOD(PdoPgsql, escapeIdentifier) +{ + zend_string *from = NULL; + char *tmp; + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + /* Obtain db Handle */ + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + tmp = PQescapeIdentifier(H->server, ZSTR_VAL(from), ZSTR_LEN(from)); + if (!tmp) { + // TODO - exception + php_error_docref(NULL, E_WARNING,"Failed to escape identifier"); + RETURN_FALSE; + } + + RETVAL_STRING(tmp); + PQfreemem(tmp); +} + +/* Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyFromArray) +{ + pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +/* Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyFromFile) +{ + pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +/* Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyToFile) +{ + pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +/* Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyToArray) +{ + pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +/* Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobCreate) +{ + pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +/* Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobOpen) +{ + pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +/* Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobUnlink) +{ + pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +/* Get asynchronous notification */ +PHP_METHOD(PdoPgsql, getNotify) +{ + pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +/* Get backend(server) pid */ +PHP_METHOD(PdoPgsql, getPid) +{ + pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + /* true global environment */ /* {{{ PHP_MINIT_FUNCTION */ @@ -65,6 +154,15 @@ PHP_MINIT_FUNCTION(pdo_pgsql) REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long)PGSQL_TRANSACTION_INERROR); REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long)PGSQL_TRANSACTION_UNKNOWN); + PdoPgsql_ce = register_class_PdoPgsql(pdo_dbh_ce); + PdoPgsql_ce->create_object = pdo_dbh_new; + + PdoPgsql_pdo_driver_class_entry.driver_name = "pgsql"; + PdoPgsql_pdo_driver_class_entry.driver_ce = PdoPgsql_ce; + if (pdo_register_driver_specific_class(&PdoPgsql_pdo_driver_class_entry) == FAILURE) { + return FAILURE; + } + return php_pdo_register_driver(&pdo_pgsql_driver); } /* }}} */ diff --git a/ext/pdo_pgsql/pdo_pgsql.stub.php b/ext/pdo_pgsql/pdo_pgsql.stub.php new file mode 100644 index 000000000000..eb7707147a19 --- /dev/null +++ b/ext/pdo_pgsql/pdo_pgsql.stub.php @@ -0,0 +1,50 @@ +ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + zval const_ATTR_DISABLE_PREPARES_value; + ZVAL_LONG(&const_ATTR_DISABLE_PREPARES_value, PDO_PGSQL_ATTR_DISABLE_PREPARES); + zend_string *const_ATTR_DISABLE_PREPARES_name = zend_string_init_interned("ATTR_DISABLE_PREPARES", sizeof("ATTR_DISABLE_PREPARES") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_DISABLE_PREPARES_name, &const_ATTR_DISABLE_PREPARES_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_DISABLE_PREPARES_name); + + zval const_TRANSACTION_IDLE_value; + ZVAL_LONG(&const_TRANSACTION_IDLE_value, PGSQL_TRANSACTION_IDLE); + zend_string *const_TRANSACTION_IDLE_name = zend_string_init_interned("TRANSACTION_IDLE", sizeof("TRANSACTION_IDLE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_IDLE_name, &const_TRANSACTION_IDLE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_IDLE_name); + + zval const_TRANSACTION_ACTIVE_value; + ZVAL_LONG(&const_TRANSACTION_ACTIVE_value, PGSQL_TRANSACTION_ACTIVE); + zend_string *const_TRANSACTION_ACTIVE_name = zend_string_init_interned("TRANSACTION_ACTIVE", sizeof("TRANSACTION_ACTIVE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_ACTIVE_name, &const_TRANSACTION_ACTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_ACTIVE_name); + + zval const_TRANSACTION_INTRANS_value; + ZVAL_LONG(&const_TRANSACTION_INTRANS_value, PGSQL_TRANSACTION_INTRANS); + zend_string *const_TRANSACTION_INTRANS_name = zend_string_init_interned("TRANSACTION_INTRANS", sizeof("TRANSACTION_INTRANS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INTRANS_name, &const_TRANSACTION_INTRANS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_INTRANS_name); + + zval const_TRANSACTION_INERROR_value; + ZVAL_LONG(&const_TRANSACTION_INERROR_value, PGSQL_TRANSACTION_INERROR); + zend_string *const_TRANSACTION_INERROR_name = zend_string_init_interned("TRANSACTION_INERROR", sizeof("TRANSACTION_INERROR") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INERROR_name, &const_TRANSACTION_INERROR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_INERROR_name); + + zval const_TRANSACTION_UNKNOWN_value; + ZVAL_LONG(&const_TRANSACTION_UNKNOWN_value, PGSQL_TRANSACTION_UNKNOWN); + zend_string *const_TRANSACTION_UNKNOWN_name = zend_string_init_interned("TRANSACTION_UNKNOWN", sizeof("TRANSACTION_UNKNOWN") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_UNKNOWN_name, &const_TRANSACTION_UNKNOWN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_UNKNOWN_name); + + return class_entry; +} diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 8205938a4382..9aa0ad220dab 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -588,8 +588,7 @@ static bool pgsql_handle_rollback(pdo_dbh_t *dbh) return ret; } -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) +void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -699,10 +698,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) RETURN_FALSE; } } -/* }}} */ /* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) +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; @@ -796,11 +800,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) RETURN_FALSE; } } -/* }}} */ - /* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) +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; @@ -891,10 +899,16 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) RETURN_FALSE; } } -/* }}} */ /* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) +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; @@ -966,11 +980,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) 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); +} +/* }}} */ -/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) +void pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -995,10 +1013,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) 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); +} /* }}} */ -/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) +void pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1046,10 +1069,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) 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); +} /* }}} */ -/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) +void pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1081,10 +1109,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) 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); +} /* }}} */ -/* {{{ Get asynchronous notification */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) +void pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1161,10 +1194,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) PQfreemem(pgsql_notify); } + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) +{ + pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} /* }}} */ -/* {{{ Get backend(server) pid */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) +void pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1178,6 +1216,12 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) RETURN_LONG(PQbackendPID(H->server)); } + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) +{ + pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} /* }}} */ static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index f45718f0c96a..303aff600605 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -109,4 +109,14 @@ extern const php_stream_ops pdo_pgsql_lob_stream_ops; 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/ext/pdo_pgsql/tests/pdopgsql_001.phpt b/ext/pdo_pgsql/tests/pdopgsql_001.phpt new file mode 100644 index 000000000000..40c6be5ba764 --- /dev/null +++ b/ext/pdo_pgsql/tests/pdopgsql_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +PdoPgsql subclass basic +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +query('CREATE TABLE IF NOT EXISTS pdopgsql_001 (id INT, name TEXT)'); +$db->query("INSERT INTO pdopgsql_001 VALUES (NULL, 'PHP'), (NULL, 'PHP6')"); + +foreach ($db->query('SELECT name FROM pdopgsql_001') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +query("DROP TABLE IF EXISTS pdopgsql_001"); +?> +--EXPECT-- +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_pgsql/tests/pdopgsql_002.phpt b/ext/pdo_pgsql/tests/pdopgsql_002.phpt new file mode 100644 index 000000000000..a4f676295d63 --- /dev/null +++ b/ext/pdo_pgsql/tests/pdopgsql_002.phpt @@ -0,0 +1,56 @@ +--TEST-- +PdoPgsql connect through PDO::connect +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE IF NOT EXISTS pdopgsql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO pdopgsql_002 VALUES(1, 'A'), (2, 'B'), (3, 'C')"); + +foreach ($db->query('SELECT name FROM pdopgsql_002') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--CLEAN-- +query("DROP TABLE IF EXISTS pdopgsql_002"); +?> +--EXPECT-- +array(2) { + ["name"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["name"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["name"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_pgsql/tests/pdopgsql_003.phpt b/ext/pdo_pgsql/tests/pdopgsql_003.phpt new file mode 100644 index 000000000000..2f7d530f04d0 --- /dev/null +++ b/ext/pdo_pgsql/tests/pdopgsql_003.phpt @@ -0,0 +1,27 @@ +--TEST-- +PdoPgsql getWarningCount +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +escapeIdentifier("This is a quote\""); + +// TODO - ask someone who knows about postgresql if this is correct: +echo "Result is [$result]\n"; +--EXPECT-- +Result is ["This is a quote"""] diff --git a/ext/pdo_sqlite/config.m4 b/ext/pdo_sqlite/config.m4 index 64311eaa6436..2231ff5c6de8 100644 --- a/ext/pdo_sqlite/config.m4 +++ b/ext/pdo_sqlite/config.m4 @@ -26,6 +26,12 @@ if test "$PHP_PDO_SQLITE" != "no"; then AC_DEFINE(HAVE_SQLITE3_COLUMN_TABLE_NAME, 1, [have sqlite3_column_table_name]) ], [], [$PDO_SQLITE_SHARED_LIBADD]) + PHP_CHECK_LIBRARY(sqlite3, sqlite3_load_extension, + [], + [AC_DEFINE(PDO_SQLITE_OMIT_LOAD_EXTENSION, 1, [have sqlite3 with extension support])], + [$PDO_SQLITE_SHARED_LIBADD] + ) + PHP_SUBST(PDO_SQLITE_SHARED_LIBADD) PHP_NEW_EXTENSION(pdo_sqlite, pdo_sqlite.c sqlite_driver.c sqlite_statement.c, $ext_shared,,-I$pdo_cv_inc_path) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 6da770857636..72ff15fb7df2 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -21,32 +21,42 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "SAPI.h" #include "pdo/php_pdo.h" #include "pdo/php_pdo_driver.h" #include "php_pdo_sqlite.h" #include "php_pdo_sqlite_int.h" #include "zend_exceptions.h" +#include "pdo_sqlite_arginfo.h" + +zend_class_entry *pdosqlite_ce; +static pdo_driver_class_entry pdosqlite_pdo_driver_class_entry; + +typedef struct { + zval val; + zend_long row; +} pdopgsql_aggregate_context; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_sqlite_deps[] = { - ZEND_MOD_REQUIRED("pdo") - ZEND_MOD_END + ZEND_MOD_REQUIRED("pdo") + ZEND_MOD_END }; /* }}} */ /* {{{ pdo_sqlite_module_entry */ zend_module_entry pdo_sqlite_module_entry = { - STANDARD_MODULE_HEADER_EX, NULL, - pdo_sqlite_deps, - "pdo_sqlite", - NULL, - PHP_MINIT(pdo_sqlite), - PHP_MSHUTDOWN(pdo_sqlite), - NULL, - NULL, - PHP_MINFO(pdo_sqlite), - PHP_PDO_SQLITE_VERSION, - STANDARD_MODULE_PROPERTIES + STANDARD_MODULE_HEADER_EX, NULL, + pdo_sqlite_deps, + "pdo_sqlite", + NULL, + PHP_MINIT(pdo_sqlite), + PHP_MSHUTDOWN(pdo_sqlite), + NULL, + NULL, + PHP_MINFO(pdo_sqlite), + PHP_PDO_SQLITE_VERSION, + STANDARD_MODULE_PROPERTIES }; /* }}} */ @@ -54,6 +64,611 @@ zend_module_entry pdo_sqlite_module_entry = { ZEND_GET_MODULE(pdo_sqlite) #endif +static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, + int argc, sqlite3_value **argv, sqlite3_context *context, + int is_agg) +{ + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + pdopgsql_aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* 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(pdopgsql_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; + } + } + + fc->fci.params = zargs; + + if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* 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)) { + zval_ptr_dtor(&agg_context->val); + 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; +} + +void php_pgsql_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->afunc, &func->func, argc, argv, context, 0); +} + +static void php_pgsql_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->astep, &func->step, argc, argv, context, 1); +} + +static void php_pgsql_func_final_callback(sqlite3_context *context) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); +} + +/* proto bool PdoSqlite::createFunction(string $function_name, callable $callback, int $num_args = -1, int $flags = 0) + Creates a function that can be used in a query +*/ +PHP_METHOD(PdoSqlite, createFunction) +{ + //copied from sqlite_driver.c + + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + 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_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, + func, php_pgsql_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} + +#ifndef PDO_SQLITE_OMIT_LOAD_EXTENSION +/* Attempts to load an SQLite extension library. */ +PHP_METHOD(PdoSqlite, loadExtension) +{ + char *extension, *errtext = NULL; + char fullpath[MAXPATHLEN]; + size_t extension_len; + + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *db_handle; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &extension, &extension_len) == FAILURE) { + RETURN_THROWS(); + } + + if (extension_len == 0) { + zend_argument_value_error(1, "cannot be empty"); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + db_handle = (pdo_sqlite_db_handle *)dbh->driver_data; + +#ifdef ZTS + if ((strncmp(sapi_module.name, "cgi", 3) != 0) && + (strcmp(sapi_module.name, "cli") != 0) && + (strncmp(sapi_module.name, "embed", 5) != 0) + ) { + // TODO - needs test. + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Not supported in multithreaded Web servers"); + RETURN_THROWS(); + } +#endif + + if (!VCWD_REALPATH(extension, fullpath)) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension \"%s\"", extension); + RETURN_THROWS(); + } + + sqlite3 *sqlite_handle; + sqlite_handle = db_handle->db; + + // This only enables extension loading for the C api, not for SQL + sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); + + if (sqlite3_load_extension(sqlite_handle, fullpath, 0, &errtext) != SQLITE_OK) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension \"%s\"", errtext); + sqlite3_free(errtext); + sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL); + RETURN_THROWS(); + } + + // We disable extension loading for a vague feeling of safety. This is probably not necessary + // as extensions can only be loaded through C code, not through SQL, and if someone can get + // some C code to run on the server, they can do anything. + sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL); +} +#endif + +typedef struct { + sqlite3_blob *blob; + size_t position; + size_t size; + int flags; +} php_stream_pdosqlite3_data; + +static ssize_t php_pdosqlite3_stream_write(php_stream *stream, const char *buf, size_t count) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_stream->flags & SQLITE_OPEN_READONLY) { + php_error_docref(NULL, E_WARNING, "Can't write to blob stream: is open as read only"); + return -1; + } + + if (sqlite3_stream->position + count > sqlite3_stream->size) { + php_error_docref(NULL, E_WARNING, "It is not possible to increase the size of a BLOB"); + return -1; + } + + if (sqlite3_blob_write(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) { + return -1; + } + + if (sqlite3_stream->position + count >= sqlite3_stream->size) { + stream->eof = 1; + sqlite3_stream->position = sqlite3_stream->size; + } + else { + sqlite3_stream->position += count; + } + + return count; +} + +static ssize_t php_pdosqlite3_stream_read(php_stream *stream, char *buf, size_t count) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_stream->position + count >= sqlite3_stream->size) { + count = sqlite3_stream->size - sqlite3_stream->position; + stream->eof = 1; + } + if (count) { + if (sqlite3_blob_read(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) { + return -1; + } + sqlite3_stream->position += count; + } + return count; +} + +static int php_pdosqlite3_stream_close(php_stream *stream, int close_handle) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_blob_close(sqlite3_stream->blob) != SQLITE_OK) { + /* Error occurred, but it still closed */ + } + + efree(sqlite3_stream); + + return 0; +} + +static int php_pdosqlite3_stream_flush(php_stream *stream) +{ + /* do nothing */ + return 0; +} + +static int php_pdosqlite3_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + switch(whence) { + case SEEK_CUR: + if (offset < 0) { + if (sqlite3_stream->position < (size_t)(-offset)) { + sqlite3_stream->position = 0; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->position + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + } else { + if (sqlite3_stream->position + (size_t)(offset) > sqlite3_stream->size) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->position + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + } + case SEEK_SET: + if (sqlite3_stream->size < (size_t)(offset)) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + case SEEK_END: + if (offset > 0) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else if (sqlite3_stream->size < (size_t)(-offset)) { + sqlite3_stream->position = 0; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->size + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + default: + *newoffs = sqlite3_stream->position; + return -1; + } +} + +static int php_pdosqlite3_stream_cast(php_stream *stream, int castas, void **ret) +{ + return FAILURE; +} + +static int php_pdosqlite3_stream_stat(php_stream *stream, php_stream_statbuf *ssb) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + ssb->sb.st_size = sqlite3_stream->size; + return 0; +} + +static const php_stream_ops php_stream_pdosqlite3_ops = { + php_pdosqlite3_stream_write, + php_pdosqlite3_stream_read, + php_pdosqlite3_stream_close, + php_pdosqlite3_stream_flush, + "PDOSQLite", + php_pdosqlite3_stream_seek, + php_pdosqlite3_stream_cast, + php_pdosqlite3_stream_stat, + NULL +}; + +/* Open a blob as a stream which we can read / write to. */ +PHP_METHOD(PdoSqlite, openBlob) +{ + char *table, *column, *dbname = "main", *mode = "rb"; + size_t table_len, column_len, dbname_len; + zend_long rowid, flags = SQLITE_OPEN_READONLY, sqlite_flags = 0; + sqlite3_blob *blob = NULL; + php_stream_pdosqlite3_data *sqlite3_stream; + php_stream *stream; + + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *db_handle; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + db_handle = (pdo_sqlite_db_handle *)dbh->driver_data; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl|pl", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len, &flags) == FAILURE) { + RETURN_THROWS(); + } + + sqlite3 *sqlite_handle; + sqlite_handle = db_handle->db; + sqlite_flags = (flags & SQLITE_OPEN_READWRITE) ? 1 : 0; + + if (sqlite3_blob_open(sqlite_handle, dbname, table, column, rowid, sqlite_flags, &blob) != SQLITE_OK) { + // TODO - exception needed here. +// php_sqlite3_error(db_obj, "Unable to open blob: %s", sqlite3_errmsg(db_obj->db)); + // and converting to exception zend_throw_exception_ex(php_pdo_get_exception(), 0, errtext); + RETURN_FALSE; + } + + sqlite3_stream = emalloc(sizeof(php_stream_pdosqlite3_data)); + sqlite3_stream->blob = blob; + sqlite3_stream->flags = flags; + sqlite3_stream->position = 0; + sqlite3_stream->size = sqlite3_blob_bytes(blob); + + if (sqlite_flags != 0) { + mode = "r+b"; + } + + stream = php_stream_alloc(&php_stream_pdosqlite3_ops, sqlite3_stream, 0, mode); + + if (stream) { + php_stream_to_zval(stream, return_value); + } else { + RETURN_FALSE; + } +} + +static int php_pgsql_collation_callback(void *context, int string1_len, const void *string1, + int string2_len, const void *string2) +{ + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + +PHP_METHOD(PdoSqlite, createAggregate) +{ + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, + func, NULL, php_pgsql_func_step_callback, php_pgsql_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} + +/* bool SQLite::createCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PdoSqlite, createCollation) +{ + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_pgsql_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + ZVAL_COPY(&collation->callback, &fci.function_name); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + efree(collation); + RETURN_FALSE; +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(pdo_sqlite) { @@ -68,6 +683,16 @@ PHP_MINIT_FUNCTION(pdo_sqlite) REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long)PDO_SQLITE_ATTR_READONLY_STATEMENT); REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", (zend_long)PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES); + pdosqlite_ce = register_class_PdoSqlite(pdo_dbh_ce); + pdosqlite_ce->create_object = pdo_dbh_new; + + pdosqlite_pdo_driver_class_entry.driver_name = "sqlite"; + pdosqlite_pdo_driver_class_entry.driver_ce = pdosqlite_ce; + + if (pdo_register_driver_specific_class(&pdosqlite_pdo_driver_class_entry) == FAILURE) { + return FAILURE; + } + return php_pdo_register_driver(&pdo_sqlite_driver); } /* }}} */ diff --git a/ext/pdo_sqlite/pdo_sqlite.stub.php b/ext/pdo_sqlite/pdo_sqlite.stub.php new file mode 100644 index 000000000000..23f7f5dd1a25 --- /dev/null +++ b/ext/pdo_sqlite/pdo_sqlite.stub.php @@ -0,0 +1,67 @@ +ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; +#if defined(SQLITE_DETERMINISTIC) + + zval const_DETERMINISTIC_value; + ZVAL_LONG(&const_DETERMINISTIC_value, SQLITE_DETERMINISTIC); + zend_string *const_DETERMINISTIC_name = zend_string_init_interned("DETERMINISTIC", sizeof("DETERMINISTIC") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_DETERMINISTIC_name, &const_DETERMINISTIC_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_DETERMINISTIC_name); +#endif + + zval const_OPEN_READONLY_value; + ZVAL_LONG(&const_OPEN_READONLY_value, SQLITE_OPEN_READONLY); + zend_string *const_OPEN_READONLY_name = zend_string_init_interned("OPEN_READONLY", sizeof("OPEN_READONLY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_OPEN_READONLY_name, &const_OPEN_READONLY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_OPEN_READONLY_name); + + zval const_OPEN_READWRITE_value; + ZVAL_LONG(&const_OPEN_READWRITE_value, SQLITE_OPEN_READWRITE); + zend_string *const_OPEN_READWRITE_name = zend_string_init_interned("OPEN_READWRITE", sizeof("OPEN_READWRITE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_OPEN_READWRITE_name, &const_OPEN_READWRITE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_OPEN_READWRITE_name); + + zval const_OPEN_CREATE_value; + ZVAL_LONG(&const_OPEN_CREATE_value, SQLITE_OPEN_CREATE); + zend_string *const_OPEN_CREATE_name = zend_string_init_interned("OPEN_CREATE", sizeof("OPEN_CREATE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_OPEN_CREATE_name, &const_OPEN_CREATE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_OPEN_CREATE_name); + + zval const_ATTR_OPEN_FLAGS_value; + ZVAL_LONG(&const_ATTR_OPEN_FLAGS_value, PDO_SQLITE_ATTR_OPEN_FLAGS); + zend_string *const_ATTR_OPEN_FLAGS_name = zend_string_init_interned("ATTR_OPEN_FLAGS", sizeof("ATTR_OPEN_FLAGS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_OPEN_FLAGS_name, &const_ATTR_OPEN_FLAGS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_OPEN_FLAGS_name); + + zval const_ATTR_READONLY_STATEMENT_value; + ZVAL_LONG(&const_ATTR_READONLY_STATEMENT_value, PDO_SQLITE_ATTR_READONLY_STATEMENT); + zend_string *const_ATTR_READONLY_STATEMENT_name = zend_string_init_interned("ATTR_READONLY_STATEMENT", sizeof("ATTR_READONLY_STATEMENT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_READONLY_STATEMENT_name, &const_ATTR_READONLY_STATEMENT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_READONLY_STATEMENT_name); + + zval const_ATTR_EXTENDED_RESULT_CODES_value; + ZVAL_LONG(&const_ATTR_EXTENDED_RESULT_CODES_value, PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES); + zend_string *const_ATTR_EXTENDED_RESULT_CODES_name = zend_string_init_interned("ATTR_EXTENDED_RESULT_CODES", sizeof("ATTR_EXTENDED_RESULT_CODES") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_EXTENDED_RESULT_CODES_name, &const_ATTR_EXTENDED_RESULT_CODES_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_EXTENDED_RESULT_CODES_name); + + return class_entry; +} diff --git a/ext/pdo_sqlite/tests/subclasses/config.inc b/ext/pdo_sqlite/tests/subclasses/config.inc new file mode 100644 index 000000000000..3bc88411b439 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/config.inc @@ -0,0 +1,16 @@ +a = Pdo::connect('sqlite::memory:'); + +if (!$obj->a instanceof PdoSqlite) { + echo "Wrong class type. Should be PdoSqlite but is " . get_class($obj->a) . "]\n"; +} + +$obj->a->createFunction('func1', function() use ($obj) {}, 1); +$obj->a->createAggregate('func2', function() use ($obj) {}, function() use($obj) {}); +$obj->a->createCollation('col', function() use ($obj) {}); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt new file mode 100644 index 000000000000..da48a02c7cfe --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt @@ -0,0 +1,26 @@ +--TEST-- +PDO_sqlite: Testing constants exist +--EXTENSIONS-- +pdo_sqlite +--FILE-- + +--EXPECTF-- +Hello +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt new file mode 100644 index 000000000000..cc4763e4dbd9 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt @@ -0,0 +1,32 @@ +--TEST-- +PDO_sqlite: Testing createAggregate() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +$db->createAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); + + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(2) "12" + [0]=> + string(2) "12" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt new file mode 100644 index 000000000000..e4152c06e81e --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +PDO_sqlite: Testing invalid callback for createAggregate() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +createAggregate('foo', 'a', ''); +} catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + $pdo->createAggregate('foo', 'strlen', ''); +} catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; +} + +?> +--EXPECT-- +PdoSqlite::createAggregate(): Argument #2 ($step) must be a valid callback, function "a" not found or invalid function name +PdoSqlite::createAggregate(): Argument #3 ($finalize) must be a valid callback, function "" not found or invalid function name diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt new file mode 100644 index 000000000000..80344de0a59f --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt @@ -0,0 +1,39 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateCollation() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "1")'); +$db->query('INSERT INTO foobar VALUES (NULL, "2")'); +$db->query('INSERT INTO foobar VALUES (NULL, "10")'); + +$db->createCollation('MYCOLLATE', function($a, $b) { return strnatcmp($a, $b); }); + +$result = $db->query('SELECT name FROM foobar ORDER BY name COLLATE MYCOLLATE'); +foreach ($result as $row) { + echo $row['name'] . "\n"; +} + +$result = $db->query('SELECT name FROM foobar ORDER BY name'); +foreach ($result as $row) { + echo $row['name'] . "\n"; +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +1 +2 +10 +1 +10 +2 diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt new file mode 100644 index 000000000000..d29e79fc903d --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt @@ -0,0 +1,38 @@ +--TEST-- +PDO_sqlite: Testing createFunction() with flags +--EXTENSIONS-- +pdo_sqlite +--SKIPIF-- + +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt new file mode 100644 index 000000000000..512feb33e7cd --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +PdoSqlite basic +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE IF NOT EXISTS pdosqlite_001 (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO pdosqlite_001 VALUES (NULL, "PHP")'); +$db->query('INSERT INTO pdosqlite_001 VALUES (NULL, "PHP6")'); + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + +foreach ($db->query('SELECT testing(name) FROM pdosqlite_001') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE pdosqlite_001'); + +echo "Fin."; +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} +Fin. diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt new file mode 100644 index 000000000000..56098a999534 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt @@ -0,0 +1,38 @@ +--TEST-- +PdoSqlite create through PDO::connect and function define. +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE IF NOT EXISTS pdosqlite_002 (id INT AUTO INCREMENT, name TEXT)'); +$db->query('INSERT INTO pdosqlite_002 VALUES (NULL, "PHP")'); +$db->query('INSERT INTO pdosqlite_002 VALUES (NULL, "PHP6")'); + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + +foreach ($db->query('SELECT testing(name) FROM pdosqlite_002') as $row) { + var_dump($row); +} + +echo "Fin."; +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} +Fin. diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt new file mode 100644 index 000000000000..9a14d892977d --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt @@ -0,0 +1,65 @@ +--TEST-- +PdoSqlite load extension +--EXTENSIONS-- +pdo_sqlite +--SKIPIF-- + +--FILE-- +loadExtension(getSpatialiteExtensionLocation();); +if ($result !== true) { + echo "Failed to load extension mod_spatialite.so"; + exit(-1); +} + +$result = $db->query('SELECT AsText(Buffer(GeomFromText("LINESTRING(0 0, 1 0)"), 0.2)) as geometry_data;'); + +$row = $result->fetch(PDO::FETCH_ASSOC); +if ($row === false) { + echo "Failed to get data from geometry."; + exit(-1); +} + +if (array_key_exists('geometry_data', $row) !== true) { + echo "Data is not under key 'geometry_data'. Available array keys are:"; + var_dump(array_keys($row)); + exit(-1); +} + +echo $row['geometry_data'] . "\n"; + +$row = $result->fetch(PDO::FETCH_ASSOC); + +if ($row !== false) { + echo "We appear to have more data than expected."; + var_dump($row); + exit(-1); +} + +echo "Fin."; +?> +--EXPECT-- +POLYGON((1 0.2, 1.010467 0.199726, 1.020906 0.198904, 1.031287 0.197538, 1.041582 0.19563, 1.051764 0.193185, 1.061803 0.190211, 1.071674 0.186716, 1.081347 0.182709, 1.090798 0.178201, 1.1 0.173205, 1.108928 0.167734, 1.117557 0.161803, 1.125864 0.155429, 1.133826 0.148629, 1.141421 0.141421, 1.148629 0.133826, 1.155429 0.125864, 1.161803 0.117557, 1.167734 0.108928, 1.173205 0.1, 1.178201 0.090798, 1.182709 0.081347, 1.186716 0.071674, 1.190211 0.061803, 1.193185 0.051764, 1.19563 0.041582, 1.197538 0.031287, 1.198904 0.020906, 1.199726 0.010467, 1.2 0, 1.199726 -0.010467, 1.198904 -0.020906, 1.197538 -0.031287, 1.19563 -0.041582, 1.193185 -0.051764, 1.190211 -0.061803, 1.186716 -0.071674, 1.182709 -0.081347, 1.178201 -0.090798, 1.173205 -0.1, 1.167734 -0.108928, 1.161803 -0.117557, 1.155429 -0.125864, 1.148629 -0.133826, 1.141421 -0.141421, 1.133826 -0.148629, 1.125864 -0.155429, 1.117557 -0.161803, 1.108928 -0.167734, 1.1 -0.173205, 1.090798 -0.178201, 1.081347 -0.182709, 1.071674 -0.186716, 1.061803 -0.190211, 1.051764 -0.193185, 1.041582 -0.19563, 1.031287 -0.197538, 1.020906 -0.198904, 1.010467 -0.199726, 1 -0.2, 0 -0.2, -0.010467 -0.199726, -0.020906 -0.198904, -0.031287 -0.197538, -0.041582 -0.19563, -0.051764 -0.193185, -0.061803 -0.190211, -0.071674 -0.186716, -0.081347 -0.182709, -0.090798 -0.178201, -0.1 -0.173205, -0.108928 -0.167734, -0.117557 -0.161803, -0.125864 -0.155429, -0.133826 -0.148629, -0.141421 -0.141421, -0.148629 -0.133826, -0.155429 -0.125864, -0.161803 -0.117557, -0.167734 -0.108928, -0.173205 -0.1, -0.178201 -0.090798, -0.182709 -0.081347, -0.186716 -0.071674, -0.190211 -0.061803, -0.193185 -0.051764, -0.19563 -0.041582, -0.197538 -0.031287, -0.198904 -0.020906, -0.199726 -0.010467, -0.2 0, -0.199726 0.010467, -0.198904 0.020906, -0.197538 0.031287, -0.19563 0.041582, -0.193185 0.051764, -0.190211 0.061803, -0.186716 0.071674, -0.182709 0.081347, -0.178201 0.090798, -0.173205 0.1, -0.167734 0.108928, -0.161803 0.117557, -0.155429 0.125864, -0.148629 0.133826, -0.141421 0.141421, -0.133826 0.148629, -0.125864 0.155429, -0.117557 0.161803, -0.108928 0.167734, -0.1 0.173205, -0.090798 0.178201, -0.081347 0.182709, -0.071674 0.186716, -0.061803 0.190211, -0.051764 0.193185, -0.041582 0.19563, -0.031287 0.197538, -0.020906 0.198904, -0.010467 0.199726, 0 0.2, 1 0.2)) +Fin. diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_004_blobopen.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_004_blobopen.phpt new file mode 100644 index 000000000000..a2b0b7b4fb86 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_004_blobopen.phpt @@ -0,0 +1,77 @@ +--TEST-- +PdoSqlite::blobOpen stream test +--EXTENSIONS-- +pdo_sqlite +--FILE-- +exec('CREATE TABLE test (id STRING, data BLOB)'); + +echo "PREPARING insert\n"; +$insert_stmt = $db->prepare("INSERT INTO test (id, data) VALUES (?, ?)"); + +echo "BINDING Parameter\n"; +var_dump($insert_stmt->bindValue(1, 'a', PDO::PARAM_STR)); +var_dump($insert_stmt->bindValue(2, 'TEST TEST', PDO::PARAM_LOB)); +$insert_stmt->execute(); +echo "\n"; + +$stream = $db->openBlob('test', 'data', 1); +var_dump($stream); +echo "Stream Contents\n"; +var_dump(stream_get_contents($stream)); +echo "Writing to read-only stream\n"; +var_dump(fwrite($stream, 'ABCD')); +echo "Closing Stream\n"; +var_dump(fclose($stream)); +echo "Opening stream in write mode\n"; +$stream = $db->openBlob('test', 'data', 1, 'main', PdoSqlite::OPEN_READWRITE); +var_dump($stream); +echo "Writing to blob\n"; +var_dump(fwrite($stream, 'ABCD')); +echo "Stream Contents\n"; +fseek($stream, 0); +var_dump(stream_get_contents($stream)); +echo "Expanding blob size\n"; +var_dump(fwrite($stream, 'ABCD ABCD ABCD')); +echo "Closing Stream\n"; +var_dump(fclose($stream)); + +echo "Done\n"; +?> +--EXPECTF-- +Creating Table +PREPARING insert +BINDING Parameter +bool(true) +bool(true) + +resource(%d) of type (stream) +Stream Contents +string(9) "TEST TEST" +Writing to read-only stream + +Warning: fwrite(): Can't write to blob stream: is open as read only in %s on line %d +bool(false) +Closing Stream +bool(true) +Opening stream in write mode +resource(%d) of type (stream) +Writing to blob +int(4) +Stream Contents +string(9) "ABCD TEST" +Expanding blob size + +Warning: fwrite(): It is not possible to increase the size of a BLOB in %s on line %d +bool(false) +Closing Stream +bool(true) +Done diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt new file mode 100644 index 000000000000..bf8598bf2949 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt @@ -0,0 +1,17 @@ +--TEST-- +Calling PDO::connect through the wrong classname +--EXTENSIONS-- +pdo_pgsql +pdo_sqlite +--FILE-- +getMessage() . "\n"; +} + +?> +--EXPECT-- +PdoPgsql::connect() cannot be called when connecting to the "sqlite" driver, you must call either PdoSqlite::connect() or PDO::connect() instead diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt new file mode 100644 index 000000000000..1bc8e522dcc7 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt @@ -0,0 +1,16 @@ +--TEST-- +Calling PDO::connect through the wrong classname +--EXTENSIONS-- +pdo_sqlite +--FILE-- + +--EXPECT-- diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt new file mode 100644 index 000000000000..355e4271ea4b --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt @@ -0,0 +1,18 @@ +--TEST-- +Calling PDO::connect through the wrong classname +--EXTENSIONS-- +pdo_sqlite +--FILE-- +getMessage() . "\n"; +} + +?> +--EXPECT-- +MyPDO::connect() cannot be called when connecting to the "sqlite" driver, you must call either PdoSqlite::connect() or PDO::connect() instead diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt new file mode 100644 index 000000000000..462f22cdb67b --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt @@ -0,0 +1,44 @@ +--TEST-- +PdoSqlite load extension +--EXTENSIONS-- +pdo_sqlite +--SKIPIF-- + +--FILE-- +loadExtension("/this/does/not_exist"); + echo "Failed to throw exception"; +} +catch (PDOException $pdoException) { + echo $pdoException->getMessage() . "\n"; +} + +try { + echo "Loading invalid file.\n"; + $result = $db->loadExtension(__FILE__); + echo "Failed to throw exception"; +} +catch (PDOException $pdoException) { + echo $pdoException->getMessage() . "\n"; +} + +echo "Fin."; +?> +--EXPECTF-- +Loading non-existent file. +Unable to load extension "/this/does/not_exist" +Loading invalid file. +Unable to load extension "%a" +Fin. diff --git a/ext/pdo_sqlite/tests/subclasses/stream_test.inc b/ext/pdo_sqlite/tests/subclasses/stream_test.inc new file mode 100644 index 000000000000..d4bee82fbc49 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/stream_test.inc @@ -0,0 +1,46 @@ +position = 0; + return true; + } + + public function stream_read($count) + { + $ret = substr(self::$string, $this->position, $count); + $this->position += strlen($ret); + return $ret; + } + + public function stream_write($data) + { + return 0; + } + + public function stream_stat() + { + return array('size' => self::$string_length); + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return ($this->position >= self::$string_length); + } +} + +stream_wrapper_register('pdosqliteBlobTest', PDOSQLite_Test_Stream::class) or die("Unable to register pdosqliteBlobTest stream"); From 714e091cebd80cf579e297199c55b084b68a5824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 14 Dec 2023 10:11:55 +0100 Subject: [PATCH 02/11] Address code review comments --- ext/pdo/pdo_dbh.c | 6 ++-- ext/pdo_dblib/tests/pdodblib_001.phpt | 4 +-- ext/pdo_dblib/tests/pdodblib_002.phpt | 4 +-- ext/pdo_mysql/tests/pdomysql_001.phpt | 2 +- ext/pdo_pgsql/pdo_pgsql.c | 2 +- ext/pdo_pgsql/tests/pdopgsql_001.phpt | 6 ++-- ext/pdo_pgsql/tests/pdopgsql_002.phpt | 6 ++-- ext/pdo_pgsql/tests/pdopgsql_003.phpt | 2 +- ext/pdo_sqlite/pdo_sqlite.c | 28 +++++++++---------- .../tests/pdo_sqlite_createaggregate.phpt | 8 ++---- .../tests/pdo_sqlite_createcollation.phpt | 10 +++---- .../tests/pdo_sqlite_createfunction.phpt | 8 ++---- .../pdo_sqlite_createfunction_with_flags.phpt | 8 ++---- .../tests/pdo_sqlite_lastinsertid.phpt | 10 +++---- .../tests/pdo_sqlite_transaction.phpt | 10 ++----- .../pdo_sqlite_createaggregate.phpt | 2 +- .../tests/subclasses/pdosqlite_001.phpt | 2 +- .../tests/subclasses/pdosqlite_002.phpt | 2 +- .../tests/subclasses/pdosqlite_005.phpt | 2 +- .../tests/subclasses/pdosqlite_006.phpt | 3 +- .../tests/subclasses/pdosqlite_007.phpt | 2 +- ext/pgsql/pgsql.c | 2 +- 22 files changed, 58 insertions(+), 71 deletions(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index d8e8c6f2e6d4..29933ad6ace1 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -260,7 +260,7 @@ static bool create_driver_specific_pdo_object(const char *driver_name, zend_clas if (instanceof_function(ce_based_on_called_object, ce_based_on_driver_name) == false) { zend_throw_exception_ex(php_pdo_get_exception(), 0, "%s::connect() cannot be called when connecting to the \"%s\" driver, " - "you must call either %s::connect() or PDO::connect() instead", + "either %s::connect() or PDO::connect() must be called instead", ZSTR_VAL(called_scope->name), driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); return false; } @@ -270,7 +270,7 @@ static bool create_driver_specific_pdo_object(const char *driver_name, zend_clas } else { zend_throw_exception_ex(php_pdo_get_exception(), 0, "%s::connect() cannot be called when connecting to an unknown driver, " - "you must call PDO::connect() instead", + "PDO::connect() must be called instead", ZSTR_VAL(called_scope->name)); return false; } @@ -280,7 +280,7 @@ static bool create_driver_specific_pdo_object(const char *driver_name, zend_clas if (called_scope != pdo_dbh_ce) { zend_throw_exception_ex(php_pdo_get_exception(), 0, "%s::connect() cannot be called when connecting to the \"%s\" driver, " - "you must call either %s::connect() or PDO::connect() instead", + "either %s::connect() or PDO::connect() must be called instead", ZSTR_VAL(called_scope->name), driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); return false; } diff --git a/ext/pdo_dblib/tests/pdodblib_001.phpt b/ext/pdo_dblib/tests/pdodblib_001.phpt index 06b91b933651..f4e98fb8ec7e 100644 --- a/ext/pdo_dblib/tests/pdodblib_001.phpt +++ b/ext/pdo_dblib/tests/pdodblib_001.phpt @@ -13,8 +13,8 @@ getDbConnection(); require __DIR__ . '/config.inc'; $db = getDbConnection(PdoDblib::class); -$db->query("create table #pdo_dblib_001(name varchar(32)); "); -$db->query("insert into #pdo_dblib_001 values('PHP'), ('PHP6');"); +$db->query("CREATE TABLE #pdo_dblib_001(name varchar(32)); "); +$db->query("INSERT INTO #pdo_dblib_001 values('PHP'), ('PHP6');"); foreach ($db->query('SELECT name FROM #pdo_dblib_001') as $row) { var_dump($row); diff --git a/ext/pdo_dblib/tests/pdodblib_002.phpt b/ext/pdo_dblib/tests/pdodblib_002.phpt index 825abfaeb7d8..fd3d2bbb9423 100644 --- a/ext/pdo_dblib/tests/pdodblib_002.phpt +++ b/ext/pdo_dblib/tests/pdodblib_002.phpt @@ -17,8 +17,8 @@ if (!$db instanceof PdoDblib) { echo "Wrong class type. Should be PdoDblib but is " . get_class($db) . "\n"; } -$db->query("create table #pdo_dblib_002(name varchar(32))"); -$db->query("insert into #pdo_dblib_002 values('PHP'), ('PHP6')"); +$db->query("CREATE TABLE #pdo_dblib_002(name varchar(32))"); +$db->query("INSERT INTO #pdo_dblib_002 values('PHP'), ('PHP6')"); foreach ($db->query('SELECT name FROM #pdo_dblib_002') as $row) { var_dump($row); diff --git a/ext/pdo_mysql/tests/pdomysql_001.phpt b/ext/pdo_mysql/tests/pdomysql_001.phpt index 37a564403198..8f87760e5d42 100644 --- a/ext/pdo_mysql/tests/pdomysql_001.phpt +++ b/ext/pdo_mysql/tests/pdomysql_001.phpt @@ -14,7 +14,7 @@ require_once __DIR__ . '/inc/mysql_pdo_test.inc'; $db = MySQLPDOTest::factory(PdoMysql::class); -$db->query('CREATE TABLE IF NOT EXISTS pdomysql_001 (id INT, name TEXT)'); +$db->query('CREATE TABLE pdomysql_001 (id INT, name TEXT)'); $db->query('INSERT INTO pdomysql_001 VALUES (NULL, "PHP")'); $db->query('INSERT INTO pdomysql_001 VALUES (NULL, "PHP6")'); diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 72c6106cdfa5..fdf3ebcd9820 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -80,7 +80,7 @@ PHP_METHOD(PdoPgsql, escapeIdentifier) tmp = PQescapeIdentifier(H->server, ZSTR_VAL(from), ZSTR_LEN(from)); if (!tmp) { // TODO - exception - php_error_docref(NULL, E_WARNING,"Failed to escape identifier"); + php_error_docref(NULL, E_WARNING, "Failed to escape identifier"); RETURN_FALSE; } diff --git a/ext/pdo_pgsql/tests/pdopgsql_001.phpt b/ext/pdo_pgsql/tests/pdopgsql_001.phpt index 40c6be5ba764..524b4ad093e0 100644 --- a/ext/pdo_pgsql/tests/pdopgsql_001.phpt +++ b/ext/pdo_pgsql/tests/pdopgsql_001.phpt @@ -6,7 +6,7 @@ pdo_pgsql --SKIPIF-- --FILE-- @@ -16,7 +16,7 @@ require_once __DIR__ . "/config.inc"; $db = new PdoPgsql($config['ENV']['PDOTEST_DSN']); -$db->query('CREATE TABLE IF NOT EXISTS pdopgsql_001 (id INT, name TEXT)'); +$db->query('CREATE TABLE pdopgsql_001 (id INT, name TEXT)'); $db->query("INSERT INTO pdopgsql_001 VALUES (NULL, 'PHP'), (NULL, 'PHP6')"); foreach ($db->query('SELECT name FROM pdopgsql_001') as $row) { @@ -27,7 +27,7 @@ echo "Fin."; ?> --CLEAN-- query("DROP TABLE IF EXISTS pdopgsql_001"); ?> diff --git a/ext/pdo_pgsql/tests/pdopgsql_002.phpt b/ext/pdo_pgsql/tests/pdopgsql_002.phpt index a4f676295d63..24f198f87348 100644 --- a/ext/pdo_pgsql/tests/pdopgsql_002.phpt +++ b/ext/pdo_pgsql/tests/pdopgsql_002.phpt @@ -6,7 +6,7 @@ pdo_pgsql --SKIPIF-- --FILE-- @@ -19,7 +19,7 @@ if (!$db instanceof PdoPgsql) { echo "Wrong class type. Should be PdoPgsql but is " . get_class($db) . "\n"; } -$db->exec('CREATE TABLE IF NOT EXISTS pdopgsql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec('CREATE TABLE pdopgsql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); $db->exec("INSERT INTO pdopgsql_002 VALUES(1, 'A'), (2, 'B'), (3, 'C')"); foreach ($db->query('SELECT name FROM pdopgsql_002') as $row) { @@ -30,7 +30,7 @@ echo "Fin."; ?> --CLEAN-- query("DROP TABLE IF EXISTS pdopgsql_002"); ?> diff --git a/ext/pdo_pgsql/tests/pdopgsql_003.phpt b/ext/pdo_pgsql/tests/pdopgsql_003.phpt index 2f7d530f04d0..5b86fe1c54b9 100644 --- a/ext/pdo_pgsql/tests/pdopgsql_003.phpt +++ b/ext/pdo_pgsql/tests/pdopgsql_003.phpt @@ -6,7 +6,7 @@ pdo_pgsql --SKIPIF-- --FILE-- diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 72ff15fb7df2..2dea45c8725a 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -39,24 +39,24 @@ typedef struct { /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_sqlite_deps[] = { - ZEND_MOD_REQUIRED("pdo") - ZEND_MOD_END + ZEND_MOD_REQUIRED("pdo") + ZEND_MOD_END }; /* }}} */ /* {{{ pdo_sqlite_module_entry */ zend_module_entry pdo_sqlite_module_entry = { - STANDARD_MODULE_HEADER_EX, NULL, - pdo_sqlite_deps, - "pdo_sqlite", - NULL, - PHP_MINIT(pdo_sqlite), - PHP_MSHUTDOWN(pdo_sqlite), - NULL, - NULL, - PHP_MINFO(pdo_sqlite), - PHP_PDO_SQLITE_VERSION, - STANDARD_MODULE_PROPERTIES + STANDARD_MODULE_HEADER_EX, NULL, + pdo_sqlite_deps, + "pdo_sqlite", + NULL, + PHP_MINIT(pdo_sqlite), + PHP_MSHUTDOWN(pdo_sqlite), + NULL, + NULL, + PHP_MINFO(pdo_sqlite), + PHP_PDO_SQLITE_VERSION, + STANDARD_MODULE_PROPERTIES }; /* }}} */ @@ -508,7 +508,7 @@ PHP_METHOD(PdoSqlite, openBlob) PDO_CONSTRUCT_CHECK; db_handle = (pdo_sqlite_db_handle *)dbh->driver_data; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl|pl", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len, &flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppl|pl", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len, &flags) == FAILURE) { RETURN_THROWS(); } diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_createaggregate.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_createaggregate.phpt index 98240b5943fb..3ecc0ccddc99 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_createaggregate.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_createaggregate.phpt @@ -7,19 +7,17 @@ pdo_sqlite $db = new PDO('sqlite::memory:'); -$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE test_pdo_sqlite_createaggregate (id INT AUTO INCREMENT, name TEXT)'); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP"), (NULL, "PHP6")'); +$db->query('INSERT INTO test_pdo_sqlite_createaggregate VALUES (NULL, "PHP"), (NULL, "PHP6")'); $db->sqliteCreateAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); -foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { +foreach ($db->query('SELECT testing(name) FROM test_pdo_sqlite_createaggregate') as $row) { var_dump($row); } -$db->query('DROP TABLE foobar'); - ?> --EXPECT-- array(2) { diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt index bf1cac2a49bd..9e4751e33aa0 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt @@ -8,24 +8,22 @@ pdo_sqlite $db = new PDO('sqlite::memory:'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE test_pdo_sqlite_createcollation (id INT AUTO INCREMENT, name TEXT)'); -$db->query('INSERT INTO foobar VALUES (NULL, "1"), (NULL, "2"), (NULL, "10")'); +$db->query('INSERT INTO test_pdo_sqlite_createcollation VALUES (NULL, "1"), (NULL, "2"), (NULL, "10")'); $db->sqliteCreateCollation('MYCOLLATE', function($a, $b) { return strnatcmp($a, $b); }); -$result = $db->query('SELECT name FROM foobar ORDER BY name COLLATE MYCOLLATE'); +$result = $db->query('SELECT name FROM test_pdo_sqlite_createcollation ORDER BY name COLLATE MYCOLLATE'); foreach ($result as $row) { echo $row['name'] . "\n"; } -$result = $db->query('SELECT name FROM foobar ORDER BY name'); +$result = $db->query('SELECT name FROM test_pdo_sqlite_createcollation ORDER BY name'); foreach ($result as $row) { echo $row['name'] . "\n"; } -$db->query('DROP TABLE foobar'); - ?> --EXPECT-- 1 diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_createfunction.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_createfunction.phpt index 9ea8edc8ad7f..bda7ab0b4155 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_createfunction.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_createfunction.phpt @@ -7,20 +7,18 @@ pdo_sqlite $db = new PDO('sqlite::memory:'); -$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE test_pdo_sqlite_createfunction (id INT AUTO INCREMENT, name TEXT)'); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP"), (NULL, "PHP6")'); +$db->query('INSERT INTO test_pdo_sqlite_createfunction VALUES (NULL, "PHP"), (NULL, "PHP6")'); $db->sqliteCreateFunction('testing', function($v) { return strtolower($v); }); -foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { +foreach ($db->query('SELECT testing(name) FROM test_pdo_sqlite_createfunction') as $row) { var_dump($row); } -$db->query('DROP TABLE foobar'); - ?> --EXPECT-- array(2) { diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt index bc3def2bf68b..f9312bcee69e 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt @@ -11,20 +11,18 @@ if (!defined('PDO::SQLITE_DETERMINISTIC')) die('skip system sqlite is too old'); $db = new PDO('sqlite::memory:'); -$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE test_pdo_sqlite_createfunction_with_flags (id INT AUTO INCREMENT, name TEXT)'); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP"), (NULL, "PHP6")'); +$db->query('INSERT INTO test_pdo_sqlite_createfunction_with_flags VALUES (NULL, "PHP"), (NULL, "PHP6")'); $db->sqliteCreateFunction('testing', function($v) { return strtolower($v); }, 1, PDO::SQLITE_DETERMINISTIC); -foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { +foreach ($db->query('SELECT testing(name) FROM test_pdo_sqlite_createfunction_with_flags') as $row) { var_dump($row); } -$db->query('DROP TABLE foobar'); - ?> --EXPECT-- array(2) { diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_lastinsertid.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_lastinsertid.phpt index 2237e36722d1..7e55ed60d5f1 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_lastinsertid.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_lastinsertid.phpt @@ -6,19 +6,17 @@ pdo_sqlite query('CREATE TABLE IF NOT EXISTS foo (id INT AUTO INCREMENT, name TEXT)'); -$db->query('INSERT INTO foo VALUES (NULL, "PHP"), (NULL, "PHP6")'); -var_dump($db->query('SELECT * FROM foo')); +$db->query('CREATE TABLE test_pdo_sqlite_lastinsertid (id INT AUTO INCREMENT, name TEXT)'); +$db->query('INSERT INTO test_pdo_sqlite_lastinsertid VALUES (NULL, "PHP"), (NULL, "PHP6")'); +var_dump($db->query('SELECT * FROM test_pdo_sqlite_lastinsertid')); var_dump($db->errorInfo()); var_dump($db->lastInsertId()); -$db->query('DROP TABLE foo'); - ?> --EXPECT-- object(PDOStatement)#2 (1) { ["queryString"]=> - string(17) "SELECT * FROM foo" + string(42) "SELECT * FROM test_pdo_sqlite_lastinsertid" } array(3) { [0]=> diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt index 26e6cd6bd370..2dba9eeb477a 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt @@ -10,20 +10,16 @@ $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $db->beginTransaction(); -$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE test_pdo_sqlite_transaction (id INT AUTO INCREMENT, name TEXT)'); $db->commit(); $db->beginTransaction(); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); +$db->query('INSERT INTO test_pdo_sqlite_transaction VALUES (NULL, "PHP"), (NULL, "PHP6")'); $db->rollback(); -$r = $db->query('SELECT COUNT(*) FROM foobar'); +$r = $db->query('SELECT COUNT(*) FROM test_pdo_sqlite_transaction'); var_dump($r->rowCount()); - -$db->query('DROP TABLE foobar'); - ?> --EXPECTF-- int(0) diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt index cc4763e4dbd9..ef1f34255467 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt @@ -8,7 +8,7 @@ pdo_sqlite // This test was copied from the pdo_sqlite test for sqliteCreateAggregate $db = new PdoSqlite('sqlite::memory:'); -$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE foobar (id INT AUTO INCREMENT, name TEXT)'); $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt index 512feb33e7cd..770fa8e4552c 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt @@ -7,7 +7,7 @@ pdo_sqlite $db = new PdoSqlite('sqlite::memory:'); -$db->query('CREATE TABLE IF NOT EXISTS pdosqlite_001 (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE pdosqlite_001 (id INT AUTO INCREMENT, name TEXT)'); $db->query('INSERT INTO pdosqlite_001 VALUES (NULL, "PHP")'); $db->query('INSERT INTO pdosqlite_001 VALUES (NULL, "PHP6")'); diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt index 56098a999534..61c4dd6d3183 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt @@ -10,7 +10,7 @@ if (!$db instanceof PdoSqlite) { echo "Wrong class type. Should be PdoSqlite but is " . get_class($db) . "\n"; } -$db->query('CREATE TABLE IF NOT EXISTS pdosqlite_002 (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE pdosqlite_002 (id INT AUTO INCREMENT, name TEXT)'); $db->query('INSERT INTO pdosqlite_002 VALUES (NULL, "PHP")'); $db->query('INSERT INTO pdosqlite_002 VALUES (NULL, "PHP6")'); diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt index bf8598bf2949..29cfc2e17503 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt @@ -14,4 +14,4 @@ try { ?> --EXPECT-- -PdoPgsql::connect() cannot be called when connecting to the "sqlite" driver, you must call either PdoSqlite::connect() or PDO::connect() instead +PdoPgsql::connect() cannot be called when connecting to the "sqlite" driver, either PdoSqlite::connect() or PDO::connect() must be called instead diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt index 1bc8e522dcc7..79626edc6a19 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt @@ -11,6 +11,7 @@ $db = MyPdoSqlite::connect('sqlite::memory:'); if (!$db instanceof MyPdoSqlite) { echo "Wrong class type. Should be MyPdoSqlite but is " .get_class($db) . "\n"; } - +echo "OK"; ?> --EXPECT-- +OK diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt index 355e4271ea4b..899b6fb41284 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt @@ -15,4 +15,4 @@ try { ?> --EXPECT-- -MyPDO::connect() cannot be called when connecting to the "sqlite" driver, you must call either PdoSqlite::connect() or PDO::connect() instead +MyPDO::connect() cannot be called when connecting to the "sqlite" driver, either PdoSqlite::connect() or PDO::connect() must be called instead diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index fba4d70e9a5b..c393f3c4a180 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3361,7 +3361,7 @@ static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_l tmp = PQescapeIdentifier(pgsql, ZSTR_VAL(from), ZSTR_LEN(from)); } if (!tmp) { - php_error_docref(NULL, E_WARNING,"Failed to escape"); + php_error_docref(NULL, E_WARNING, "Failed to escape"); RETURN_FALSE; } From 204dcda94f3485bd7e029a8d8e02f88912602e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Fri, 22 Dec 2023 14:49:05 +0100 Subject: [PATCH 03/11] Refactor driver specific ce registration + fix tests --- ext/pdo/pdo.c | 17 ++++++++ ext/pdo/pdo_dbh.c | 40 +++++-------------- ext/pdo/php_pdo_driver.h | 15 +++---- ext/pdo/php_pdo_int.h | 1 + ext/pdo_dblib/pdo_dblib.c | 7 +--- ext/pdo_firebird/pdo_firebird.c | 7 +--- ext/pdo_firebird/tests/pdofirebird_001.phpt | 2 +- ext/pdo_firebird/tests/pdofirebird_002.phpt | 2 +- ext/pdo_mysql/pdo_mysql.c | 13 +++--- ext/pdo_mysql/tests/inc/mysql_pdo_test.inc | 27 ------------- ext/pdo_mysql/tests/pdomysql_002.phpt | 2 +- ext/pdo_odbc/pdo_odbc.c | 15 ++----- ext/pdo_pgsql/pdo_pgsql.c | 7 +--- ext/pdo_sqlite/pdo_sqlite.c | 24 ++++++----- .../tests/pdo_sqlite_transaction.phpt | 2 + 15 files changed, 65 insertions(+), 116 deletions(-) diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index d2a4741139c8..450c65ae60dc 100644 --- a/ext/pdo/pdo.c +++ b/ext/pdo/pdo.c @@ -41,6 +41,9 @@ zend_class_entry *pdo_exception_ce; /* the registry of PDO drivers */ HashTable pdo_driver_hash; +/* the registry of PDO driver specific class entries */ +HashTable pdo_driver_specific_ce_hash; + /* we use persistent resources for the driver connection stuff */ static int le_ppdo; @@ -129,10 +132,22 @@ PDO_API void php_pdo_unregister_driver(const pdo_driver_t *driver) /* {{{ */ return; } + zend_hash_str_del(&pdo_driver_specific_ce_hash, driver->driver_name, driver->driver_name_len); zend_hash_str_del(&pdo_driver_hash, driver->driver_name, driver->driver_name_len); } /* }}} */ +PDO_API zend_result php_pdo_register_driver_specific_ce(const pdo_driver_t *driver, zend_class_entry *ce) /* {{{ */ +{ + if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) { + zend_error_noreturn(E_ERROR, "You MUST load PDO before loading any PDO drivers"); + return FAILURE; /* NOTREACHED */ + } + + return zend_hash_str_add_ptr(&pdo_driver_specific_ce_hash, driver->driver_name, + driver->driver_name_len, (void*)ce) != NULL ? SUCCESS : FAILURE; +} + pdo_driver_t *pdo_find_driver(const char *name, int namelen) /* {{{ */ { return zend_hash_str_find_ptr(&pdo_driver_hash, name, namelen); @@ -246,6 +261,7 @@ PHP_MINIT_FUNCTION(pdo) pdo_sqlstate_init_error_table(); zend_hash_init(&pdo_driver_hash, 0, NULL, NULL, 1); + zend_hash_init(&pdo_driver_specific_ce_hash, 0, NULL, NULL, 1); le_ppdo = zend_register_list_destructors_ex(NULL, php_pdo_pdbh_dtor, "PDO persistent database", module_number); @@ -263,6 +279,7 @@ PHP_MINIT_FUNCTION(pdo) PHP_MSHUTDOWN_FUNCTION(pdo) { zend_hash_destroy(&pdo_driver_hash); + zend_hash_destroy(&pdo_driver_specific_ce_hash); pdo_sqlstate_fini_error_table(); return SUCCESS; } diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 29933ad6ace1..bf8e027f4e3c 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -221,39 +221,19 @@ static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */ } /* }}} */ -#define MAX_PDO_SUB_CLASSES 64 -static unsigned int number_of_pdo_driver_class_entries = 0; -static pdo_driver_class_entry *pdo_driver_class_entries[MAX_PDO_SUB_CLASSES]; - -// It would be possible remove this and roll it into the standard driver class entries -// I chose not to do it at this time, as that would break existing PDO extensions -zend_result pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry) -{ - if (number_of_pdo_driver_class_entries >= MAX_PDO_SUB_CLASSES) { - return FAILURE; - } - - pdo_driver_class_entries[number_of_pdo_driver_class_entries] = driver_class_entry; - number_of_pdo_driver_class_entries += 1; - - return SUCCESS; -} - -static bool create_driver_specific_pdo_object(const char *driver_name, zend_class_entry *called_scope, zval *new_object) +static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_entry *called_scope, zval *new_object) { + zend_class_entry *ce; zend_class_entry *ce_based_on_driver_name = NULL, *ce_based_on_called_object = NULL; - for (int i = 0; i < number_of_pdo_driver_class_entries && (ce_based_on_driver_name == NULL || ce_based_on_called_object == NULL); i++) { - pdo_driver_class_entry *driver_class_entry = pdo_driver_class_entries[i]; - - if (strcmp(driver_class_entry->driver_name, driver_name) == 0) { - ce_based_on_driver_name = driver_class_entry->driver_ce; - } + ce_based_on_driver_name = zend_hash_str_find_ptr(&pdo_driver_specific_ce_hash, driver->driver_name, driver->driver_name_len); - if (called_scope != pdo_dbh_ce && instanceof_function(called_scope, driver_class_entry->driver_ce)) { + ZEND_HASH_MAP_FOREACH_PTR(&pdo_driver_specific_ce_hash, ce) { + if (called_scope != pdo_dbh_ce && instanceof_function(called_scope, ce)) { ce_based_on_called_object = called_scope; + break; } - } + } ZEND_HASH_FOREACH_END(); if (ce_based_on_called_object) { if (ce_based_on_driver_name) { @@ -261,7 +241,7 @@ static bool create_driver_specific_pdo_object(const char *driver_name, zend_clas zend_throw_exception_ex(php_pdo_get_exception(), 0, "%s::connect() cannot be called when connecting to the \"%s\" driver, " "either %s::connect() or PDO::connect() must be called instead", - ZSTR_VAL(called_scope->name), driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); + ZSTR_VAL(called_scope->name), driver->driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); return false; } @@ -281,7 +261,7 @@ static bool create_driver_specific_pdo_object(const char *driver_name, zend_clas zend_throw_exception_ex(php_pdo_get_exception(), 0, "%s::connect() cannot be called when connecting to the \"%s\" driver, " "either %s::connect() or PDO::connect() must be called instead", - ZSTR_VAL(called_scope->name), driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); + ZSTR_VAL(called_scope->name), driver->driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); return false; } @@ -365,7 +345,7 @@ static void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zend_object *object if (new_zval_object != NULL) { ZEND_ASSERT((driver->driver_name != NULL) && "PDO driver name is null"); - bool result = create_driver_specific_pdo_object(driver->driver_name, current_scope, new_zval_object); + bool result = create_driver_specific_pdo_object(driver, current_scope, new_zval_object); if (!result) { RETURN_THROWS(); } diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index 71c4ff989ccc..262a908f9764 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -215,16 +215,6 @@ typedef struct { } pdo_driver_t; -// NOTE - This separate struct, could be rolled it into pdo_driver_t -// I chose not to, as that would cause BC break and require a lot of -// downstream work. -typedef struct { - char *driver_name; - zend_class_entry *driver_ce; -} pdo_driver_class_entry; - -PHPAPI zend_result pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry); - /* {{{ methods for a database handle */ /* close or otherwise disconnect the database */ @@ -663,6 +653,11 @@ PDO_API zend_result php_pdo_register_driver(const pdo_driver_t *driver); /* call this in MSHUTDOWN to unregister your PDO driver */ PDO_API void php_pdo_unregister_driver(const pdo_driver_t *driver); +/* Call this in MINIT to register the PDO driver specific class entry. + * Registering the driver specific class entry might fail and should be reported accordingly in MINIT. + * Unregistering the class entry is not necessary, since php_pdo_unregister_driver() takes care of it. */ +PDO_API zend_result php_pdo_register_driver_specific_ce(const pdo_driver_t *driver, zend_class_entry *ce); + /* For the convenience of drivers, this function will parse a data source * string, of the form "name=value; name2=value2" and populate variables * according to the data you pass in and array of pdo_data_src_parser structures */ diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index a276a7972e74..13c2e3d9431b 100644 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -22,6 +22,7 @@ #include "php_pdo_error.h" extern HashTable pdo_driver_hash; +extern HashTable pdo_driver_specific_ce_hash; extern zend_class_entry *pdo_exception_ce; int php_pdo_list_entry(void); diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index 04d504b0b8ac..149a8824bf08 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -33,7 +33,6 @@ ZEND_DECLARE_MODULE_GLOBALS(dblib) static PHP_GINIT_FUNCTION(dblib); zend_class_entry *PdoDblib_ce; -static pdo_driver_class_entry PdoDblib_pdo_driver_class_entry; static const zend_module_dep pdo_dblib_deps[] = { ZEND_MOD_REQUIRED("pdo") @@ -208,10 +207,6 @@ PHP_MINIT_FUNCTION(pdo_dblib) PdoDblib_ce = register_class_PdoDblib(pdo_dbh_ce); PdoDblib_ce->create_object = pdo_dbh_new; - PdoDblib_pdo_driver_class_entry.driver_name = "dblib"; - PdoDblib_pdo_driver_class_entry.driver_ce = PdoDblib_ce; - pdo_register_driver_specific_class(&PdoDblib_pdo_driver_class_entry); - if (FAILURE == php_pdo_register_driver(&pdo_dblib_driver)) { return FAILURE; } @@ -221,7 +216,7 @@ PHP_MINIT_FUNCTION(pdo_dblib) dbmsghandle((MHANDLEFUNC) pdo_dblib_msg_handler); #endif - return SUCCESS; + return php_pdo_register_driver_specific_ce(&pdo_dblib_driver, PdoDblib_ce); } PHP_MSHUTDOWN_FUNCTION(pdo_dblib) diff --git a/ext/pdo_firebird/pdo_firebird.c b/ext/pdo_firebird/pdo_firebird.c index b1bc0cd7c688..3c2045f6fa79 100644 --- a/ext/pdo_firebird/pdo_firebird.c +++ b/ext/pdo_firebird/pdo_firebird.c @@ -28,7 +28,6 @@ #include "pdo_firebird_arginfo.h" zend_class_entry *PdoFirebird_ce; -static pdo_driver_class_entry PdoFirebird_pdo_driver_class_entry; /* {{{ pdo_firebird_deps */ static const zend_module_dep pdo_firebird_deps[] = { @@ -74,16 +73,12 @@ PHP_MINIT_FUNCTION(pdo_firebird) /* {{{ */ PdoFirebird_ce = register_class_PdoFirebird(pdo_dbh_ce); PdoFirebird_ce->create_object = pdo_dbh_new; - PdoFirebird_pdo_driver_class_entry.driver_name = "firebird"; - PdoFirebird_pdo_driver_class_entry.driver_ce = PdoFirebird_ce; - pdo_register_driver_specific_class(&PdoFirebird_pdo_driver_class_entry); - #ifdef ZEND_SIGNALS /* firebird replaces some signals at runtime, suppress warnings. */ SIGG(check) = 0; #endif - return SUCCESS; + return php_pdo_register_driver_specific_ce(&pdo_firebird_driver, PdoFirebird_ce); } /* }}} */ diff --git a/ext/pdo_firebird/tests/pdofirebird_001.phpt b/ext/pdo_firebird/tests/pdofirebird_001.phpt index d34bfa1943f9..10bdd3e45b68 100644 --- a/ext/pdo_firebird/tests/pdofirebird_001.phpt +++ b/ext/pdo_firebird/tests/pdofirebird_001.phpt @@ -11,7 +11,7 @@ require_once __DIR__ . "/testdb.inc"; $db = getDbConnection(); -$db->query('RECREATE TABLE pdofirebird_001 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); +$db->query('CREATE TABLE pdofirebird_001 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); $db->query("INSERT INTO pdofirebird_001 VALUES (1, 'PHP')"); $db->query("INSERT INTO pdofirebird_001 VALUES (2, 'PHP6')"); diff --git a/ext/pdo_firebird/tests/pdofirebird_002.phpt b/ext/pdo_firebird/tests/pdofirebird_002.phpt index cf3836ec8b43..ea48263bb57f 100644 --- a/ext/pdo_firebird/tests/pdofirebird_002.phpt +++ b/ext/pdo_firebird/tests/pdofirebird_002.phpt @@ -14,7 +14,7 @@ if (!$db instanceof PdoFirebird) { echo "Wrong class type. Should be PdoOdbc but is " . get_class($db) . "\n"; } -$db->query('RECREATE TABLE pdofirebird_002 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); +$db->query('CREATE TABLE pdofirebird_002 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); $db->exec("INSERT INTO pdofirebird_002 VALUES(1, 'A')"); $db->exec("INSERT INTO pdofirebird_002 VALUES(2, 'B')"); diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c index 4768a861fe12..d7f7dbaf6c5a 100644 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -28,8 +28,7 @@ #include "php_pdo_mysql_int.h" #include "pdo_mysql_arginfo.h" -zend_class_entry *pdomysql_ce; -static pdo_driver_class_entry pdomysql_pdo_driver_class_entry; +zend_class_entry *pdo_mysql_ce; #ifdef COMPILE_DL_PDO_MYSQL #ifdef ZTS @@ -150,16 +149,14 @@ static PHP_MINIT_FUNCTION(pdo_mysql) mysqlnd_reverse_api_register_api(&pdo_mysql_reverse_api); #endif - pdomysql_ce = register_class_PdoMysql(pdo_dbh_ce); - pdomysql_ce->create_object = pdo_dbh_new; + pdo_mysql_ce = register_class_PdoMysql(pdo_dbh_ce); + pdo_mysql_ce->create_object = pdo_dbh_new; - pdomysql_pdo_driver_class_entry.driver_name = "mysql"; - pdomysql_pdo_driver_class_entry.driver_ce = pdomysql_ce; - if (pdo_register_driver_specific_class(&pdomysql_pdo_driver_class_entry) == FAILURE) { + if (php_pdo_register_driver(&pdo_mysql_driver) == FAILURE) { return FAILURE; } - return php_pdo_register_driver(&pdo_mysql_driver); + return php_pdo_register_driver_specific_ce(&pdo_mysql_driver, pdo_mysql_ce); } /* }}} */ diff --git a/ext/pdo_mysql/tests/inc/mysql_pdo_test.inc b/ext/pdo_mysql/tests/inc/mysql_pdo_test.inc index a783d812ea43..48a7c525a701 100644 --- a/ext/pdo_mysql/tests/inc/mysql_pdo_test.inc +++ b/ext/pdo_mysql/tests/inc/mysql_pdo_test.inc @@ -35,33 +35,6 @@ class MySQLPDOTest extends PDOTest { return self::factory('PDO', null, $attr); } - static function connect($drop_test_tables = false, $myattr = null, $mydsn = null) { - $dsn = self::getDSN($mydsn); - $user = PDO_MYSQL_TEST_USER; - $pass = PDO_MYSQL_TEST_PASS; - $attr = getenv('PDOTEST_ATTR'); - - if (is_string($attr) && strlen($attr)) { - $attr = unserialize($attr); - } else { - $attr = null; - } - if ($user === false) - $user = NULL; - if ($pass === false) - $pass = NULL; - - $db = PDO::connect($dsn, $user, $pass, $attr); - if (!$db) { - die("Could not create PDO object (DSN=$dsn, user=$user)\n"); - } - - $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); - $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); - - return $db; - } - static function createTestTable($table, $db, $engine = null) { if (!$engine) $engine = PDO_MYSQL_TEST_ENGINE; diff --git a/ext/pdo_mysql/tests/pdomysql_002.phpt b/ext/pdo_mysql/tests/pdomysql_002.phpt index 1be1739e1ee0..41bc2a200a17 100644 --- a/ext/pdo_mysql/tests/pdomysql_002.phpt +++ b/ext/pdo_mysql/tests/pdomysql_002.phpt @@ -17,7 +17,7 @@ if (!$db instanceof PdoMysql) { echo "Wrong class type. Should be PdoMysql but is " . get_class($db) . "\n"; } -$db->exec('CREATE TABLE IF NOT EXISTS pdomysql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec('CREATE TABLE pdomysql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); $db->exec("INSERT INTO pdomysql_002 VALUES(1, 'A'), (2, 'B'), (3, 'C')"); foreach ($db->query('SELECT name FROM pdomysql_002') as $row) { diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c index 1bb30e9a0aa4..2d6249afb76c 100644 --- a/ext/pdo_odbc/pdo_odbc.c +++ b/ext/pdo_odbc/pdo_odbc.c @@ -27,8 +27,7 @@ #include "php_pdo_odbc_int.h" #include "pdo_odbc_arginfo.h" -zend_class_entry *pdoodbc_ce; -static pdo_driver_class_entry pdoodbc_pdo_driver_class_entry; +zend_class_entry *pdo_odbc_ce; /* {{{ pdo_odbc_deps[] */ static const zend_module_dep pdo_odbc_deps[] = { @@ -108,16 +107,10 @@ PHP_MINIT_FUNCTION(pdo_odbc) REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_DRIVER", SQL_CUR_USE_DRIVER); REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_ODBC", SQL_CUR_USE_ODBC); - pdoodbc_ce = register_class_PdoOdbc(pdo_dbh_ce); - pdoodbc_ce->create_object = pdo_dbh_new; + pdo_odbc_ce = register_class_PdoOdbc(pdo_dbh_ce); + pdo_odbc_ce->create_object = pdo_dbh_new; - pdoodbc_pdo_driver_class_entry.driver_name = "odbc"; - pdoodbc_pdo_driver_class_entry.driver_ce = pdoodbc_ce; - if (pdo_register_driver_specific_class(&pdoodbc_pdo_driver_class_entry) == FAILURE) { - return FAILURE; - } - - return SUCCESS; + return php_pdo_register_driver_specific_ce(&pdo_odbc_driver, pdo_odbc_ce); } /* }}} */ diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index fdf3ebcd9820..b702f9a4f6ca 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -29,7 +29,6 @@ #include "pdo_pgsql_arginfo.h" zend_class_entry *PdoPgsql_ce; -static pdo_driver_class_entry PdoPgsql_pdo_driver_class_entry; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_pgsql_deps[] = { @@ -157,13 +156,11 @@ PHP_MINIT_FUNCTION(pdo_pgsql) PdoPgsql_ce = register_class_PdoPgsql(pdo_dbh_ce); PdoPgsql_ce->create_object = pdo_dbh_new; - PdoPgsql_pdo_driver_class_entry.driver_name = "pgsql"; - PdoPgsql_pdo_driver_class_entry.driver_ce = PdoPgsql_ce; - if (pdo_register_driver_specific_class(&PdoPgsql_pdo_driver_class_entry) == FAILURE) { + if (php_pdo_register_driver(&pdo_pgsql_driver) == FAILURE) { return FAILURE; } - return php_pdo_register_driver(&pdo_pgsql_driver); + return php_pdo_register_driver_specific_ce(&pdo_pgsql_driver, PdoPgsql_ce); } /* }}} */ diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 2dea45c8725a..bce9681ed107 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -30,7 +30,6 @@ #include "pdo_sqlite_arginfo.h" zend_class_entry *pdosqlite_ce; -static pdo_driver_class_entry pdosqlite_pdo_driver_class_entry; typedef struct { zval val; @@ -284,7 +283,7 @@ PHP_METHOD(PdoSqlite, loadExtension) pdo_dbh_t *dbh; pdo_sqlite_db_handle *db_handle; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &extension, &extension_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &extension, &extension_len) == FAILURE) { RETURN_THROWS(); } @@ -542,7 +541,7 @@ PHP_METHOD(PdoSqlite, openBlob) } } -static int php_pgsql_collation_callback(void *context, int string1_len, const void *string1, +static int php_sqlite3_collation_callback(void *context, int string1_len, const void *string1, int string2_len, const void *string2) { int ret; @@ -565,7 +564,11 @@ static int php_pgsql_collation_callback(void *context, int string1_len, const vo php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); } else if (!Z_ISUNDEF(retval)) { if (Z_TYPE(retval) != IS_LONG) { - convert_to_long(&retval); + zend_string *func_name = get_active_function_or_method_name(); + zend_type_error("%s(): Return value of the callback must be of type int, %s returned", + ZSTR_VAL(func_name), zend_zval_value_name(&retval)); + zend_string_release(func_name); + return FAILURE; } ret = 0; if (Z_LVAL(retval) > 0) { @@ -653,7 +656,7 @@ PHP_METHOD(PdoSqlite, createCollation) collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); - ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_pgsql_collation_callback); + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); if (ret == SQLITE_OK) { collation->name = estrdup(collation_name); @@ -665,6 +668,10 @@ PHP_METHOD(PdoSqlite, createCollation) RETURN_TRUE; } + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + efree(collation); RETURN_FALSE; } @@ -686,14 +693,11 @@ PHP_MINIT_FUNCTION(pdo_sqlite) pdosqlite_ce = register_class_PdoSqlite(pdo_dbh_ce); pdosqlite_ce->create_object = pdo_dbh_new; - pdosqlite_pdo_driver_class_entry.driver_name = "sqlite"; - pdosqlite_pdo_driver_class_entry.driver_ce = pdosqlite_ce; - - if (pdo_register_driver_specific_class(&pdosqlite_pdo_driver_class_entry) == FAILURE) { + if (php_pdo_register_driver(&pdo_sqlite_driver) == FAILURE) { return FAILURE; } - return php_pdo_register_driver(&pdo_sqlite_driver); + return php_pdo_register_driver_specific_ce(&pdo_sqlite_driver, pdosqlite_ce); } /* }}} */ diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt index 2dba9eeb477a..dc1e4c13a330 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt @@ -20,6 +20,8 @@ $db->rollback(); $r = $db->query('SELECT COUNT(*) FROM test_pdo_sqlite_transaction'); var_dump($r->rowCount()); +$db->query('DROP TABLE test_pdo_sqlite_transaction'); + ?> --EXPECTF-- int(0) From c5dbda2a145899ed34c58ac7847290cf35d853e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 23 Dec 2023 15:02:39 +0100 Subject: [PATCH 04/11] Add new Firebird class constants --- ext/pdo_firebird/pdo_firebird.stub.php | 15 ++++++++++++ ext/pdo_firebird/pdo_firebird_arginfo.h | 32 ++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/ext/pdo_firebird/pdo_firebird.stub.php b/ext/pdo_firebird/pdo_firebird.stub.php index e8804277a531..85c4ab83433c 100644 --- a/ext/pdo_firebird/pdo_firebird.stub.php +++ b/ext/pdo_firebird/pdo_firebird.stub.php @@ -16,4 +16,19 @@ class PdoFirebird extends PDO /** @cvalue PDO_FB_ATTR_TIMESTAMP_FORMAT */ public const int ATTR_TIMESTAMP_FORMAT = UNKNOWN; + + /** @cvalue PDO_FB_TRANSACTION_ISOLATION_LEVEL */ + public const int TRANSACTION_ISOLATION_LEVEL = UNKNOWN; + + /** @cvalue PDO_FB_READ_COMMITTED */ + public const int READ_COMMITTED = UNKNOWN; + + /** @cvalue PDO_FB_REPEATABLE_READ */ + public const int REPEATABLE_READ = UNKNOWN; + + /** @cvalue PDO_FB_SERIALIZABLE */ + public const int SERIALIZABLE = UNKNOWN; + + /** @cvalue PDO_FB_WRITABLE_TRANSACTION */ + public const int WRITABLE_TRANSACTION = UNKNOWN; } diff --git a/ext/pdo_firebird/pdo_firebird_arginfo.h b/ext/pdo_firebird/pdo_firebird_arginfo.h index 6122d6ebbe44..e8b297a4b7df 100644 --- a/ext/pdo_firebird/pdo_firebird_arginfo.h +++ b/ext/pdo_firebird/pdo_firebird_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8b089db6fa589edf596ed0564751ad60507ed0dd */ + * Stub hash: 037c073de9e3e3593c62cb7da8ee7202c38c2723 */ @@ -34,5 +34,35 @@ static zend_class_entry *register_class_PdoFirebird(zend_class_entry *class_entr zend_declare_typed_class_constant(class_entry, const_ATTR_TIMESTAMP_FORMAT_name, &const_ATTR_TIMESTAMP_FORMAT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ATTR_TIMESTAMP_FORMAT_name); + zval const_TRANSACTION_ISOLATION_LEVEL_value; + ZVAL_LONG(&const_TRANSACTION_ISOLATION_LEVEL_value, PDO_FB_TRANSACTION_ISOLATION_LEVEL); + zend_string *const_TRANSACTION_ISOLATION_LEVEL_name = zend_string_init_interned("TRANSACTION_ISOLATION_LEVEL", sizeof("TRANSACTION_ISOLATION_LEVEL") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_ISOLATION_LEVEL_name, &const_TRANSACTION_ISOLATION_LEVEL_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_ISOLATION_LEVEL_name); + + zval const_READ_COMMITTED_value; + ZVAL_LONG(&const_READ_COMMITTED_value, PDO_FB_READ_COMMITTED); + zend_string *const_READ_COMMITTED_name = zend_string_init_interned("READ_COMMITTED", sizeof("READ_COMMITTED") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_READ_COMMITTED_name, &const_READ_COMMITTED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_READ_COMMITTED_name); + + zval const_REPEATABLE_READ_value; + ZVAL_LONG(&const_REPEATABLE_READ_value, PDO_FB_REPEATABLE_READ); + zend_string *const_REPEATABLE_READ_name = zend_string_init_interned("REPEATABLE_READ", sizeof("REPEATABLE_READ") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_REPEATABLE_READ_name, &const_REPEATABLE_READ_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_REPEATABLE_READ_name); + + zval const_SERIALIZABLE_value; + ZVAL_LONG(&const_SERIALIZABLE_value, PDO_FB_SERIALIZABLE); + zend_string *const_SERIALIZABLE_name = zend_string_init_interned("SERIALIZABLE", sizeof("SERIALIZABLE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_SERIALIZABLE_name, &const_SERIALIZABLE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_SERIALIZABLE_name); + + zval const_WRITABLE_TRANSACTION_value; + ZVAL_LONG(&const_WRITABLE_TRANSACTION_value, PDO_FB_WRITABLE_TRANSACTION); + zend_string *const_WRITABLE_TRANSACTION_name = zend_string_init_interned("WRITABLE_TRANSACTION", sizeof("WRITABLE_TRANSACTION") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_WRITABLE_TRANSACTION_name, &const_WRITABLE_TRANSACTION_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_WRITABLE_TRANSACTION_name); + return class_entry; } From b08b2e849726239d5443f45c0d8edcbb3ad2fd60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 24 Dec 2023 14:04:29 +0100 Subject: [PATCH 05/11] Address feedback from another review round --- ext/pdo/pdo.c | 4 +- ext/pdo/pdo_dbh.c | 6 +- ext/pdo_firebird/tests/pdofirebird_002.phpt | 2 +- ext/pdo_sqlite/pdo_sqlite.c | 16 ++-- ...pdo_sqlite_createafunction_trampoline.phpt | 47 ++++++++++++ .../pdo_sqlite_createaggregate.phpt | 10 +-- ...pdo_sqlite_createaggregate_trampoline.phpt | 60 +++++++++++++++ .../pdo_sqlite_createcollation.phpt | 12 +-- ...pdo_sqlite_createcollation_trampoline.phpt | 75 +++++++++++++++++++ .../pdo_sqlite_createfunction_with_flags.phpt | 9 +-- .../tests/subclasses/pdosqlite_001.phpt | 2 - 11 files changed, 206 insertions(+), 37 deletions(-) create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createafunction_trampoline.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_trampoline.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.phpt diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index 450c65ae60dc..6aa0f1d6f3df 100644 --- a/ext/pdo/pdo.c +++ b/ext/pdo/pdo.c @@ -118,7 +118,7 @@ PDO_API zend_result php_pdo_register_driver(const pdo_driver_t *driver) /* {{{ * return FAILURE; } if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) { - zend_error_noreturn(E_ERROR, "You MUST load PDO before loading any PDO drivers"); + zend_error_noreturn(E_ERROR, "The PDO extension must be loaded first in order to load PDO drivers"); return FAILURE; /* NOTREACHED */ } @@ -140,7 +140,7 @@ PDO_API void php_pdo_unregister_driver(const pdo_driver_t *driver) /* {{{ */ PDO_API zend_result php_pdo_register_driver_specific_ce(const pdo_driver_t *driver, zend_class_entry *ce) /* {{{ */ { if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) { - zend_error_noreturn(E_ERROR, "You MUST load PDO before loading any PDO drivers"); + zend_error_noreturn(E_ERROR, "The PDO extension must be loaded first in order to load PDO drivers"); return FAILURE; /* NOTREACHED */ } diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index bf8e027f4e3c..a306ce05e1fc 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -238,7 +238,7 @@ static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_e if (ce_based_on_called_object) { if (ce_based_on_driver_name) { if (instanceof_function(ce_based_on_called_object, ce_based_on_driver_name) == false) { - zend_throw_exception_ex(php_pdo_get_exception(), 0, + zend_throw_exception_ex(pdo_exception_ce, 0, "%s::connect() cannot be called when connecting to the \"%s\" driver, " "either %s::connect() or PDO::connect() must be called instead", ZSTR_VAL(called_scope->name), driver->driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); @@ -248,7 +248,7 @@ static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_e object_init_ex(new_object, ce_based_on_called_object); return true; } else { - zend_throw_exception_ex(php_pdo_get_exception(), 0, + zend_throw_exception_ex(pdo_exception_ce, 0, "%s::connect() cannot be called when connecting to an unknown driver, " "PDO::connect() must be called instead", ZSTR_VAL(called_scope->name)); @@ -258,7 +258,7 @@ static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_e if (ce_based_on_driver_name) { if (called_scope != pdo_dbh_ce) { - zend_throw_exception_ex(php_pdo_get_exception(), 0, + zend_throw_exception_ex(pdo_exception_ce, 0, "%s::connect() cannot be called when connecting to the \"%s\" driver, " "either %s::connect() or PDO::connect() must be called instead", ZSTR_VAL(called_scope->name), driver->driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); diff --git a/ext/pdo_firebird/tests/pdofirebird_002.phpt b/ext/pdo_firebird/tests/pdofirebird_002.phpt index ea48263bb57f..f9e4a026f5af 100644 --- a/ext/pdo_firebird/tests/pdofirebird_002.phpt +++ b/ext/pdo_firebird/tests/pdofirebird_002.phpt @@ -11,7 +11,7 @@ require_once __DIR__ . "/testdb.inc"; $db = connectToDb(); if (!$db instanceof PdoFirebird) { - echo "Wrong class type. Should be PdoOdbc but is " . get_class($db) . "\n"; + echo "Wrong class type. Should be PdoFirebird but is " . get_class($db) . "\n"; } $db->query('CREATE TABLE pdofirebird_002 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index bce9681ed107..801659ac2b61 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -602,17 +602,17 @@ PHP_METHOD(PdoSqlite, createAggregate) H = (pdo_sqlite_db_handle *)dbh->driver_data; ZEND_PARSE_PARAMETERS_START(3, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(step_fci, step_fcc) - Z_PARAM_FUNC(fini_fci, fini_fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) ZEND_PARSE_PARAMETERS_END(); func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, - func, NULL, php_pgsql_func_step_callback, php_pgsql_func_final_callback); + func, NULL, php_pgsql_func_step_callback, php_pgsql_func_final_callback); if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); @@ -646,8 +646,8 @@ PHP_METHOD(PdoSqlite, createCollation) int ret; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(collation_name, collation_name_len) - Z_PARAM_FUNC(fci, fcc) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) ZEND_PARSE_PARAMETERS_END(); dbh = Z_PDO_DBH_P(ZEND_THIS); diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createafunction_trampoline.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createafunction_trampoline.phpt new file mode 100644 index 000000000000..29eac9fae0ad --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createafunction_trampoline.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test PdoSqlite::createFunction() trampoline callback +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE test_pdo_sqlite_createaggregate_trampoline (a INTEGER, b INTEGER)'); + +$stmt = $db->query('INSERT INTO test_pdo_sqlite_createaggregate_trampoline VALUES (1, -1), (2, -2), (3, -3), (4, -4), (4, -4)'); + +class TrampolineTest { + public function __call(string $name, array $arguments) { + echo 'Trampoline for ', $name, PHP_EOL; + return strtoupper($arguments[0]); + } +} + +var_dump($db->createFunction('strtoupper', [new TrampolineTest(), 'strtoupper'])); + +foreach ($db->query('SELECT strtoupper("test")') as $row) { + var_dump($row); +} + +foreach ($db->query('SELECT strtoupper("test")') as $row) { + var_dump($row); +} + +?> +--EXPECT-- +bool(true) +Trampoline for strtoupper +array(2) { + ["strtoupper("test")"]=> + string(4) "TEST" + [0]=> + string(4) "TEST" +} +Trampoline for strtoupper +array(2) { + ["strtoupper("test")"]=> + string(4) "TEST" + [0]=> + string(4) "TEST" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt index ef1f34255467..67ae3660e068 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt @@ -8,20 +8,16 @@ pdo_sqlite // This test was copied from the pdo_sqlite test for sqliteCreateAggregate $db = new PdoSqlite('sqlite::memory:'); -$db->query('CREATE TABLE foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE test_pdo_sqlite_createaggregate (id INT AUTO INCREMENT, name TEXT)'); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); +$db->query('INSERT INTO test_pdo_sqlite_createaggregate VALUES (NULL, "PHP"), (NULL, "PHP6")'); $db->createAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); - -foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { +foreach ($db->query('SELECT testing(name) FROM test_pdo_sqlite_createaggregate') as $row) { var_dump($row); } -$db->query('DROP TABLE foobar'); - ?> --EXPECT-- array(2) { diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_trampoline.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_trampoline.phpt new file mode 100644 index 000000000000..0381f428e33c --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_trampoline.phpt @@ -0,0 +1,60 @@ +--TEST-- +Test PdoSqlite::createAggregate() trampoline callback +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE test_pdo_sqlite_createaggregate_trampoline (a INTEGER, b INTEGER)'); + +$stmt = $db->query('INSERT INTO test_pdo_sqlite_createaggregate_trampoline VALUES (1, -1), (2, -2), (3, -3), (4, -4), (4, -4)'); + +class TrampolineTest { + public function __call(string $name, array $arguments) { + echo 'Trampoline for ', $name, PHP_EOL; + $context = $arguments[0]; + if ($name === 'finalize') { + return implode(',', $context['values']); + } + if (empty($context)) { + $context = ['total' => 0, 'values' => []]; + } + $context['total'] += (int) $arguments[2]; + $context['values'][] = $context['total']; + return $context; + } +} + +var_dump($db->createAggregate('S', [new TrampolineTest(), 'step'], [new TrampolineTest(), 'finalize'], 1)); + +foreach ($db->query('SELECT S(a), S(b) FROM test_pdo_sqlite_createaggregate_trampoline') as $row) { + var_dump($row); +} + +?> +--EXPECT-- +bool(true) +Trampoline for step +Trampoline for step +Trampoline for step +Trampoline for step +Trampoline for step +Trampoline for step +Trampoline for step +Trampoline for step +Trampoline for step +Trampoline for step +Trampoline for finalize +Trampoline for finalize +array(4) { + ["S(a)"]=> + string(11) "1,3,6,10,14" + [0]=> + string(11) "1,3,6,10,14" + ["S(b)"]=> + string(16) "-1,-3,-6,-10,-14" + [1]=> + string(16) "-1,-3,-6,-10,-14" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt index 80344de0a59f..461e55d98128 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt @@ -9,26 +9,22 @@ pdo_sqlite $db = new PdoSqlite('sqlite::memory:'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE IF NOT EXISTS test_pdo_sqlite_createcollation (id INT AUTO INCREMENT, name TEXT)'); -$db->query('INSERT INTO foobar VALUES (NULL, "1")'); -$db->query('INSERT INTO foobar VALUES (NULL, "2")'); -$db->query('INSERT INTO foobar VALUES (NULL, "10")'); +$db->query('INSERT INTO test_pdo_sqlite_createcollation VALUES (NULL, "1"), (NULL, "2"), (NULL, "10")'); $db->createCollation('MYCOLLATE', function($a, $b) { return strnatcmp($a, $b); }); -$result = $db->query('SELECT name FROM foobar ORDER BY name COLLATE MYCOLLATE'); +$result = $db->query('SELECT name FROM test_pdo_sqlite_createcollation ORDER BY name COLLATE MYCOLLATE'); foreach ($result as $row) { echo $row['name'] . "\n"; } -$result = $db->query('SELECT name FROM foobar ORDER BY name'); +$result = $db->query('SELECT name FROM test_pdo_sqlite_createcollation ORDER BY name'); foreach ($result as $row) { echo $row['name'] . "\n"; } -$db->query('DROP TABLE foobar'); - ?> --EXPECT-- 1 diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.phpt new file mode 100644 index 000000000000..4b98432b7d71 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.phpt @@ -0,0 +1,75 @@ +--TEST-- +Test PdoSqlite::createCollation() trampoline callback +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE test_pdo_sqlite_createcollation_trampoline (s varchar(4))'); + +$stmt = $db->query('INSERT INTO test_pdo_sqlite_createcollation_trampoline VALUES ("a1"), ("a10"), ("a2")'); + +class TrampolineTest { + public function __call(string $name, array $arguments) { + echo 'Trampoline for ', $name, PHP_EOL; + return strnatcmp(...$arguments); + } +} + +var_dump($db->createCollation('NAT', [new TrampolineTest(), 'NAT'])); + +echo "default\n"; +foreach ($db->query('SELECT s FROM test_pdo_sqlite_createcollation_trampoline ORDER BY s') as $row) { + var_dump($row); +} + +echo "natural\n"; +foreach ($db->query('SELECT s FROM test_pdo_sqlite_createcollation_trampoline ORDER BY s COLLATE NAT') as $row) { + var_dump($row); +} + +?> +--EXPECT-- +bool(true) +default +array(2) { + ["s"]=> + string(2) "a1" + [0]=> + string(2) "a1" +} +array(2) { + ["s"]=> + string(3) "a10" + [0]=> + string(3) "a10" +} +array(2) { + ["s"]=> + string(2) "a2" + [0]=> + string(2) "a2" +} +natural +Trampoline for NAT +Trampoline for NAT +array(2) { + ["s"]=> + string(2) "a1" + [0]=> + string(2) "a1" +} +array(2) { + ["s"]=> + string(2) "a2" + [0]=> + string(2) "a2" +} +array(2) { + ["s"]=> + string(3) "a10" + [0]=> + string(3) "a10" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt index d29e79fc903d..c25801deb590 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt @@ -10,18 +10,15 @@ if (!defined('PDO::SQLITE_DETERMINISTIC')) die('skip system sqlite is too old'); query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); -$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); +$db->query('CREATE TABLE test_pdo_sqlite_createfunction_with_flags (id INT AUTO INCREMENT, name TEXT)'); +$db->query('INSERT INTO test_pdo_sqlite_createfunction_with_flags VALUES (NULL, "PHP"), (NULL, "PHP6")'); $db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); -foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { +foreach ($db->query('SELECT testing(name) FROM test_pdo_sqlite_createfunction_with_flags') as $row) { var_dump($row); } -$db->query('DROP TABLE foobar'); - ?> --EXPECT-- array(2) { diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt index 770fa8e4552c..26b234c4ee96 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt @@ -18,8 +18,6 @@ foreach ($db->query('SELECT testing(name) FROM pdosqlite_001') as $row) { var_dump($row); } -$db->query('DROP TABLE pdosqlite_001'); - echo "Fin."; ?> --EXPECT-- From 83f33171a00528a01d038203cf11d5eba3ea1487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Wed, 27 Dec 2023 10:20:48 +0100 Subject: [PATCH 06/11] Add comment, improve error messages, remove todos --- ext/pdo/pdo_dbh.c | 9 +++++---- ext/pdo/pdo_stmt.c | 4 ++-- ext/pdo/php_pdo.h | 2 +- ext/pdo/tests/pdo_uninitialized.phpt | 6 +++--- ext/pdo_pgsql/pdo_pgsql.c | 9 ++++++--- ext/pdo_pgsql/pgsql_driver.c | 8 ++++---- ext/pdo_sqlite/pdo_sqlite.c | 4 +--- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index a306ce05e1fc..4c0d8965f266 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -245,6 +245,7 @@ static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_e return false; } + /* A driver-specific implementation was instantiated via the connect() method of the appropriate driver class */ object_init_ex(new_object, ce_based_on_called_object); return true; } else { @@ -258,6 +259,7 @@ static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_e if (ce_based_on_driver_name) { if (called_scope != pdo_dbh_ce) { + /* A driver-specific implementation was instantiated via the connect method of a wrong driver class */ zend_throw_exception_ex(pdo_exception_ce, 0, "%s::connect() cannot be called when connecting to the \"%s\" driver, " "either %s::connect() or PDO::connect() must be called instead", @@ -265,10 +267,10 @@ static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_e return false; } - // A specific driver implementation was called on PDO + /* A driver-specific implementation was instantiated via PDO::__construct() */ object_init_ex(new_object, ce_based_on_driver_name); } else { - // No specific DB implementation found + /* No driver-specific implementation found */ object_init_ex(new_object, called_scope); } @@ -345,8 +347,7 @@ static void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zend_object *object if (new_zval_object != NULL) { ZEND_ASSERT((driver->driver_name != NULL) && "PDO driver name is null"); - bool result = create_driver_specific_pdo_object(driver, current_scope, new_zval_object); - if (!result) { + if (!create_driver_specific_pdo_object(driver, current_scope, new_zval_object)) { RETURN_THROWS(); } diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 2d0010eb747a..d11b7fbd57f2 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -37,7 +37,7 @@ #define PHP_STMT_GET_OBJ \ pdo_stmt_t *stmt = Z_PDO_STMT_P(ZEND_THIS); \ if (!stmt->dbh) { \ - zend_throw_error(NULL, "PDO object is uninitialized"); \ + zend_throw_error(NULL, "%s object is uninitialized", ZSTR_VAL(Z_OBJ(EX(This))->ce->name)); \ RETURN_THROWS(); \ } \ @@ -2231,7 +2231,7 @@ zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int pdo_stmt_t *stmt = Z_PDO_STMT_P(object); if (!stmt->dbh) { - zend_throw_error(NULL, "PDO object is uninitialized"); + zend_throw_error(NULL, "%s object is uninitialized", ZSTR_VAL(ce->name)); return NULL; } diff --git a/ext/pdo/php_pdo.h b/ext/pdo/php_pdo.h index 4de505a391b0..8f37990e955d 100644 --- a/ext/pdo/php_pdo.h +++ b/ext/pdo/php_pdo.h @@ -57,7 +57,7 @@ PHP_MINFO_FUNCTION(pdo); #define PDO_CONSTRUCT_CHECK \ if (!dbh->driver) { \ - zend_throw_error(NULL, "PDO object is not initialized, constructor was not called"); \ + zend_throw_error(NULL, "%s object is uninitialized", ZSTR_VAL(Z_OBJ(EX(This))->ce->name)); \ RETURN_THROWS(); \ } \ diff --git a/ext/pdo/tests/pdo_uninitialized.phpt b/ext/pdo/tests/pdo_uninitialized.phpt index 2e229a2bada3..e3b4c96af811 100644 --- a/ext/pdo/tests/pdo_uninitialized.phpt +++ b/ext/pdo/tests/pdo_uninitialized.phpt @@ -34,6 +34,6 @@ try { ?> --EXPECT-- -PDO object is not initialized, constructor was not called -PDO object is uninitialized -PDO object is uninitialized +MyPDO object is uninitialized +MyPDOStatement object is uninitialized +MyPDOStatement object is uninitialized diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index b702f9a4f6ca..e300ff39e34a 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -75,12 +75,15 @@ PHP_METHOD(PdoPgsql, escapeIdentifier) /* Obtain db Handle */ H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (H->server == NULL) { + zend_throw_error(NULL, "PostgreSQL connection has already been closed"); + RETURN_THROWS(); + } tmp = PQescapeIdentifier(H->server, ZSTR_VAL(from), ZSTR_LEN(from)); if (!tmp) { - // TODO - exception - php_error_docref(NULL, E_WARNING, "Failed to escape identifier"); - RETURN_FALSE; + zend_throw_error(NULL, "Failed to escape identifier"); + RETURN_THROWS(); } RETVAL_STRING(tmp); diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 9aa0ad220dab..525f9cd1c15f 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -603,8 +603,8 @@ void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS) ExecStatusType status; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss!", - &table_name, &table_name_len, &pg_rows, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + &table_name, &table_name_len, &pg_rows, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { RETURN_THROWS(); } @@ -719,8 +719,8 @@ void pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS) 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) { + &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(); } diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 801659ac2b61..8f2e82e0cd92 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -516,9 +516,7 @@ PHP_METHOD(PdoSqlite, openBlob) sqlite_flags = (flags & SQLITE_OPEN_READWRITE) ? 1 : 0; if (sqlite3_blob_open(sqlite_handle, dbname, table, column, rowid, sqlite_flags, &blob) != SQLITE_OK) { - // TODO - exception needed here. -// php_sqlite3_error(db_obj, "Unable to open blob: %s", sqlite3_errmsg(db_obj->db)); - // and converting to exception zend_throw_exception_ex(php_pdo_get_exception(), 0, errtext); + zend_error(E_WARNING, "Unable to open blob: %s", sqlite3_errmsg(sqlite_handle)); RETURN_FALSE; } From 711b3c290ff8101aa74bb556521dec19cdac7b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Wed, 3 Jan 2024 23:32:24 +0100 Subject: [PATCH 07/11] Display pgsql internal error message --- ext/pdo_pgsql/pdo_pgsql.c | 3 ++- ext/pdo_pgsql/tests/pdopgsql_003.phpt | 13 +++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index e300ff39e34a..7c16b7363a49 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -82,7 +82,8 @@ PHP_METHOD(PdoPgsql, escapeIdentifier) tmp = PQescapeIdentifier(H->server, ZSTR_VAL(from), ZSTR_LEN(from)); if (!tmp) { - zend_throw_error(NULL, "Failed to escape identifier"); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); RETURN_THROWS(); } diff --git a/ext/pdo_pgsql/tests/pdopgsql_003.phpt b/ext/pdo_pgsql/tests/pdopgsql_003.phpt index 5b86fe1c54b9..15b922dd3c0b 100644 --- a/ext/pdo_pgsql/tests/pdopgsql_003.phpt +++ b/ext/pdo_pgsql/tests/pdopgsql_003.phpt @@ -19,9 +19,14 @@ if (!$db instanceof PdoPgsql) { echo "Wrong class type. Should be PdoPgsql but is " . get_class($db) . "\n"; } -$result = $db->escapeIdentifier("This is a quote\""); +echo $db->escapeIdentifier("This is a quote\"") . "\n"; + +try { + $db->escapeIdentifier("aa\xC3\xC3\xC3"); +} catch (PDOException $e) { + echo $e->getMessage() . "\n"; +} -// TODO - ask someone who knows about postgresql if this is correct: -echo "Result is [$result]\n"; --EXPECT-- -Result is ["This is a quote"""] +"This is a quote""" +SQLSTATE[HY000]: General error: 7 incomplete multibyte character From 94f9d277f5c8a9d4466a983ae745f1083fb61fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 6 Jan 2024 19:06:33 +0100 Subject: [PATCH 08/11] Rename pgsql symbols inside sqlite folder --- ext/pdo_sqlite/pdo_sqlite.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 8f2e82e0cd92..a4034d782b2a 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -34,7 +34,7 @@ zend_class_entry *pdosqlite_ce; typedef struct { zval val; zend_long row; -} pdopgsql_aggregate_context; +} pdopsqlite_aggregate_context; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_sqlite_deps[] = { @@ -72,7 +72,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, int i; int ret; int fake_argc; - pdopgsql_aggregate_context *agg_context = NULL; + pdopsqlite_aggregate_context *agg_context = NULL; if (is_agg) { is_agg = 2; @@ -93,7 +93,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, } if (is_agg) { - agg_context = sqlite3_aggregate_context(context, sizeof(pdopgsql_aggregate_context)); + agg_context = sqlite3_aggregate_context(context, sizeof(pdopsqlite_aggregate_context)); if (!agg_context) { efree(zargs); return FAILURE; @@ -198,7 +198,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, return ret; } -void php_pgsql_func_callback(sqlite3_context *context, int argc, +void php_sqlite_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); @@ -206,7 +206,7 @@ void php_pgsql_func_callback(sqlite3_context *context, int argc, do_callback(&func->afunc, &func->func, argc, argv, context, 0); } -static void php_pgsql_func_step_callback(sqlite3_context *context, int argc, +static void php_sqlite_func_step_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); @@ -214,7 +214,7 @@ static void php_pgsql_func_step_callback(sqlite3_context *context, int argc, do_callback(&func->astep, &func->step, argc, argv, context, 1); } -static void php_pgsql_func_final_callback(sqlite3_context *context) +static void php_sqlite_func_final_callback(sqlite3_context *context) { struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); @@ -254,7 +254,7 @@ PHP_METHOD(PdoSqlite, createFunction) func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, - func, php_pgsql_func_callback, NULL, NULL); + func, php_sqlite_func_callback, NULL, NULL); if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); @@ -610,7 +610,7 @@ PHP_METHOD(PdoSqlite, createAggregate) func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, - func, NULL, php_pgsql_func_step_callback, php_pgsql_func_final_callback); + func, NULL, php_sqlite_func_step_callback, php_sqlite_func_final_callback); if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); From 0de1b88c7d80de4b10f60d7f3f2d74490361c78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 8 Jan 2024 00:01:02 +0100 Subject: [PATCH 09/11] Address new review comments --- ext/pdo/pdo_dbh.c | 8 - ext/pdo_dblib/tests/pdodblib_001.phpt | 4 +- ext/pdo_dblib/tests/pdodblib_002.phpt | 4 +- ext/pdo_firebird/tests/pdofirebird_001.phpt | 2 +- ext/pdo_firebird/tests/pdofirebird_002.phpt | 2 +- ext/pdo_mysql/pdo_mysql.c | 4 +- ext/pdo_mysql/tests/pdomysql_002.phpt | 2 +- ext/pdo_odbc/pdo_odbc.c | 2 +- ext/pdo_odbc/tests/pdoodbc_002.phpt | 2 +- ext/pdo_pgsql/tests/pdopgsql_002.phpt | 2 +- ext/pdo_sqlite/pdo_sqlite.c | 153 ++---------------- ext/pdo_sqlite/pdo_sqlite.stub.php | 4 +- ext/pdo_sqlite/pdo_sqlite_arginfo.h | 4 +- ext/pdo_sqlite/php_pdo_sqlite_int.h | 10 ++ ext/pdo_sqlite/sqlite_driver.c | 96 ++++++----- .../pdo_sqlite_createcollation.phpt | 2 +- ...pdo_sqlite_createcollation_trampoline.phpt | 2 +- .../tests/subclasses/pdosqlite_003.phpt | 2 +- .../tests/subclasses/pdosqlite_006.phpt | 2 +- .../pdosqlite_load_extension_failure.phpt | 2 +- 20 files changed, 98 insertions(+), 211 deletions(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 4c0d8965f266..43121dc9b5c6 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1410,7 +1410,6 @@ static HashTable *dbh_get_gc(zend_object *object, zval **gc_data, int *gc_count) } static zend_object_handlers pdo_dbh_object_handlers; -static zend_object_handlers pdosqlite_dbh_object_handlers; static void pdo_dbh_free_storage(zend_object *std); @@ -1427,13 +1426,6 @@ void pdo_dbh_init(int module_number) pdo_dbh_object_handlers.get_method = dbh_method_get; pdo_dbh_object_handlers.compare = zend_objects_not_comparable; pdo_dbh_object_handlers.get_gc = dbh_get_gc; - - memcpy(&pdosqlite_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - pdosqlite_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std); - pdosqlite_dbh_object_handlers.free_obj = pdo_dbh_free_storage; - pdosqlite_dbh_object_handlers.get_method = dbh_method_get; - pdosqlite_dbh_object_handlers.compare = zend_objects_not_comparable; - pdosqlite_dbh_object_handlers.get_gc = dbh_get_gc; } static void dbh_free(pdo_dbh_t *dbh, bool free_persistent) diff --git a/ext/pdo_dblib/tests/pdodblib_001.phpt b/ext/pdo_dblib/tests/pdodblib_001.phpt index f4e98fb8ec7e..63f8e40be45b 100644 --- a/ext/pdo_dblib/tests/pdodblib_001.phpt +++ b/ext/pdo_dblib/tests/pdodblib_001.phpt @@ -13,8 +13,8 @@ getDbConnection(); require __DIR__ . '/config.inc'; $db = getDbConnection(PdoDblib::class); -$db->query("CREATE TABLE #pdo_dblib_001(name varchar(32)); "); -$db->query("INSERT INTO #pdo_dblib_001 values('PHP'), ('PHP6');"); +$db->query("CREATE TABLE #pdo_dblib_001(name VARCHAR(32)); "); +$db->query("INSERT INTO #pdo_dblib_001 VALUES('PHP'), ('PHP6');"); foreach ($db->query('SELECT name FROM #pdo_dblib_001') as $row) { var_dump($row); diff --git a/ext/pdo_dblib/tests/pdodblib_002.phpt b/ext/pdo_dblib/tests/pdodblib_002.phpt index fd3d2bbb9423..16e0444c1109 100644 --- a/ext/pdo_dblib/tests/pdodblib_002.phpt +++ b/ext/pdo_dblib/tests/pdodblib_002.phpt @@ -17,8 +17,8 @@ if (!$db instanceof PdoDblib) { echo "Wrong class type. Should be PdoDblib but is " . get_class($db) . "\n"; } -$db->query("CREATE TABLE #pdo_dblib_002(name varchar(32))"); -$db->query("INSERT INTO #pdo_dblib_002 values('PHP'), ('PHP6')"); +$db->query("CREATE TABLE #pdo_dblib_002(name VARCHAR(32))"); +$db->query("INSERT INTO #pdo_dblib_002 VALUES('PHP'), ('PHP6')"); foreach ($db->query('SELECT name FROM #pdo_dblib_002') as $row) { var_dump($row); diff --git a/ext/pdo_firebird/tests/pdofirebird_001.phpt b/ext/pdo_firebird/tests/pdofirebird_001.phpt index 10bdd3e45b68..70fc594dfbf5 100644 --- a/ext/pdo_firebird/tests/pdofirebird_001.phpt +++ b/ext/pdo_firebird/tests/pdofirebird_001.phpt @@ -11,7 +11,7 @@ require_once __DIR__ . "/testdb.inc"; $db = getDbConnection(); -$db->query('CREATE TABLE pdofirebird_001 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); +$db->query('CREATE TABLE pdofirebird_001 (idx INT NOT NULL PRIMARY KEY, name VARCHAR(20))'); $db->query("INSERT INTO pdofirebird_001 VALUES (1, 'PHP')"); $db->query("INSERT INTO pdofirebird_001 VALUES (2, 'PHP6')"); diff --git a/ext/pdo_firebird/tests/pdofirebird_002.phpt b/ext/pdo_firebird/tests/pdofirebird_002.phpt index f9e4a026f5af..e849b8bb27fa 100644 --- a/ext/pdo_firebird/tests/pdofirebird_002.phpt +++ b/ext/pdo_firebird/tests/pdofirebird_002.phpt @@ -14,7 +14,7 @@ if (!$db instanceof PdoFirebird) { echo "Wrong class type. Should be PdoFirebird but is " . get_class($db) . "\n"; } -$db->query('CREATE TABLE pdofirebird_002 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); +$db->query('CREATE TABLE pdofirebird_002 (idx INT NOT NULL PRIMARY KEY, name VARCHAR(20))'); $db->exec("INSERT INTO pdofirebird_002 VALUES(1, 'A')"); $db->exec("INSERT INTO pdofirebird_002 VALUES(2, 'B')"); diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c index d7f7dbaf6c5a..291b60c59dab 100644 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -28,7 +28,7 @@ #include "php_pdo_mysql_int.h" #include "pdo_mysql_arginfo.h" -zend_class_entry *pdo_mysql_ce; +static zend_class_entry *pdo_mysql_ce; #ifdef COMPILE_DL_PDO_MYSQL #ifdef ZTS @@ -92,6 +92,8 @@ PHP_METHOD(PdoMysql, getWarningCount) pdo_dbh_t *dbh; pdo_mysql_db_handle *H; + ZEND_PARSE_PARAMETERS_NONE(); + dbh = Z_PDO_DBH_P(ZEND_THIS); PDO_CONSTRUCT_CHECK; diff --git a/ext/pdo_mysql/tests/pdomysql_002.phpt b/ext/pdo_mysql/tests/pdomysql_002.phpt index 41bc2a200a17..faa1c2d1712c 100644 --- a/ext/pdo_mysql/tests/pdomysql_002.phpt +++ b/ext/pdo_mysql/tests/pdomysql_002.phpt @@ -17,7 +17,7 @@ if (!$db instanceof PdoMysql) { echo "Wrong class type. Should be PdoMysql but is " . get_class($db) . "\n"; } -$db->exec('CREATE TABLE pdomysql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec('CREATE TABLE pdomysql_002(id INT NOT NULL PRIMARY KEY, name VARCHAR(10))'); $db->exec("INSERT INTO pdomysql_002 VALUES(1, 'A'), (2, 'B'), (3, 'C')"); foreach ($db->query('SELECT name FROM pdomysql_002') as $row) { diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c index 2d6249afb76c..b7081d5b693a 100644 --- a/ext/pdo_odbc/pdo_odbc.c +++ b/ext/pdo_odbc/pdo_odbc.c @@ -27,7 +27,7 @@ #include "php_pdo_odbc_int.h" #include "pdo_odbc_arginfo.h" -zend_class_entry *pdo_odbc_ce; +static zend_class_entry *pdo_odbc_ce; /* {{{ pdo_odbc_deps[] */ static const zend_module_dep pdo_odbc_deps[] = { diff --git a/ext/pdo_odbc/tests/pdoodbc_002.phpt b/ext/pdo_odbc/tests/pdoodbc_002.phpt index fd3547745026..02be07c560d7 100644 --- a/ext/pdo_odbc/tests/pdoodbc_002.phpt +++ b/ext/pdo_odbc/tests/pdoodbc_002.phpt @@ -17,7 +17,7 @@ if (!$db instanceof PdoOdbc) { echo "Wrong class type. Should be PdoOdbc but is " . get_class($db) . "\n"; } -$db->exec('CREATE TABLE pdoodbc_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec('CREATE TABLE pdoodbc_002(id INT NOT NULL PRIMARY KEY, name VARCHAR(10))'); $db->exec("INSERT INTO pdoodbc_002 VALUES(1, 'A'), (2, 'B'), (3, 'C')"); foreach ($db->query('SELECT name FROM pdoodbc_002') as $row) { diff --git a/ext/pdo_pgsql/tests/pdopgsql_002.phpt b/ext/pdo_pgsql/tests/pdopgsql_002.phpt index 24f198f87348..036b4649d202 100644 --- a/ext/pdo_pgsql/tests/pdopgsql_002.phpt +++ b/ext/pdo_pgsql/tests/pdopgsql_002.phpt @@ -19,7 +19,7 @@ if (!$db instanceof PdoPgsql) { echo "Wrong class type. Should be PdoPgsql but is " . get_class($db) . "\n"; } -$db->exec('CREATE TABLE pdopgsql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec('CREATE TABLE pdopgsql_002(id INT NOT NULL PRIMARY KEY, name VARCHAR(10))'); $db->exec("INSERT INTO pdopgsql_002 VALUES(1, 'A'), (2, 'B'), (3, 'C')"); foreach ($db->query('SELECT name FROM pdopgsql_002') as $row) { diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index a4034d782b2a..178a077d4030 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -29,7 +29,7 @@ #include "zend_exceptions.h" #include "pdo_sqlite_arginfo.h" -zend_class_entry *pdosqlite_ce; +static zend_class_entry *pdosqlite_ce; typedef struct { zval val; @@ -63,9 +63,7 @@ zend_module_entry pdo_sqlite_module_entry = { ZEND_GET_MODULE(pdo_sqlite) #endif -static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, - int argc, sqlite3_value **argv, sqlite3_context *context, - int is_agg) +static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) { zval *zargs = NULL; zval retval; @@ -198,16 +196,14 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, return ret; } -void php_sqlite_func_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) +static void php_sqlite_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->afunc, &func->func, argc, argv, context, 0); } -static void php_sqlite_func_step_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) +static void php_sqlite_func_step_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); @@ -226,50 +222,7 @@ static void php_sqlite_func_final_callback(sqlite3_context *context) */ PHP_METHOD(PdoSqlite, createFunction) { - //copied from sqlite_driver.c - - struct pdo_sqlite_func *func; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *func_name; - size_t func_name_len; - 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_STRING(func_name, func_name_len) - Z_PARAM_FUNC(fci, fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, - func, php_sqlite_func_callback, NULL, NULL); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->func, &fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; + pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite_func_callback); } #ifndef PDO_SQLITE_OMIT_LOAD_EXTENSION @@ -302,7 +255,6 @@ PHP_METHOD(PdoSqlite, loadExtension) (strcmp(sapi_module.name, "cli") != 0) && (strncmp(sapi_module.name, "embed", 5) != 0) ) { - // TODO - needs test. zend_throw_exception_ex(php_pdo_get_exception(), 0, "Not supported in multithreaded Web servers"); RETURN_THROWS(); } @@ -316,7 +268,7 @@ PHP_METHOD(PdoSqlite, loadExtension) sqlite3 *sqlite_handle; sqlite_handle = db_handle->db; - // This only enables extension loading for the C api, not for SQL + /* This only enables extension loading for the C api, not for SQL */ sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); if (sqlite3_load_extension(sqlite_handle, fullpath, 0, &errtext) != SQLITE_OK) { @@ -326,9 +278,9 @@ PHP_METHOD(PdoSqlite, loadExtension) RETURN_THROWS(); } - // We disable extension loading for a vague feeling of safety. This is probably not necessary - // as extensions can only be loaded through C code, not through SQL, and if someone can get - // some C code to run on the server, they can do anything. + /* We disable extension loading for a vague feeling of safety. This is probably not necessary + as extensions can only be loaded through C code, not through SQL, and if someone can get + some C code to run on the server, they can do anything.*/ sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL); } #endif @@ -539,7 +491,7 @@ PHP_METHOD(PdoSqlite, openBlob) } } -static int php_sqlite3_collation_callback(void *context, int string1_len, const void *string1, +static int php_sqlite_collation_callback(void *context, int string1_len, const void *string1, int string2_len, const void *string2) { int ret; @@ -585,93 +537,12 @@ static int php_sqlite3_collation_callback(void *context, int string1_len, const PHP_METHOD(PdoSqlite, createAggregate) { - struct pdo_sqlite_func *func; - zend_fcall_info step_fci, fini_fci; - zend_fcall_info_cache step_fcc, fini_fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - ZEND_PARSE_PARAMETERS_START(3, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(step_fci, step_fcc) - Z_PARAM_FUNC(fini_fci, fini_fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - ZEND_PARSE_PARAMETERS_END(); - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, - func, NULL, php_sqlite_func_step_callback, php_sqlite_func_final_callback); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->step, &step_fci.function_name); - - ZVAL_COPY(&func->fini, &fini_fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; + pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite_func_step_callback, php_sqlite_func_final_callback); } -/* bool SQLite::createCollation(string name, callable callback) - Registers a collation with the sqlite db handle */ PHP_METHOD(PdoSqlite, createCollation) { - struct pdo_sqlite_collation *collation; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *collation_name; - size_t collation_name_len; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(collation_name, collation_name_len) - Z_PARAM_FUNC(fci, fcc) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); - - ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); - if (ret == SQLITE_OK) { - collation->name = estrdup(collation_name); - - ZVAL_COPY(&collation->callback, &fci.function_name); - - collation->next = H->collations; - H->collations = collation; - - RETURN_TRUE; - } - - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); - } - - efree(collation); - RETURN_FALSE; + pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite_collation_callback); } /* {{{ PHP_MINIT_FUNCTION */ diff --git a/ext/pdo_sqlite/pdo_sqlite.stub.php b/ext/pdo_sqlite/pdo_sqlite.stub.php index 23f7f5dd1a25..5e46b393cca3 100644 --- a/ext/pdo_sqlite/pdo_sqlite.stub.php +++ b/ext/pdo_sqlite/pdo_sqlite.stub.php @@ -51,9 +51,9 @@ public function createFunction( // PDO_SQLITE_OMIT_LOAD_EXTENSION might be defined by ext/pdo_sqlite/config.m4 // if Sqlite3 did not have the sqlite3_load_extension function present -// which can deepend on how SQLite was compiled: https://www.sqlite.org/compile.html +// which can depend on how SQLite was compiled: https://www.sqlite.org/compile.html #ifndef PDO_SQLITE_OMIT_LOAD_EXTENSION - public function loadExtension(string $name): bool {} + public function loadExtension(string $name): void {} #endif /** @return resource|false */ diff --git a/ext/pdo_sqlite/pdo_sqlite_arginfo.h b/ext/pdo_sqlite/pdo_sqlite_arginfo.h index 2804dd88c4b3..eb45b8041e01 100644 --- a/ext/pdo_sqlite/pdo_sqlite_arginfo.h +++ b/ext/pdo_sqlite/pdo_sqlite_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0431d72c529b897acadb1db04d5049bcd46cac0e */ + * Stub hash: 167d0e30af99bd31c46ea1649c661b6aa38eaccb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_PdoSqlite_createAggregate, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) @@ -21,7 +21,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_PdoSqlite_createFunction, ZEND_END_ARG_INFO() #if !defined(PDO_SQLITE_OMIT_LOAD_EXTENSION) -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_PdoSqlite_loadExtension, 0, 1, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_PdoSqlite_loadExtension, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) ZEND_END_ARG_INFO() #endif diff --git a/ext/pdo_sqlite/php_pdo_sqlite_int.h b/ext/pdo_sqlite/php_pdo_sqlite_int.h index c9aeee08a3a5..d92734af0202 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite_int.h +++ b/ext/pdo_sqlite/php_pdo_sqlite_int.h @@ -78,4 +78,14 @@ enum { PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES }; +typedef void pdo_sqlite_func_callback(sqlite3_context*, int, sqlite3_value**); +typedef void pdo_sqlite_func_step_callback(sqlite3_context*, int, sqlite3_value **); +typedef void pdo_sqlite_func_final_callback(sqlite3_context*); +typedef int pdo_sqlite_create_collation_callback(void*, int, const void*, int, const void*); + +void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_func_callback callback); +void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_func_step_callback step_callback, + pdo_sqlite_func_final_callback final_callback); +void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback); + #endif diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index f83ce43a3ce8..8a8ad56e84e5 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -309,9 +309,7 @@ typedef struct { zend_long row; } aggregate_context; -static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, - int argc, sqlite3_value **argv, sqlite3_context *context, - int is_agg) +static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) { zval *zargs = NULL; zval retval; @@ -444,16 +442,14 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, return ret; } -static void php_sqlite3_func_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) +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->afunc, &func->func, argc, argv, context, 0); } -static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) +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); @@ -467,9 +463,7 @@ static void php_sqlite3_func_final_callback(sqlite3_context *context) do_callback(&func->afini, &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) +static int php_sqlite3_collation_callback(void *context, int string1_len, const void *string1, int string2_len, const void *string2) { int ret; zval zargs[2]; @@ -481,7 +475,7 @@ static int php_sqlite3_collation_callback(void *context, collation->fc.fci.object = NULL; collation->fc.fci.retval = &retval; - // Prepare the arguments. + /* Prepare the arguments. */ ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); collation->fc.fci.param_count = 2; @@ -508,9 +502,7 @@ static int php_sqlite3_collation_callback(void *context, return ret; } -/* {{{ 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) +void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_func_callback callback) { struct pdo_sqlite_func *func; zend_fcall_info fci; @@ -538,8 +530,7 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, - func, php_sqlite3_func_callback, NULL, NULL); + ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, func, callback, NULL, NULL); if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); @@ -556,29 +547,18 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) efree(func); RETURN_FALSE; } -/* }}} */ -/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) +/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) 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) +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) { + pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite3_func_callback); +} +/* }}} */ + +void pdo_sqlite_create_aggregate_internal( + INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_func_step_callback step_callback, pdo_sqlite_func_final_callback final_callback +) { struct pdo_sqlite_func *func; zend_fcall_info step_fci, fini_fci; zend_fcall_info_cache step_fcc, fini_fcc; @@ -604,8 +584,7 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, - func, NULL, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); + ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, func, NULL, step_callback, final_callback); if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); @@ -624,11 +603,33 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) efree(func); 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, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); +} /* }}} */ -/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) - Registers a collation with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) +void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback) { struct pdo_sqlite_collation *collation; zend_fcall_info fci; @@ -651,7 +652,7 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); - ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, callback); if (ret == SQLITE_OK) { collation->name = estrdup(collation_name); @@ -663,9 +664,20 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) RETURN_TRUE; } + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + efree(collation); RETURN_FALSE; } + +/* {{{ 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) diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt index 461e55d98128..e65afd4ad910 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt @@ -9,7 +9,7 @@ pdo_sqlite $db = new PdoSqlite('sqlite::memory:'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -$db->query('CREATE TABLE IF NOT EXISTS test_pdo_sqlite_createcollation (id INT AUTO INCREMENT, name TEXT)'); +$db->query('CREATE TABLE test_pdo_sqlite_createcollation (id INT AUTO INCREMENT, name TEXT)'); $db->query('INSERT INTO test_pdo_sqlite_createcollation VALUES (NULL, "1"), (NULL, "2"), (NULL, "10")'); diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.phpt index 4b98432b7d71..257c2f788b5f 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.phpt @@ -7,7 +7,7 @@ pdo_sqlite $db = new PdoSqlite('sqlite::memory:'); -$db->query('CREATE TABLE test_pdo_sqlite_createcollation_trampoline (s varchar(4))'); +$db->query('CREATE TABLE test_pdo_sqlite_createcollation_trampoline (s VARCHAR(4))'); $stmt = $db->query('INSERT INTO test_pdo_sqlite_createcollation_trampoline VALUES ("a1"), ("a10"), ("a2")'); diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt index 9a14d892977d..860848dcff86 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt @@ -25,7 +25,7 @@ require __DIR__ . "/config.inc"; $db = Pdo::connect('sqlite::memory:'); if (!$db instanceof PdoSqlite) { - echo "Wrong class type. Should be PdoSqlite but is " .get_class($db) . "\n"; + echo "Wrong class type. Should be PdoSqlite but is " . get_class($db) . "\n"; } $result = $db->loadExtension(getSpatialiteExtensionLocation();); diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt index 79626edc6a19..1095b9a372d0 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt @@ -9,7 +9,7 @@ class MyPdoSqlite extends PdoSqlite {} $db = MyPdoSqlite::connect('sqlite::memory:'); if (!$db instanceof MyPdoSqlite) { - echo "Wrong class type. Should be MyPdoSqlite but is " .get_class($db) . "\n"; + echo "Wrong class type. Should be MyPdoSqlite but is " . get_class($db) . "\n"; } echo "OK"; ?> diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt index 462f22cdb67b..8a3abf66dcf1 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt @@ -13,7 +13,7 @@ if (!method_exists(PdoSqlite::class, "loadExtension")) { $db = PdoSqlite::connect('sqlite::memory:'); if (!$db instanceof PdoSqlite) { - echo "Wrong class type. Should be PdoSqlite but is " .get_class($db) . "\n"; + echo "Wrong class type. Should be PdoSqlite but is " . get_class($db) . "\n"; } try { From 147a10b597d912c1b6e30f336b4b1ef4421cd1b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 8 Jan 2024 17:08:43 +0100 Subject: [PATCH 10/11] Remove even more redundancies --- ext/pdo_sqlite/pdo_sqlite.c | 163 +--------------------------- ext/pdo_sqlite/php_pdo_sqlite_int.h | 8 +- ext/pdo_sqlite/sqlite_driver.c | 30 ++--- 3 files changed, 19 insertions(+), 182 deletions(-) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 178a077d4030..55bfc621291e 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -31,11 +31,6 @@ static zend_class_entry *pdosqlite_ce; -typedef struct { - zval val; - zend_long row; -} pdopsqlite_aggregate_context; - /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_sqlite_deps[] = { ZEND_MOD_REQUIRED("pdo") @@ -63,166 +58,12 @@ zend_module_entry pdo_sqlite_module_entry = { ZEND_GET_MODULE(pdo_sqlite) #endif -static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) -{ - zval *zargs = NULL; - zval retval; - int i; - int ret; - int fake_argc; - pdopsqlite_aggregate_context *agg_context = NULL; - - if (is_agg) { - is_agg = 2; - } - - fake_argc = argc + is_agg; - - fc->fci.size = sizeof(fc->fci); - ZVAL_COPY_VALUE(&fc->fci.function_name, cb); - fc->fci.object = NULL; - fc->fci.retval = &retval; - fc->fci.param_count = fake_argc; - - /* 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(pdopsqlite_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; - } - } - - fc->fci.params = zargs; - - if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } - - /* 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)) { - zval_ptr_dtor(&agg_context->val); - 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_sqlite_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->afunc, &func->func, argc, argv, context, 0); -} - -static void php_sqlite_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->astep, &func->step, argc, argv, context, 1); -} - -static void php_sqlite_func_final_callback(sqlite3_context *context) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - - do_callback(&func->afini, &func->fini, 0, NULL, context, 1); -} - /* proto bool PdoSqlite::createFunction(string $function_name, callable $callback, int $num_args = -1, int $flags = 0) Creates a function that can be used in a query */ PHP_METHOD(PdoSqlite, createFunction) { - pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite_func_callback); + pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); } #ifndef PDO_SQLITE_OMIT_LOAD_EXTENSION @@ -537,7 +378,7 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v PHP_METHOD(PdoSqlite, createAggregate) { - pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite_func_step_callback, php_sqlite_func_final_callback); + pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); } PHP_METHOD(PdoSqlite, createCollation) diff --git a/ext/pdo_sqlite/php_pdo_sqlite_int.h b/ext/pdo_sqlite/php_pdo_sqlite_int.h index d92734af0202..ae42a03e8a80 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite_int.h +++ b/ext/pdo_sqlite/php_pdo_sqlite_int.h @@ -78,14 +78,10 @@ enum { PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES }; -typedef void pdo_sqlite_func_callback(sqlite3_context*, int, sqlite3_value**); -typedef void pdo_sqlite_func_step_callback(sqlite3_context*, int, sqlite3_value **); -typedef void pdo_sqlite_func_final_callback(sqlite3_context*); typedef int pdo_sqlite_create_collation_callback(void*, int, const void*, int, const void*); -void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_func_callback callback); -void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_func_step_callback step_callback, - pdo_sqlite_func_final_callback final_callback); +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/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index 8a8ad56e84e5..7cd8880b8609 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -442,13 +442,6 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_va 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->afunc, &func->func, argc, argv, context, 0); -} - 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); @@ -502,7 +495,14 @@ static int php_sqlite3_collation_callback(void *context, int string1_len, const return ret; } -void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_func_callback callback) +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->afunc, &func->func, argc, argv, context, 0); +} + +void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) { struct pdo_sqlite_func *func; zend_fcall_info fci; @@ -530,7 +530,7 @@ void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlit func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, func, callback, NULL, NULL); + ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, func, php_sqlite3_func_callback, NULL, NULL); if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); @@ -552,13 +552,12 @@ void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlit Registers a UDF with the sqlite db handle */ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) { - pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite3_func_callback); + pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ -void pdo_sqlite_create_aggregate_internal( - INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_func_step_callback step_callback, pdo_sqlite_func_final_callback final_callback -) { +void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) +{ struct pdo_sqlite_func *func; zend_fcall_info step_fci, fini_fci; zend_fcall_info_cache step_fcc, fini_fcc; @@ -584,7 +583,8 @@ void pdo_sqlite_create_aggregate_internal( func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, func, NULL, step_callback, final_callback); + ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, func, NULL, + php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); @@ -625,7 +625,7 @@ void pdo_sqlite_create_aggregate_internal( PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) { - pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); + pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ From 631024b2a68994185d25ba21e4d72b021fb948d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 9 Jan 2024 08:22:04 +0100 Subject: [PATCH 11/11] Mark all PDO subclass entries as static --- ext/pdo_dblib/pdo_dblib.c | 2 +- ext/pdo_firebird/pdo_firebird.c | 2 +- ext/pdo_pgsql/pdo_pgsql.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index 149a8824bf08..f4299cf51735 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -32,7 +32,7 @@ ZEND_DECLARE_MODULE_GLOBALS(dblib) static PHP_GINIT_FUNCTION(dblib); -zend_class_entry *PdoDblib_ce; +static zend_class_entry *PdoDblib_ce; static const zend_module_dep pdo_dblib_deps[] = { ZEND_MOD_REQUIRED("pdo") diff --git a/ext/pdo_firebird/pdo_firebird.c b/ext/pdo_firebird/pdo_firebird.c index 3c2045f6fa79..99fe6b57a100 100644 --- a/ext/pdo_firebird/pdo_firebird.c +++ b/ext/pdo_firebird/pdo_firebird.c @@ -27,7 +27,7 @@ #include "php_pdo_firebird_int.h" #include "pdo_firebird_arginfo.h" -zend_class_entry *PdoFirebird_ce; +static zend_class_entry *PdoFirebird_ce; /* {{{ pdo_firebird_deps */ static const zend_module_dep pdo_firebird_deps[] = { diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 7c16b7363a49..a38f87896e62 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -28,7 +28,7 @@ #include "php_pdo_pgsql_int.h" #include "pdo_pgsql_arginfo.h" -zend_class_entry *PdoPgsql_ce; +static zend_class_entry *PdoPgsql_ce; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_pgsql_deps[] = {