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 15b9433c7c383..d8e8c6f2e6d40 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 9b32a62790fd8..7fcec0226b0ba 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 cad4bdef03056..2d2471baff5af 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 023372ebc8e6e..4de505a391b03 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 c832284627750..71c4ff989ccc3 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 aa83c6603b5aa..a276a7972e74a 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 22f653e238261..5138452ff9c51 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 3230f9151bf4d..04d504b0b8aca 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 0000000000000..569a1b47b0166 --- /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 50abb5d1ebaf1..5b5c35252b0f3 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 c871f681be752..80bc8ab533f02 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 bbe8cb1565915..afc87dd05d7c7 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 fa101c8622689..03f8456c78765 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 2d9fbc7a4c7bf..2fab8dcda742f 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 30e2bf58bebc3..9e735ff877152 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 1ec340a8c051b..cd244ba1ec333 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 13db577f90487..956f109095c34 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 e4d46c23f3e34..218088b1e11f8 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 8222a1ee39c0c..86bd64a40781c 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 b88e4ad9c526a..9128b8c254049 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 936f5229567ff..09a8f1e8f0e11 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 a7d30eb493846..aee046111573e 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 75dedf8a77d06..d7114e8fa09d6 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 1bd439782ffbf..b2f96f8ec1c98 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 9ee39b09ac703..e4f65048dd897 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 a025dc460adda..ec858cf351c57 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 cec305e276ad5..aa05606a93269 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 53c2bb6c006a6..44dd8f4a86dc3 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 6a4da2bd414d1..f0162138062cf 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 1855841d4a455..05d1bbf82dd81 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 0000000000000..06b91b9336519 --- /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 0000000000000..825abfaeb7d80 --- /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 f83a9e643f171..86c94877c63c1 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 951e3b3e59afc..a58a969c6f1b0 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 0000000000000..e8804277a5312 --- /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 5ba61e11f7a8d..5b431859bf85d 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 b8d4e542d9c14..0ccc38f623929 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 6a7fc6ff7f77d..0889403f419b8 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 f7fa25ba1c834..04e14a7ce312e 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 b1da6789ba84d..6accc81e585cd 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 2f8fa09a93635..8cbeab61b85ae 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 545e6feaf6948..c7b1a327c15c5 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 0f72ce1b14ff8..b86f40c7e2289 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 9cda4b7ef09a0..c54779973206f 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 5d5493ddb2a52..182cf920737e2 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 177fb08b35faa..e63ca1f6a7c72 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 fcb60c9ad7f56..4427e8c972bc3 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 724e9ec89ee5c..8a59e7a7a0308 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 fe233f334a7af..cc97b4855e727 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 dba8ced3814ce..6e4e40db0209a 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 8b6301d647414..cd92c36d98ecc 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 f258fb6f930c1..1ca78bfb1fbef 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 475144bf6b7dd..8df6f84d692f2 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 d070d61a1e70a..b042263f6d19f 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 c5a772597748c..3d9976f0f955c 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 1b5a3e240eb45..f8a3e80c859aa 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 97f3327db698a..e94f8eeed1405 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 27f83f6ebf004..432cf605f7a48 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 9478eb024c531..ceb1b0ff57f35 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 0000000000000..d34bfa1943f99 --- /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 0000000000000..cf3836ec8b432 --- /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 d435cc10a249c..e7e8dcd6c3c2e 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 23008fa0d99d2..0ccf9ab81d372 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 3f5c9bcca7013..7614feccc7ae6 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 3f3ccb402ec96..5b6852286aad2 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 7aa39ca35a6bd..4768a861fe122 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 0000000000000..a1b9a13a80b66 --- /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 0000000000000..f3ef371147f00 --- /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 984eab060945a..a783d812ea43d 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 4f7a2316a1482..5d7fd83c2d8e7 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 0000000000000..37a5644031983 --- /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 0000000000000..1be1739e1ee0b --- /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 0000000000000..4ba538b969dd4 --- /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 8e090a397f061..1bb30e9a0aa4b 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 745be283375da..c83d6f29f06f2 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 8a26d600d72ff..4816748c6474f 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 0000000000000..70985dcb6d3a3 --- /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 0000000000000..fd35477450264 --- /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 582915a5a34f3..72c6106cdfa59 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 0000000000000..eb7707147a192 --- /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 8205938a43821..9aa0ad220dab9 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 f45718f0c96a2..303aff6006058 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 0000000000000..40c6be5ba7640 --- /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 0000000000000..a4f676295d63a --- /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 0000000000000..2f7d530f04d08 --- /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 64311eaa6436e..2231ff5c6de88 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 6da7708576368..72ff15fb7df2f 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 0000000000000..23f7f5dd1a252 --- /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 0000000000000..3bc88411b4392 --- /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 0000000000000..da48a02c7cfe8 --- /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 0000000000000..cc4763e4dbd91 --- /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 0000000000000..e4152c06e81e8 --- /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 0000000000000..80344de0a59f2 --- /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 0000000000000..d29e79fc903df --- /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 0000000000000..512feb33e7cd3 --- /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 0000000000000..56098a999534c --- /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 0000000000000..9a14d892977dc --- /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 0000000000000..a2b0b7b4fb861 --- /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 0000000000000..bf8598bf29490 --- /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 0000000000000..1bc8e522dcc74 --- /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 0000000000000..355e4271ea4b3 --- /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 0000000000000..462f22cdb67b4 --- /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 0000000000000..d4bee82fbc492 --- /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 d8e8c6f2e6d40..29933ad6ace18 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 06b91b9336519..f4e98fb8ec7ec 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 825abfaeb7d80..fd3d2bbb94238 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 37a5644031983..8f87760e5d42a 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 72c6106cdfa59..fdf3ebcd98204 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 40c6be5ba7640..524b4ad093e08 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 a4f676295d63a..24f198f873486 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 2f7d530f04d08..5b86fe1c54b94 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 72ff15fb7df2f..2dea45c8725a4 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 98240b5943fb1..3ecc0ccddc991 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 bf1cac2a49bd0..9e4751e33aa01 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 9ea8edc8ad7fb..bda7ab0b41555 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 bc3def2bf68bb..f9312bcee69e9 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 2237e36722d14..7e55ed60d5f1d 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 26e6cd6bd3704..2dba9eeb477a7 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 cc4763e4dbd91..ef1f342554673 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 512feb33e7cd3..770fa8e4552ca 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 56098a999534c..61c4dd6d31839 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 bf8598bf29490..29cfc2e175035 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 1bc8e522dcc74..79626edc6a19b 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 355e4271ea4b3..899b6fb412842 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 fba4d70e9a5bf..c393f3c4a1801 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 d2a4741139c82..450c65ae60dca 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 29933ad6ace18..bf8e027f4e3cb 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 71c4ff989ccc3..262a908f97641 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 a276a7972e74a..13c2e3d9431b4 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 04d504b0b8aca..149a8824bf08a 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 b1bc0cd7c688e..3c2045f6fa79b 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 d34bfa1943f99..10bdd3e45b680 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 cf3836ec8b432..ea48263bb57f6 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 4768a861fe122..d7f7dbaf6c5ad 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 a783d812ea43d..48a7c525a7016 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 1be1739e1ee0b..41bc2a200a17f 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 1bb30e9a0aa4b..2d6249afb76cc 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 fdf3ebcd98204..b702f9a4f6ca5 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 2dea45c8725a4..bce9681ed1076 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 2dba9eeb477a7..dc1e4c13a3307 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 e8804277a5312..85c4ab83433c3 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 6122d6ebbe449..e8b297a4b7df3 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 450c65ae60dca..6aa0f1d6f3dfc 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 bf8e027f4e3cb..a306ce05e1fc5 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 ea48263bb57f6..f9e4a026f5af2 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 bce9681ed1076..801659ac2b61b 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 0000000000000..29eac9fae0ad6 --- /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 ef1f342554673..67ae3660e0688 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 0000000000000..0381f428e33c9 --- /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 80344de0a59f2..461e55d98128b 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 0000000000000..4b98432b7d711 --- /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 d29e79fc903df..c25801deb5900 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 770fa8e4552ca..26b234c4ee967 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 a306ce05e1fc5..4c0d8965f2664 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 2d0010eb747a8..d11b7fbd57f25 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 4de505a391b03..8f37990e955d1 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 2e229a2bada34..e3b4c96af811f 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 b702f9a4f6ca5..e300ff39e34a0 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 9aa0ad220dab9..525f9cd1c15f7 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 801659ac2b61b..8f2e82e0cd92f 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 e300ff39e34a0..7c16b7363a493 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 5b86fe1c54b94..15b922dd3c0be 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 8f2e82e0cd92f..a4034d782b2a8 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 4c0d8965f2664..43121dc9b5c6f 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 f4e98fb8ec7ec..63f8e40be45b7 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 fd3d2bbb94238..16e0444c11096 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 10bdd3e45b680..70fc594dfbf5b 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 f9e4a026f5af2..e849b8bb27fa8 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 d7f7dbaf6c5ad..291b60c59daba 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 41bc2a200a17f..faa1c2d1712c8 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 2d6249afb76cc..b7081d5b693a1 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 fd35477450264..02be07c560d7e 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 24f198f873486..036b4649d202f 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 a4034d782b2a8..178a077d40301 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 23f7f5dd1a252..5e46b393cca37 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 2804dd88c4b3d..eb45b8041e01d 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 c9aeee08a3a59..d92734af02028 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 f83ce43a3ce8b..8a8ad56e84e51 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 461e55d98128b..e65afd4ad910c 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 4b98432b7d711..257c2f788b5fd 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 9a14d892977dc..860848dcff867 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 79626edc6a19b..1095b9a372d02 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 462f22cdb67b4..8a3abf66dcf15 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 178a077d40301..55bfc621291ee 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 d92734af02028..ae42a03e8a80c 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 8a8ad56e84e51..7cd8880b86099 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 149a8824bf08a..f4299cf517351 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 3c2045f6fa79b..99fe6b57a1002 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 7c16b7363a493..a38f87896e62a 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[] = {