From d6a0b3af68a55836ad7d24d4d832898c5b8c2d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 11 Jan 2024 22:58:28 +0100 Subject: [PATCH] Implement PDO driver-specific subclasses RFC: https://wiki.php.net/rfc/pdo_driver_specific_subclasses Closes GH-12804 Co-Authored-By: Danack --- NEWS | 11 + UPGRADING | 27 ++ ext/pdo/pdo.c | 19 +- ext/pdo/pdo_dbh.c | 87 ++++- ext/pdo/pdo_dbh.stub.php | 7 + ext/pdo/pdo_dbh_arginfo.h | 13 +- ext/pdo/pdo_stmt.c | 4 +- ext/pdo/php_pdo.h | 10 +- ext/pdo/php_pdo_driver.h | 5 + ext/pdo/php_pdo_int.h | 3 +- ext/pdo/tests/pdo_test.inc | 6 +- ext/pdo/tests/pdo_uninitialized.phpt | 6 +- ext/pdo_dblib/pdo_dblib.c | 8 +- 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 | 8 +- ext/pdo_firebird/pdo_firebird.stub.php | 34 ++ ext/pdo_firebird/pdo_firebird_arginfo.h | 68 ++++ 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 | 30 +- 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 | 2 +- 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 | 7 +- 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 | 101 +++++- ext/pdo_pgsql/pdo_pgsql.stub.php | 50 +++ ext/pdo_pgsql/pdo_pgsql_arginfo.h | 125 +++++++ ext/pdo_pgsql/pgsql_driver.c | 92 +++-- 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 | 32 ++ ext/pdo_sqlite/config.m4 | 6 + ext/pdo_sqlite/pdo_sqlite.c | 341 +++++++++++++++++- ext/pdo_sqlite/pdo_sqlite.stub.php | 67 ++++ ext/pdo_sqlite/pdo_sqlite_arginfo.h | 111 ++++++ ext/pdo_sqlite/php_pdo_sqlite_int.h | 6 + ext/pdo_sqlite/sqlite_driver.c | 106 +++--- .../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 +- ext/pdo_sqlite/tests/subclasses/config.inc | 16 + ext/pdo_sqlite/tests/subclasses/gc.phpt | 28 ++ .../subclasses/pdo_sqlite_constants.phpt | 26 ++ ...pdo_sqlite_createafunction_trampoline.phpt | 47 +++ .../pdo_sqlite_createaggregate.phpt | 28 ++ .../pdo_sqlite_createaggregate_002.phpt | 25 ++ ...pdo_sqlite_createaggregate_trampoline.phpt | 60 +++ .../pdo_sqlite_createcollation.phpt | 35 ++ ...pdo_sqlite_createcollation_trampoline.phpt | 75 ++++ .../pdo_sqlite_createfunction_with_flags.phpt | 35 ++ .../tests/subclasses/pdosqlite_001.phpt | 36 ++ .../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 | 17 + .../tests/subclasses/pdosqlite_007.phpt | 18 + .../pdosqlite_load_extension_failure.phpt | 44 +++ .../tests/subclasses/stream_test.inc | 46 +++ ext/pgsql/pgsql.c | 2 +- 132 files changed, 3178 insertions(+), 198 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_createafunction_trampoline.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_createaggregate_trampoline.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_trampoline.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/NEWS b/NEWS index b171b9cc2c02e..569c0e6820bfd 100644 --- a/NEWS +++ b/NEWS @@ -60,22 +60,33 @@ OpenSSL: PDO: . Fixed setAttribute and getAttribute (SakiTakamachi) + . Implement PDO driver-specific subclasses RFC (danack, kocsismate) PDO_DBLIB: . Fixed setAttribute and getAttribute (SakiTakamachi) + . Added class PdoDbLib (danack, kocsismate) PDO_FIREBIRD: . Fixed setAttribute and getAttribute (SakiTakamachi) . Feature: Add transaction isolation level and mode settings to pdo_firebird (SakiTakamachi) + . Added class PdoFirebird. (danack, kocsismate) PDO_MYSQL: . Fixed setAttribute and getAttribute (SakiTakamachi) + . Added class PdoMysql. (danack, kocsismate) + +PDO_ODBC: + . Added class PdoOdbc. (danack, kocsismate) PDO_PGSQL: . Fixed GH-12423, DSN credentials being prioritized over the user/password PDO constructor arguments. (SakiTakamachi) . Fixed native float support with pdo_pgsql query results. (Yurunsoft) + . Added class PdoPgsql. (danack, kocsismate) + +PDO_SQLITE: + . Added class PdoSqlite. (danack, kocsismate) PGSQL: . Added the possibility to have no conditions for pg_select. (OmarEmaraDev) diff --git a/UPGRADING b/UPGRADING index 17d508487cfb0..1b4e036a46539 100644 --- a/UPGRADING +++ b/UPGRADING @@ -152,6 +152,33 @@ PHP 8.4 UPGRADE NOTES - Phar: . Added support for the unix timestamp extension for zip archives. +- PDO: + . Added support for driver-specific subclasses. + RFC: https://wiki.php.net/rfc/pdo_driver_specific_subclasses + This RFC adds subclasses for PDO in order to better support + database-specific functionalities. The new classes are + instantiatable either via calling the PDO::connect() method + or by invoking their constructor directly. + +PDO_DBLIB: + . Fixed setAttribute and getAttribute (SakiTakamachi) + . Added PdoDbLib class (danack, kocsismate) + +PDO_FIREBIRD: + . Added class PdoFirebird. + +PDO_MYSQL: + . Added class PdoMysql. + +PDO_ODBC: + . Added class PdoOdbc. + +PDO_PGSQL: + . Added class PdoPgsql. + +PDO_SQLITE: + . Added class PdoSqlite. + - POSIX: . Added constant POSIX_SC_CHILD_MAX . Added constant POSIX_SC_CLK_TCK diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index d2a4741139c82..6aa0f1d6f3dfc 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; @@ -115,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 */ } @@ -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, "The PDO extension must be loaded first in order to load 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 15b9433c7c383..43121dc9b5c6f 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -221,10 +221,64 @@ static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */ } /* }}} */ -/* {{{ */ -PHP_METHOD(PDO, __construct) +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; + + ce_based_on_driver_name = zend_hash_str_find_ptr(&pdo_driver_specific_ce_hash, driver->driver_name, driver->driver_name_len); + + 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) { + if (instanceof_function(ce_based_on_called_object, ce_based_on_driver_name) == false) { + 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)); + 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 { + 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)); + return false; + } + } + + 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", + ZSTR_VAL(called_scope->name), driver->driver_name, ZSTR_VAL(ce_based_on_driver_name->name)); + return false; + } + + /* A driver-specific implementation was instantiated via PDO::__construct() */ + object_init_ex(new_object, ce_based_on_driver_name); + } else { + /* No driver-specific 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 +345,16 @@ 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"); + if (!create_driver_specific_pdo_object(driver, current_scope, new_zval_object)) { + 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 +415,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 +495,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 +1410,7 @@ static HashTable *dbh_get_gc(zend_object *object, zval **gc_data, int *gc_count) } static zend_object_handlers pdo_dbh_object_handlers; + static void pdo_dbh_free_storage(zend_object *std); void pdo_dbh_init(int module_number) 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/pdo_stmt.c b/ext/pdo/pdo_stmt.c index bb2efb4684a1c..48b63344e72bc 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 023372ebc8e6e..8f37990e955d1 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,14 +53,11 @@ 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 \ 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/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index c832284627750..262a908f97641 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -653,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 aa83c6603b5aa..13c2e3d9431b4 100644 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -22,15 +22,14 @@ #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); 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/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_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index 3230f9151bf4d..f4299cf517351 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -27,10 +27,13 @@ #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); +static zend_class_entry *PdoDblib_ce; + static const zend_module_dep pdo_dblib_deps[] = { ZEND_MOD_REQUIRED("pdo") ZEND_MOD_END @@ -201,6 +204,9 @@ PHP_MINIT_FUNCTION(pdo_dblib) return FAILURE; } + PdoDblib_ce = register_class_PdoDblib(pdo_dbh_ce); + PdoDblib_ce->create_object = pdo_dbh_new; + if (FAILURE == php_pdo_register_driver(&pdo_dblib_driver)) { return FAILURE; } @@ -210,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_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..63f8e40be45b7 --- /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..16e0444c11096 --- /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; + #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/pdo_firebird.stub.php b/ext/pdo_firebird/pdo_firebird.stub.php new file mode 100644 index 0000000000000..85c4ab83433c3 --- /dev/null +++ b/ext/pdo_firebird/pdo_firebird.stub.php @@ -0,0 +1,34 @@ +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); + + 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; +} 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..70fc594dfbf5b --- /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('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')"); + +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..e849b8bb27fa8 --- /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('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')"); +$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..291b60c59daba 100644 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -26,6 +26,9 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_mysql.h" #include "php_pdo_mysql_int.h" +#include "pdo_mysql_arginfo.h" + +static zend_class_entry *pdo_mysql_ce; #ifdef COMPILE_DL_PDO_MYSQL #ifdef ZTS @@ -81,6 +84,22 @@ 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; + + ZEND_PARSE_PARAMETERS_NONE(); + + 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 +137,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,7 +151,14 @@ static PHP_MINIT_FUNCTION(pdo_mysql) mysqlnd_reverse_api_register_api(&pdo_mysql_reverse_api); #endif - return php_pdo_register_driver(&pdo_mysql_driver); + pdo_mysql_ce = register_class_PdoMysql(pdo_dbh_ce); + pdo_mysql_ce->create_object = pdo_dbh_new; + + if (php_pdo_register_driver(&pdo_mysql_driver) == FAILURE) { + return FAILURE; + } + + return php_pdo_register_driver_specific_ce(&pdo_mysql_driver, pdo_mysql_ce); } /* }}} */ 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..48a7c525a7016 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; 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..8f87760e5d42a --- /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 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..faa1c2d1712c8 --- /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 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..b7081d5b693a1 100644 --- a/ext/pdo_odbc/pdo_odbc.c +++ b/ext/pdo_odbc/pdo_odbc.c @@ -27,6 +27,8 @@ #include "php_pdo_odbc_int.h" #include "pdo_odbc_arginfo.h" +static zend_class_entry *pdo_odbc_ce; + /* {{{ pdo_odbc_deps[] */ static const zend_module_dep pdo_odbc_deps[] = { ZEND_MOD_REQUIRED("pdo") @@ -105,7 +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); - return SUCCESS; + pdo_odbc_ce = register_class_PdoOdbc(pdo_dbh_ce); + pdo_odbc_ce->create_object = pdo_dbh_new; + + return php_pdo_register_driver_specific_ce(&pdo_odbc_driver, pdo_odbc_ce); } /* }}} */ 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..02be07c560d7e --- /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..a38f87896e62a 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -22,9 +22,13 @@ #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" + +static zend_class_entry *PdoPgsql_ce; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_pgsql_deps[] = { @@ -53,6 +57,94 @@ 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; + 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) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_THROWS(); + } + + 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,7 +157,14 @@ 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); - return php_pdo_register_driver(&pdo_pgsql_driver); + PdoPgsql_ce = register_class_PdoPgsql(pdo_dbh_ce); + PdoPgsql_ce->create_object = pdo_dbh_new; + + if (php_pdo_register_driver(&pdo_pgsql_driver) == FAILURE) { + return FAILURE; + } + + return php_pdo_register_driver_specific_ce(&pdo_pgsql_driver, PdoPgsql_ce); } /* }}} */ 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..525f9cd1c15f7 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; @@ -604,8 +603,8 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) 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(); } @@ -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; @@ -715,8 +719,8 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) 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(); } @@ -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..524b4ad093e08 --- /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 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..036b4649d202f --- /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 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..15b922dd3c0be --- /dev/null +++ b/ext/pdo_pgsql/tests/pdopgsql_003.phpt @@ -0,0 +1,32 @@ +--TEST-- +PdoPgsql getWarningCount +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +escapeIdentifier("This is a quote\"") . "\n"; + +try { + $db->escapeIdentifier("aa\xC3\xC3\xC3"); +} catch (PDOException $e) { + echo $e->getMessage() . "\n"; +} + +--EXPECT-- +"This is a quote""" +SQLSTATE[HY000]: General error: 7 incomplete multibyte character 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..55bfc621291ee 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -21,11 +21,15 @@ #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" + +static zend_class_entry *pdosqlite_ce; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_sqlite_deps[] = { @@ -54,6 +58,334 @@ zend_module_entry pdo_sqlite_module_entry = { ZEND_GET_MODULE(pdo_sqlite) #endif +/* 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); +} + +#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(), "p", &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) + ) { + 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(), "ppl|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) { + zend_error(E_WARNING, "Unable to open blob: %s", sqlite3_errmsg(sqlite_handle)); + 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_sqlite_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) { + 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) { + 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) +{ + pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +PHP_METHOD(PdoSqlite, createCollation) +{ + pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite_collation_callback); +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(pdo_sqlite) { @@ -68,7 +400,14 @@ 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); - return php_pdo_register_driver(&pdo_sqlite_driver); + pdosqlite_ce = register_class_PdoSqlite(pdo_dbh_ce); + pdosqlite_ce->create_object = pdo_dbh_new; + + if (php_pdo_register_driver(&pdo_sqlite_driver) == FAILURE) { + return FAILURE; + } + + return php_pdo_register_driver_specific_ce(&pdo_sqlite_driver, pdosqlite_ce); } /* }}} */ diff --git a/ext/pdo_sqlite/pdo_sqlite.stub.php b/ext/pdo_sqlite/pdo_sqlite.stub.php new file mode 100644 index 0000000000000..5e46b393cca37 --- /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/php_pdo_sqlite_int.h b/ext/pdo_sqlite/php_pdo_sqlite_int.h index c9aeee08a3a59..ae42a03e8a80c 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite_int.h +++ b/ext/pdo_sqlite/php_pdo_sqlite_int.h @@ -78,4 +78,10 @@ enum { PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES }; +typedef int pdo_sqlite_create_collation_callback(void*, int, const void*, int, const void*); + +void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS); +void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS); +void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback); + #endif diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index f83ce43a3ce8b..7cd8880b86099 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,7 @@ 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) -{ - 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 +456,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 +468,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 +495,14 @@ 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) +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; @@ -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, php_sqlite3_func_callback, NULL, NULL); if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); @@ -556,28 +547,16 @@ 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 */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) +{ + pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ -/* 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) +void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) { struct pdo_sqlite_func *func; zend_fcall_info step_fci, fini_fci; @@ -604,8 +583,8 @@ 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, + php_sqlite3_func_step_callback, php_sqlite3_func_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); +} /* }}} */ -/* {{{ 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/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..dc1e4c13a3307 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt @@ -10,19 +10,17 @@ $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'); +$db->query('DROP TABLE test_pdo_sqlite_transaction'); ?> --EXPECTF-- 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_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 new file mode 100644 index 0000000000000..67ae3660e0688 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt @@ -0,0 +1,28 @@ +--TEST-- +PDO_sqlite: Testing createAggregate() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE test_pdo_sqlite_createaggregate (id INT AUTO INCREMENT, name TEXT)'); + +$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 test_pdo_sqlite_createaggregate') as $row) { + var_dump($row); +} + +?> +--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_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 new file mode 100644 index 0000000000000..e65afd4ad910c --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt @@ -0,0 +1,35 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateCollation() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$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")'); + +$db->createCollation('MYCOLLATE', function($a, $b) { return strnatcmp($a, $b); }); + +$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 test_pdo_sqlite_createcollation ORDER BY name'); +foreach ($result as $row) { + echo $row['name'] . "\n"; +} + +?> +--EXPECT-- +1 +2 +10 +1 +10 +2 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..257c2f788b5fd --- /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 new file mode 100644 index 0000000000000..c25801deb5900 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt @@ -0,0 +1,35 @@ +--TEST-- +PDO_sqlite: Testing createFunction() with flags +--EXTENSIONS-- +pdo_sqlite +--SKIPIF-- + +--FILE-- +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 test_pdo_sqlite_createfunction_with_flags') as $row) { + var_dump($row); +} + +?> +--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..26b234c4ee967 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt @@ -0,0 +1,36 @@ +--TEST-- +PdoSqlite basic +--EXTENSIONS-- +pdo_sqlite +--FILE-- +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")'); + +$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); +} + +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..61c4dd6d31839 --- /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 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..860848dcff867 --- /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..29cfc2e175035 --- /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, 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 new file mode 100644 index 0000000000000..1095b9a372d02 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_006.phpt @@ -0,0 +1,17 @@ +--TEST-- +Calling PDO::connect through the wrong classname +--EXTENSIONS-- +pdo_sqlite +--FILE-- + +--EXPECT-- +OK 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..899b6fb412842 --- /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, either PdoSqlite::connect() or PDO::connect() must be called 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..8a3abf66dcf15 --- /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"); 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; }