diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 692f187a04df8..f7a8d85eb0f4f 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -218,10 +218,42 @@ static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */ } /* }}} */ -/* {{{ */ -PHP_METHOD(PDO, __construct) + +#define MAX_PDO_SUB_CLASSES 64 +static unsigned int number_of_pdo_driver_class_entries = 0; +static pdo_driver_class_entry *pdo_driver_class_entries[MAX_PDO_SUB_CLASSES]; + +// It would be possible remove this and roll it into the standard driver class entries +// I chose not to do it at this time, as that would break existing PDO extensions +void pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry) +{ + if (number_of_pdo_driver_class_entries >= MAX_PDO_SUB_CLASSES) { + // TODO - return false + } + + pdo_driver_class_entries[number_of_pdo_driver_class_entries] = driver_class_entry; + number_of_pdo_driver_class_entries += 1; +} + + +static +void create_specific_pdo_object(zval *new_object, const char *driver_name) +{ + for (int i=0; i < number_of_pdo_driver_class_entries; i += 1) { + pdo_driver_class_entry *driver_class_entry = pdo_driver_class_entries[i]; + if (strcmp(driver_class_entry->driver_name, driver_name) == 0) { + object_init_ex(new_object, driver_class_entry->driver_ce); + return; + } + } + + // No specific DB implementation found + object_init_ex(new_object, pdo_dbh_ce); +} + +static +void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zval *object, zval *new_zval_object) { - zval *object = ZEND_THIS; pdo_dbh_t *dbh = NULL; bool is_persistent = 0; char *data_source; @@ -288,7 +320,19 @@ PHP_METHOD(PDO, __construct) RETURN_THROWS(); } - dbh = Z_PDO_DBH_P(object); + if (object == NULL) { + ZEND_ASSERT((driver->driver_name != NULL) && "PDO driver name is null"); + create_specific_pdo_object(new_zval_object, driver->driver_name); + + if (new_zval_object == NULL) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Failed to create specific PDO class"); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(new_zval_object); + } else { + dbh = Z_PDO_DBH_P(object); + } /* is this supposed to be a persistent connection ? */ if (options) { @@ -429,8 +473,24 @@ PHP_METHOD(PDO, __construct) zend_throw_exception(pdo_exception_ce, "Constructor failed", 0); } } + +/* {{{ */ +PHP_METHOD(PDO, __construct) +{ + zval *object = ZEND_THIS; + internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, object, NULL); +} /* }}} */ + +/* {{{ */ +PHP_METHOD(PDO, connect) +{ + internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, return_value); +} +/* }}} */ + + static zval *pdo_stmt_instantiate(pdo_dbh_t *dbh, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */ { if (!Z_ISUNDEF_P(ctor_args)) { @@ -1323,6 +1383,8 @@ static HashTable *dbh_get_gc(zend_object *object, zval **gc_data, int *gc_count) } static zend_object_handlers pdo_dbh_object_handlers; +static zend_object_handlers pdosqlite_dbh_object_handlers; + static void pdo_dbh_free_storage(zend_object *std); void pdo_dbh_init(void) @@ -1423,6 +1485,14 @@ void pdo_dbh_init(void) REGISTER_PDO_CLASS_CONST_LONG("CURSOR_FWDONLY", (zend_long)PDO_CURSOR_FWDONLY); REGISTER_PDO_CLASS_CONST_LONG("CURSOR_SCROLL", (zend_long)PDO_CURSOR_SCROLL); + + + memcpy(&pdosqlite_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + pdosqlite_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std); + pdosqlite_dbh_object_handlers.free_obj = pdo_dbh_free_storage; + pdosqlite_dbh_object_handlers.get_method = dbh_method_get; + pdosqlite_dbh_object_handlers.compare = zend_objects_not_comparable; + pdosqlite_dbh_object_handlers.get_gc = dbh_get_gc; } static void dbh_free(pdo_dbh_t *dbh, bool free_persistent) diff --git a/ext/pdo/pdo_dbh.stub.php b/ext/pdo/pdo_dbh.stub.php index 7ff52c9696a07..a2589829b7be5 100644 --- a/ext/pdo/pdo_dbh.stub.php +++ b/ext/pdo/pdo_dbh.stub.php @@ -7,6 +7,13 @@ class PDO { public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null) {} + public static function connect( + string $dsn, + ?string $username = null, + ?string $password = null, + ?array $options = null + ): PDO|PDOSqlite {} + /** @tentative-return-type */ public function beginTransaction(): bool {} diff --git a/ext/pdo/pdo_dbh_arginfo.h b/ext/pdo/pdo_dbh_arginfo.h index 11d6fac08b577..14fde6d9cbb26 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: 7d10dbdfd55eb4a4dc779cbf4fa000cdf4fb3539 */ + * Stub hash: 9ad9db0f01bb0f36d1ff11c15428799d38a1a22f */ 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_OBJ_TYPE_MASK_EX(arginfo_class_PDO_connect, 0, 1, PDO|PDOSqlite, 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) diff --git a/ext/pdo/php_pdo.h b/ext/pdo/php_pdo.h index 9c278cd40a5cf..85b4282569eab 100644 --- a/ext/pdo/php_pdo.h +++ b/ext/pdo/php_pdo.h @@ -22,6 +22,9 @@ extern zend_module_entry pdo_module_entry; #define phpext_pdo_ptr &pdo_module_entry +extern zend_class_entry *pdo_dbh_ce; +extern zend_object *pdo_dbh_new(zend_class_entry *ce); + #include "php_version.h" #define PHP_PDO_VERSION PHP_VERSION diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index c832284627750..ce17ead86cc62 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -215,6 +215,16 @@ typedef struct { } pdo_driver_t; +// NOTE - This separate struct, could be rolled it into pdo_driver_t +// I chose not to, as that would cause BC break and require a lot of +// downstream work. +typedef struct { + char *driver_name; + zend_class_entry *driver_ce; +} pdo_driver_class_entry; + +void pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry); + /* {{{ methods for a database handle */ /* close or otherwise disconnect the database */ diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index c3d2fe00a1d70..f50925325ea0f 100644 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -28,9 +28,7 @@ int php_pdo_list_entry(void); void pdo_dbh_init(void); 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_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index e0f204cb39be9..7b1f7b5d63477 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -27,10 +27,14 @@ #include "php_pdo_dblib.h" #include "php_pdo_dblib_int.h" #include "zend_exceptions.h" +#include "pdo_dblib_arginfo.h" ZEND_DECLARE_MODULE_GLOBALS(dblib) static PHP_GINIT_FUNCTION(dblib); +zend_class_entry *PdoDblib_ce; +static pdo_driver_class_entry PdoDblib_pdo_driver_class_entry; + static const zend_module_dep pdo_dblib_deps[] = { ZEND_MOD_REQUIRED("pdo") ZEND_MOD_END @@ -201,6 +205,13 @@ PHP_MINIT_FUNCTION(pdo_dblib) return FAILURE; } + PdoDblib_ce = register_class_PdoDblib(pdo_dbh_ce); + PdoDblib_ce->create_object = pdo_dbh_new; + + PdoDblib_pdo_driver_class_entry.driver_name = "dblib"; + PdoDblib_pdo_driver_class_entry.driver_ce = PdoDblib_ce; + pdo_register_driver_specific_class(&PdoDblib_pdo_driver_class_entry); + if (FAILURE == php_pdo_register_driver(&pdo_dblib_driver)) { return FAILURE; } diff --git a/ext/pdo_dblib/pdo_dblib.stub.php b/ext/pdo_dblib/pdo_dblib.stub.php new file mode 100644 index 0000000000000..2556fb639cfa1 --- /dev/null +++ b/ext/pdo_dblib/pdo_dblib.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_dblib/tests/config_functions.inc b/ext/pdo_dblib/tests/config_functions.inc new file mode 100644 index 0000000000000..cee246c9a7ff6 --- /dev/null +++ b/ext/pdo_dblib/tests/config_functions.inc @@ -0,0 +1,24 @@ +query('drop table if exists #foobar;'); + +$db->query("create table #foobar(name varchar(32)); "); +$db->query("insert into #foobar values('PHP');"); +$db->query("insert into #foobar values('PHP6');"); + +foreach ($db->query('SELECT name FROM #foobar') as $row) { + var_dump($row); +} + +$db->query('drop table #foobar;'); + +echo "Fin."; +?> +--EXPECT-- +PdoDblib class exists. +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/subclasses/pdodblib_002.phpt b/ext/pdo_dblib/tests/subclasses/pdodblib_002.phpt new file mode 100644 index 0000000000000..43c6072fc2d53 --- /dev/null +++ b/ext/pdo_dblib/tests/subclasses/pdodblib_002.phpt @@ -0,0 +1,53 @@ +--TEST-- +PdoDblib create through PDO::connect +--EXTENSIONS-- +pdo +--FILE-- +query('drop table if exists #test;'); + +$db->query("create table #test(name varchar(32)); "); +$db->query("insert into #test values('PHP');"); +$db->query("insert into #test values('PHP6');"); + +foreach ($db->query('SELECT name FROM #test') as $row) { + var_dump($row); +} + +$db->query('drop table #test;'); + +echo "Fin."; +?> +--EXPECT-- +PdoDblib class exists. +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/pdo_firebird.c b/ext/pdo_firebird/pdo_firebird.c index 9b8ca1394bee1..4494617a441a8 100644 --- a/ext/pdo_firebird/pdo_firebird.c +++ b/ext/pdo_firebird/pdo_firebird.c @@ -25,6 +25,10 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_firebird.h" #include "php_pdo_firebird_int.h" +#include "pdo_firebird_arginfo.h" + +zend_class_entry *PdoFirebird_ce; +static pdo_driver_class_entry PdoFirebird_pdo_driver_class_entry; /* {{{ pdo_firebird_deps */ static const zend_module_dep pdo_firebird_deps[] = { @@ -62,6 +66,13 @@ PHP_MINIT_FUNCTION(pdo_firebird) /* {{{ */ return FAILURE; } + PdoFirebird_ce = register_class_PdoFirebird(pdo_dbh_ce); + PdoFirebird_ce->create_object = pdo_dbh_new; + + PdoFirebird_pdo_driver_class_entry.driver_name = "firebird"; + PdoFirebird_pdo_driver_class_entry.driver_ce = PdoFirebird_ce; + pdo_register_driver_specific_class(&PdoFirebird_pdo_driver_class_entry); + #ifdef ZEND_SIGNALS /* firebird replaces some signals at runtime, suppress warnings. */ SIGG(check) = 0; diff --git a/ext/pdo_firebird/pdo_firebird.stub.php b/ext/pdo_firebird/pdo_firebird.stub.php new file mode 100644 index 0000000000000..9b1501b58ed5e --- /dev/null +++ b/ext/pdo_firebird/pdo_firebird.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_firebird/tests/config_functions.inc b/ext/pdo_firebird/tests/config_functions.inc new file mode 100644 index 0000000000000..79e9a4ce6481b --- /dev/null +++ b/ext/pdo_firebird/tests/config_functions.inc @@ -0,0 +1,23 @@ +query('RECREATE TABLE foobar (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); +$db->query("INSERT INTO foobar VALUES (1, 'PHP')"); +$db->query("INSERT INTO foobar VALUES (2, 'PHP6')"); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoFirebird class exists. +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/subclasses/pdofirebird_002.phpt b/ext/pdo_firebird/tests/subclasses/pdofirebird_002.phpt new file mode 100644 index 0000000000000..632783aee1f6c --- /dev/null +++ b/ext/pdo_firebird/tests/subclasses/pdofirebird_002.phpt @@ -0,0 +1,59 @@ +--TEST-- +PDO_firebird connect through PDO::connect +--EXTENSIONS-- +PDO_odbc +--FILE-- +query('RECREATE TABLE test (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); + +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoFirebird class exists. +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/config.m4 b/ext/pdo_mysql/config.m4 index 7f09aabefa73a..c1047dd1488f9 100644 --- a/ext/pdo_mysql/config.m4 +++ b/ext/pdo_mysql/config.m4 @@ -99,7 +99,7 @@ if test "$PHP_PDO_MYSQL" != "no"; then AC_DEFINE_UNQUOTED(PDO_MYSQL_UNIX_ADDR, "$PDO_MYSQL_SOCKET", [ ]) fi - PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c mysql_driver.c mysql_statement.c, $ext_shared,,-I$pdo_cv_inc_path -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) + PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c pdo_mysql_class.c mysql_driver.c mysql_statement.c, $ext_shared,,-I$pdo_cv_inc_path -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_EXTENSION_DEP(pdo_mysql, pdo) diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c index 2d94b71fef782..83d2e9db5652a 100644 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -26,6 +26,10 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_mysql.h" #include "php_pdo_mysql_int.h" +#include "pdo_mysql_arginfo.h" + +zend_class_entry *pdomysql_ce; +static pdo_driver_class_entry pdomysql_pdo_driver_class_entry; #ifdef COMPILE_DL_PDO_MYSQL #ifdef ZTS @@ -132,6 +136,13 @@ static PHP_MINIT_FUNCTION(pdo_mysql) mysqlnd_reverse_api_register_api(&pdo_mysql_reverse_api); #endif + pdomysql_ce = register_class_PdoMysql(pdo_dbh_ce); + pdomysql_ce->create_object = pdo_dbh_new; + + pdomysql_pdo_driver_class_entry.driver_name = "mysql"; + pdomysql_pdo_driver_class_entry.driver_ce = pdomysql_ce; + pdo_register_driver_specific_class(&pdomysql_pdo_driver_class_entry); + return php_pdo_register_driver(&pdo_mysql_driver); } /* }}} */ diff --git a/ext/pdo_mysql/pdo_mysql.stub.php b/ext/pdo_mysql/pdo_mysql.stub.php new file mode 100644 index 0000000000000..8759d98ba1aa4 --- /dev/null +++ b/ext/pdo_mysql/pdo_mysql.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_mysql/pdo_mysql_class.c b/ext/pdo_mysql/pdo_mysql_class.c new file mode 100644 index 0000000000000..083dcb8b3a845 --- /dev/null +++ b/ext/pdo_mysql/pdo_mysql_class.c @@ -0,0 +1,47 @@ + +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Danack + +----------------------------------------------------------------------+ +*/ + +/* The PDO Database Handle Class */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "../pdo/php_pdo.h" +#include "../pdo/php_pdo_driver.h" +#include "../pdo/php_pdo_int.h" +#include "php_pdo_mysql.h" +#include "php_pdo_mysql_int.h" + +/* {{{ proto string PDO::mysqlGetWarningCount() + Returns the number of SQL warnings during the execution of the last statement +*/ +PHP_METHOD(PdoMysql, getWarningCount) +{ + pdo_dbh_t *dbh; + pdo_mysql_db_handle *H; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_mysql_db_handle *)dbh->driver_data; + RETURN_LONG(mysql_warning_count(H->server)); +} +/* }}} */ diff --git a/ext/pdo_mysql/tests/config_functions.inc b/ext/pdo_mysql/tests/config_functions.inc new file mode 100644 index 0000000000000..0f6019e0b7b7c --- /dev/null +++ b/ext/pdo_mysql/tests/config_functions.inc @@ -0,0 +1,24 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + + return $db; + } + + static function createTestTable($db, $engine = null) { if (!$engine) $engine = PDO_MYSQL_TEST_ENGINE; diff --git a/ext/pdo_mysql/tests/pdo_mysql_interface.phpt b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt index aeb441bc0363a..5b6db319261b8 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_interface.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt @@ -17,6 +17,7 @@ if (false == MySQLPDOTest::detect_transactional_mysql_engine($db)) $expected = array( '__construct' => true, + 'connect' => true, 'prepare' => true, 'beginTransaction' => true, 'commit' => true, diff --git a/ext/pdo_mysql/tests/subclasses/pdomysql_001.phpt b/ext/pdo_mysql/tests/subclasses/pdomysql_001.phpt new file mode 100644 index 0000000000000..b4c08497882f7 --- /dev/null +++ b/ext/pdo_mysql/tests/subclasses/pdomysql_001.phpt @@ -0,0 +1,48 @@ +--TEST-- +PDO_mysql subclass basic +--EXTENSIONS-- +pdo_mysql +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoMysql class exists. +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/subclasses/pdomysql_002.phpt b/ext/pdo_mysql/tests/subclasses/pdomysql_002.phpt new file mode 100644 index 0000000000000..081c9329aafe7 --- /dev/null +++ b/ext/pdo_mysql/tests/subclasses/pdomysql_002.phpt @@ -0,0 +1,60 @@ +--TEST-- +PDO_mysql connect through PDO::connect +--EXTENSIONS-- +pdo_mysql +--FILE-- +query('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE IF NOT EXISTS test(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoMysql class exists. +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/subclasses/pdomysql_003.phpt b/ext/pdo_mysql/tests/subclasses/pdomysql_003.phpt new file mode 100644 index 0000000000000..fdf9966f10925 --- /dev/null +++ b/ext/pdo_mysql/tests/subclasses/pdomysql_003.phpt @@ -0,0 +1,32 @@ +--TEST-- +PDO_mysql getWarningCount +--EXTENSIONS-- +pdo_mysql +--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_oci/pdo_oci.c b/ext/pdo_oci/pdo_oci.c index 125f4ce5704bb..7bb4da0a556c3 100644 --- a/ext/pdo_oci/pdo_oci.c +++ b/ext/pdo_oci/pdo_oci.c @@ -28,6 +28,11 @@ #ifdef ZTS #include #endif +#include "pdo_oci_arginfo.h" + +zend_class_entry *pdooci_ce; + +static pdo_driver_class_entry pdooci_pdo_driver_class_entry; /* {{{ pdo_oci_module_entry */ @@ -94,6 +99,13 @@ PHP_MINIT_FUNCTION(pdo_oci) // Defer OCI init to PHP_RINIT_FUNCTION because with php-fpm, // NLS_LANG is not yet available here. + pdooci_ce = register_class_PdoOci(pdo_dbh_ce); + pdooci_ce->create_object = pdo_dbh_new; + + pdooci_pdo_driver_class_entry.driver_name = "oci"; + pdooci_pdo_driver_class_entry.driver_ce = pdooci_ce; + pdo_register_driver_specific_class(&pdooci_pdo_driver_class_entry); + #ifdef ZTS pdo_oci_env_mutex = tsrm_mutex_alloc(); #endif diff --git a/ext/pdo_oci/pdo_oci.stub.php b/ext/pdo_oci/pdo_oci.stub.php new file mode 100644 index 0000000000000..b1db5d64c4075 --- /dev/null +++ b/ext/pdo_oci/pdo_oci.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_oci/tests/config_functions.inc b/ext/pdo_oci/tests/config_functions.inc new file mode 100644 index 0000000000000..de872fc79617c --- /dev/null +++ b/ext/pdo_oci/tests/config_functions.inc @@ -0,0 +1,23 @@ +query('CREATE TABLE IF NOT EXISTS foobar(id NUMBER, name VARCHAR2(6))'); + +$db->query("INSERT INTO foobar VALUES (NULL, 'PHP')"); +$db->query("INSERT INTO foobar VALUES (NULL, 'PHP6')"); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoOci class exists. +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_oci/tests/subclasses/pdooci_002.phpt b/ext/pdo_oci/tests/subclasses/pdooci_002.phpt new file mode 100644 index 0000000000000..6b1e0141bf393 --- /dev/null +++ b/ext/pdo_oci/tests/subclasses/pdooci_002.phpt @@ -0,0 +1,59 @@ +--TEST-- +PDO_oci connect through PDO::connect +--EXTENSIONS-- +pdo_oci +--FILE-- +query('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE IF NOT EXISTS test(id NUMBER NOT NULL PRIMARY KEY, name VARCHAR2(10))'); +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoOci class exists. +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_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c index 12874c8bd8747..f015b00f8fb2c 100644 --- a/ext/pdo_odbc/pdo_odbc.c +++ b/ext/pdo_odbc/pdo_odbc.c @@ -25,6 +25,10 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_odbc.h" #include "php_pdo_odbc_int.h" +#include "pdo_odbc_arginfo.h" + +zend_class_entry *pdoodbc_ce; +static pdo_driver_class_entry pdoodbc_pdo_driver_class_entry; /* {{{ pdo_odbc_deps[] */ static const zend_module_dep pdo_odbc_deps[] = { @@ -102,6 +106,13 @@ PHP_MINIT_FUNCTION(pdo_odbc) REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_DRIVER", SQL_CUR_USE_DRIVER); REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_ODBC", SQL_CUR_USE_ODBC); + pdoodbc_ce = register_class_PdoOdbc(pdo_dbh_ce); + pdoodbc_ce->create_object = pdo_dbh_new; + + pdoodbc_pdo_driver_class_entry.driver_name = "odbc"; + pdoodbc_pdo_driver_class_entry.driver_ce = pdoodbc_ce; + pdo_register_driver_specific_class(&pdoodbc_pdo_driver_class_entry); + return SUCCESS; } /* }}} */ diff --git a/ext/pdo_odbc/pdo_odbc.stub.php b/ext/pdo_odbc/pdo_odbc.stub.php new file mode 100644 index 0000000000000..81f4e793e350a --- /dev/null +++ b/ext/pdo_odbc/pdo_odbc.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_odbc/tests/config_functions.inc b/ext/pdo_odbc/tests/config_functions.inc new file mode 100644 index 0000000000000..99fee513d905d --- /dev/null +++ b/ext/pdo_odbc/tests/config_functions.inc @@ -0,0 +1,27 @@ +query('CREATE TABLE IF NOT EXISTS foobar (id INT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoOdbc class exists. +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/subclasses/pdoodbc_002.phpt b/ext/pdo_odbc/tests/subclasses/pdoodbc_002.phpt new file mode 100644 index 0000000000000..cf362dac70791 --- /dev/null +++ b/ext/pdo_odbc/tests/subclasses/pdoodbc_002.phpt @@ -0,0 +1,59 @@ +--TEST-- +PDO_mysql connect through PDO::connect +--EXTENSIONS-- +PDO_odbc +--FILE-- +query('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE IF NOT EXISTS test(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoOdbc class exists. +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/config.m4 b/ext/pdo_pgsql/config.m4 index 80ffd97ac2ece..624b561a9410d 100644 --- a/ext/pdo_pgsql/config.m4 +++ b/ext/pdo_pgsql/config.m4 @@ -78,6 +78,6 @@ if test "$PHP_PDO_PGSQL" != "no"; then PHP_CHECK_PDO_INCLUDES - PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c, $ext_shared,,-I$pdo_cv_inc_path) + PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pdo_pgsql_class.c pgsql_driver.c pgsql_statement.c, $ext_shared,,-I$pdo_cv_inc_path) PHP_ADD_EXTENSION_DEP(pdo_pgsql, pdo) fi diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 582915a5a34f3..db76df24078ca 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -25,6 +25,16 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_pgsql.h" #include "php_pdo_pgsql_int.h" +#include "pdo_pgsql_arginfo.h" + +#define REGISTER_PdoPgsql_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_PdoPgsql_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); + +zend_class_entry *PdoPgsql_ce; +static pdo_driver_class_entry PdoPgsql_pdo_driver_class_entry; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_pgsql_deps[] = { @@ -65,6 +75,13 @@ PHP_MINIT_FUNCTION(pdo_pgsql) REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long)PGSQL_TRANSACTION_INERROR); REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long)PGSQL_TRANSACTION_UNKNOWN); + PdoPgsql_ce = register_class_PdoPgsql(pdo_dbh_ce); + PdoPgsql_ce->create_object = pdo_dbh_new; + + PdoPgsql_pdo_driver_class_entry.driver_name = "pgsql"; + PdoPgsql_pdo_driver_class_entry.driver_ce = PdoPgsql_ce; + pdo_register_driver_specific_class(&PdoPgsql_pdo_driver_class_entry); + return php_pdo_register_driver(&pdo_pgsql_driver); } /* }}} */ diff --git a/ext/pdo_pgsql/pdo_pgsql.stub.php b/ext/pdo_pgsql/pdo_pgsql.stub.php new file mode 100644 index 0000000000000..af67a7638d720 --- /dev/null +++ b/ext/pdo_pgsql/pdo_pgsql.stub.php @@ -0,0 +1,65 @@ +ce_flags |= 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_class_constant_ex(class_entry, const_ATTR_DISABLE_PREPARES_name, &const_ATTR_DISABLE_PREPARES_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_TRANSACTION_IDLE_name, &const_TRANSACTION_IDLE_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_TRANSACTION_ACTIVE_name, &const_TRANSACTION_ACTIVE_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_TRANSACTION_INTRANS_name, &const_TRANSACTION_INTRANS_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_TRANSACTION_INERROR_name, &const_TRANSACTION_INERROR_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_TRANSACTION_UNKNOWN_name, &const_TRANSACTION_UNKNOWN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TRANSACTION_UNKNOWN_name); + + return class_entry; +} diff --git a/ext/pdo_pgsql/pdo_pgsql_class.c b/ext/pdo_pgsql/pdo_pgsql_class.c new file mode 100644 index 0000000000000..d78f94c5af27f --- /dev/null +++ b/ext/pdo_pgsql/pdo_pgsql_class.c @@ -0,0 +1,127 @@ + +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Danack + +----------------------------------------------------------------------+ +*/ + +/* The PDO Database Handle Class */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "../pdo/php_pdo.h" +#include "../pdo/php_pdo_driver.h" +#include "../pdo/php_pdo_int.h" +#include "php_pdo_pgsql.h" +#include "php_pdo_pgsql_int.h" + +/* {{{ Escape a identifier for insertion into a text field */ +PHP_METHOD(PdoPgsql, escapeIdentifier) +{ + zend_string *from = NULL; + char *tmp; + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + /* Obtain db Handle */ + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + tmp = PQescapeIdentifier(H->server, ZSTR_VAL(from), ZSTR_LEN(from)); + + if (!tmp) { + // TODO - exception + php_error_docref(NULL, E_WARNING,"Failed to escape identifier"); + RETURN_FALSE; + } + + RETVAL_STRING(tmp); + PQfreemem(tmp); +} +/* }}} */ + + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyFromArray) +{ + pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyFromFile) +{ + pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyToFile) +{ + pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); + +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyToArray) +{ + pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobCreate) +{ + pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobOpen) +{ + pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobUnlink) +{ + pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PdoPgsql, getNotify) +{ + pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PdoPgsql, getPid) +{ + pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 54bf7ede6bfaa..41d99ad81ab14 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -550,8 +550,7 @@ static bool pgsql_handle_rollback(pdo_dbh_t *dbh) return pdo_pgsql_transaction_cmd("ROLLBACK", dbh); } -/* {{{ 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; @@ -661,10 +660,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; @@ -758,11 +762,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; @@ -853,10 +861,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; @@ -928,11 +942,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; @@ -957,10 +975,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; @@ -1008,10 +1031,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; @@ -1043,10 +1071,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; @@ -1123,10 +1156,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; @@ -1140,6 +1178,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 8ef8b68138489..5e40c44596edd 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -107,4 +107,14 @@ extern const php_stream_ops pdo_pgsql_lob_stream_ops; void pdo_libpq_version(char *buf, size_t len); +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/config_functions.inc b/ext/pdo_pgsql/tests/config_functions.inc new file mode 100644 index 0000000000000..269b3380ab7c3 --- /dev/null +++ b/ext/pdo_pgsql/tests/config_functions.inc @@ -0,0 +1,25 @@ +query('CREATE TABLE IF NOT EXISTS foobar (id INT, name TEXT)'); + +$db->query("INSERT INTO foobar VALUES (NULL, 'PHP')"); +$db->query("INSERT INTO foobar VALUES (NULL, 'PHP6')"); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoPgsql class exists. +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/subclasses/pdopgsql_002.phpt b/ext/pdo_pgsql/tests/subclasses/pdopgsql_002.phpt new file mode 100644 index 0000000000000..8e25c8c3beaef --- /dev/null +++ b/ext/pdo_pgsql/tests/subclasses/pdopgsql_002.phpt @@ -0,0 +1,59 @@ +--TEST-- +PdoPgsql connect through PDO::connect +--EXTENSIONS-- +pdo_mysql +--FILE-- +query('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE IF NOT EXISTS test(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoPgsql class exists. +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/subclasses/pdopgsql_003.phpt b/ext/pdo_pgsql/tests/subclasses/pdopgsql_003.phpt new file mode 100644 index 0000000000000..babef145460d6 --- /dev/null +++ b/ext/pdo_pgsql/tests/subclasses/pdopgsql_003.phpt @@ -0,0 +1,30 @@ +--TEST-- +PdoPgsql getWarningCount +--EXTENSIONS-- +pdo_mysql +--FILE-- +escapeIdentifier("This is a quote\""); + +// TODO - ask someone who knows about postgresql if this is correct: +echo "Result is [$result]\n"; + +--EXPECT-- +Result is ["This is a quote"""] + diff --git a/ext/pdo_sqlite/config.m4 b/ext/pdo_sqlite/config.m4 index 64311eaa6436e..51b50c1de7ee0 100644 --- a/ext/pdo_sqlite/config.m4 +++ b/ext/pdo_sqlite/config.m4 @@ -26,8 +26,14 @@ 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, + PHP_NEW_EXTENSION(pdo_sqlite, pdo_sqlite.c pdo_sqlite_class.c sqlite_driver.c sqlite_statement.c, $ext_shared,,-I$pdo_cv_inc_path) PHP_ADD_EXTENSION_DEP(pdo_sqlite, pdo) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 81761b1370937..974eaa7bdc10e 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -25,8 +25,11 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_sqlite.h" #include "php_pdo_sqlite_int.h" +#include "pdo_sqlite_arginfo.h" #include "zend_exceptions.h" +zend_class_entry *pdosqlite_ce; + /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_sqlite_deps[] = { ZEND_MOD_REQUIRED("pdo") @@ -50,6 +53,8 @@ zend_module_entry pdo_sqlite_module_entry = { }; /* }}} */ +static pdo_driver_class_entry pdosqlite_pdo_driver_class_entry; + #if defined(COMPILE_DL_PDO_SQLITE) || defined(COMPILE_DL_PDO_SQLITE_EXTERNAL) ZEND_GET_MODULE(pdo_sqlite) #endif @@ -68,6 +73,13 @@ PHP_MINIT_FUNCTION(pdo_sqlite) REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long)PDO_SQLITE_ATTR_READONLY_STATEMENT); REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", (zend_long)PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES); + pdosqlite_ce = register_class_PdoSqlite(pdo_dbh_ce); + pdosqlite_ce->create_object = pdo_dbh_new; + + pdosqlite_pdo_driver_class_entry.driver_name = "sqlite"; + pdosqlite_pdo_driver_class_entry.driver_ce = pdosqlite_ce; + pdo_register_driver_specific_class(&pdosqlite_pdo_driver_class_entry); + return php_pdo_register_driver(&pdo_sqlite_driver); } /* }}} */ diff --git a/ext/pdo_sqlite/pdo_sqlite.stub.php b/ext/pdo_sqlite/pdo_sqlite.stub.php new file mode 100644 index 0000000000000..f50d70fc4b60f --- /dev/null +++ b/ext/pdo_sqlite/pdo_sqlite.stub.php @@ -0,0 +1,82 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + 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_class_constant_ex(class_entry, const_DETERMINISTIC_name, &const_DETERMINISTIC_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_DETERMINISTIC_name); + + 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_class_constant_ex(class_entry, const_OPEN_READONLY_name, &const_OPEN_READONLY_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_OPEN_READWRITE_name, &const_OPEN_READWRITE_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_OPEN_CREATE_name, &const_OPEN_CREATE_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_ATTR_OPEN_FLAGS_name, &const_ATTR_OPEN_FLAGS_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_ATTR_READONLY_STATEMENT_name, &const_ATTR_READONLY_STATEMENT_value, ZEND_ACC_PUBLIC, NULL); + 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_class_constant_ex(class_entry, const_ATTR_EXTENDED_RESULT_CODES_name, &const_ATTR_EXTENDED_RESULT_CODES_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_ATTR_EXTENDED_RESULT_CODES_name); + + return class_entry; +} diff --git a/ext/pdo_sqlite/pdo_sqlite_class.c b/ext/pdo_sqlite/pdo_sqlite_class.c new file mode 100644 index 0000000000000..dcd8f34411d00 --- /dev/null +++ b/ext/pdo_sqlite/pdo_sqlite_class.c @@ -0,0 +1,661 @@ + +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Danack + +----------------------------------------------------------------------+ +*/ + +/* The PDO Database Handle Class */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "../pdo/php_pdo.h" +#include "../pdo/php_pdo_driver.h" +#include "../pdo/php_pdo_int.h" +#include "zend_exceptions.h" +#include "zend_object_handlers.h" +#include "zend_hash.h" + +#include "../pdo_sqlite/php_pdo_sqlite.h" +#include "../pdo_sqlite/php_pdo_sqlite_int.h" + +// TODO - define this in appropriate header, not here. + +typedef struct { + zval val; + zend_long row; +} pdopgsql_aggregate_context; + + + +static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, + int argc, sqlite3_value **argv, sqlite3_context *context, + int is_agg) +{ + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + pdopgsql_aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(pdopgsql_aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; +} + + +void php_pgsql_func_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afunc, &func->func, argc, argv, context, 0); +} + +static void php_pgsql_func_step_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->astep, &func->step, argc, argv, context, 1); +} + +static void php_pgsql_func_final_callback(sqlite3_context *context) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); +} + + + + + + +/* {{{ proto bool PdoSqlite::createFunction(string $function_name, callable $callback, int $num_args = -1, int $flags = 0) + Creates a function that can be used in a query +*/ +PHP_METHOD(PdoSqlite, createFunction) +{ + //copied from sqlite_driver.c + + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, + func, php_pgsql_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +#ifndef PDO_SQLITE_OMIT_LOAD_EXTENSION +/* {{{ Attempts to load an SQLite extension library. */ +PHP_METHOD(PdoSqlite, loadExtension) +{ + char *extension, *errtext = NULL; + char fullpath[MAXPATHLEN]; + size_t extension_len; + + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *db_handle; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &extension, &extension_len)) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + db_handle = (pdo_sqlite_db_handle *)dbh->driver_data; + +#ifdef ZTS + if ((strncmp(sapi_module.name, "cgi", 3) != 0) && + (strcmp(sapi_module.name, "cli") != 0) && + (strncmp(sapi_module.name, "embed", 5) != 0) + ) { + // TODO - needs test. + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Not supported in multithreaded Web servers"); + RETURN_FALSE; + } +#endif + + + if (!VCWD_REALPATH(extension, fullpath)) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension '%s'", extension); + RETURN_FALSE; + } + + 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_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL); + RETURN_FALSE; + } + + // 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); + + RETURN_TRUE; +} +/* }}} */ +#endif + +typedef struct { + sqlite3_blob *blob; + size_t position; + size_t size; + int flags; +} php_stream_pdosqlite3_data; + +static ssize_t php_pdosqlite3_stream_write(php_stream *stream, const char *buf, size_t count) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_stream->flags & SQLITE_OPEN_READONLY) { + php_error_docref(NULL, E_WARNING, "Can't write to blob stream: is open as read only"); + return -1; + } + + if (sqlite3_stream->position + count > sqlite3_stream->size) { + php_error_docref(NULL, E_WARNING, "It is not possible to increase the size of a BLOB"); + return -1; + } + + if (sqlite3_blob_write(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) { + return -1; + } + + if (sqlite3_stream->position + count >= sqlite3_stream->size) { + stream->eof = 1; + sqlite3_stream->position = sqlite3_stream->size; + } + else { + sqlite3_stream->position += count; + } + + return count; +} + +static ssize_t php_pdosqlite3_stream_read(php_stream *stream, char *buf, size_t count) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_stream->position + count >= sqlite3_stream->size) { + count = sqlite3_stream->size - sqlite3_stream->position; + stream->eof = 1; + } + if (count) { + if (sqlite3_blob_read(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) { + return -1; + } + sqlite3_stream->position += count; + } + return count; +} + +static int php_pdosqlite3_stream_close(php_stream *stream, int close_handle) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_blob_close(sqlite3_stream->blob) != SQLITE_OK) { + /* Error occurred, but it still closed */ + } + + efree(sqlite3_stream); + + return 0; +} + +static int php_pdosqlite3_stream_flush(php_stream *stream) +{ + /* do nothing */ + return 0; +} + +/* {{{ */ +static int php_pdosqlite3_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + switch(whence) { + case SEEK_CUR: + if (offset < 0) { + if (sqlite3_stream->position < (size_t)(-offset)) { + sqlite3_stream->position = 0; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->position + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + } else { + if (sqlite3_stream->position + (size_t)(offset) > sqlite3_stream->size) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->position + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + } + case SEEK_SET: + if (sqlite3_stream->size < (size_t)(offset)) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + case SEEK_END: + if (offset > 0) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else if (sqlite3_stream->size < (size_t)(-offset)) { + sqlite3_stream->position = 0; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->size + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + default: + *newoffs = sqlite3_stream->position; + return -1; + } +} +/* }}} */ + + +static int php_pdosqlite3_stream_cast(php_stream *stream, int castas, void **ret) +{ + return FAILURE; +} + +static int php_pdosqlite3_stream_stat(php_stream *stream, php_stream_statbuf *ssb) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + ssb->sb.st_size = sqlite3_stream->size; + return 0; +} + +static const php_stream_ops php_stream_pdosqlite3_ops = { + php_pdosqlite3_stream_write, + php_pdosqlite3_stream_read, + php_pdosqlite3_stream_close, + php_pdosqlite3_stream_flush, + "PDOSQLite", + php_pdosqlite3_stream_seek, + php_pdosqlite3_stream_cast, + php_pdosqlite3_stream_stat, + NULL +}; + + +/* {{{ Open a blob as a stream which we can read / write to. */ +PHP_METHOD(PdoSqlite, openBlob) +{ + char *table, *column, *dbname = "main", *mode = "rb"; + size_t table_len, column_len, dbname_len; + zend_long rowid, flags = SQLITE_OPEN_READONLY, sqlite_flags = 0; + sqlite3_blob *blob = NULL; + php_stream_pdosqlite3_data *sqlite3_stream; + php_stream *stream; + + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *db_handle; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + db_handle = (pdo_sqlite_db_handle *)dbh->driver_data; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl|pl", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len, &flags) == FAILURE) { + RETURN_THROWS(); + } + + sqlite3 *sqlite_handle; + sqlite_handle = db_handle->db; + sqlite_flags = (flags & SQLITE_OPEN_READWRITE) ? 1 : 0; + + if (sqlite3_blob_open(sqlite_handle, dbname, table, column, rowid, sqlite_flags, &blob) != SQLITE_OK) { + // TODO - exception needed here. +// php_sqlite3_error(db_obj, "Unable to open blob: %s", sqlite3_errmsg(db_obj->db)); + // and converting to exception zend_throw_exception_ex(php_pdo_get_exception(), 0, errtext); + RETURN_FALSE; + } + + sqlite3_stream = emalloc(sizeof(php_stream_pdosqlite3_data)); + sqlite3_stream->blob = blob; + sqlite3_stream->flags = flags; + sqlite3_stream->position = 0; + sqlite3_stream->size = sqlite3_blob_bytes(blob); + + if (sqlite_flags != 0) { + mode = "r+b"; + } + + stream = php_stream_alloc(&php_stream_pdosqlite3_ops, sqlite3_stream, 0, mode); + + if (stream) { + php_stream_to_zval(stream, return_value); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +static int php_pgsql_collation_callback(void *context, + int string1_len, const void *string1, + int string2_len, const void *string2) +{ + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + + +PHP_METHOD(PdoSqlite, createAggregate) +{ + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, + func, NULL, php_pgsql_func_step_callback, php_pgsql_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::createCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PdoSqlite, createCollation) +{ + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_pgsql_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + ZVAL_COPY(&collation->callback, &fci.function_name); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + efree(collation); + RETURN_FALSE; +} +/* }}} */ diff --git a/ext/pdo_sqlite/php_pdo_sqlite_int.h b/ext/pdo_sqlite/php_pdo_sqlite_int.h index c9aeee08a3a59..08d37af9e5e58 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite_int.h +++ b/ext/pdo_sqlite/php_pdo_sqlite_int.h @@ -78,4 +78,7 @@ enum { PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES }; +#define REGISTER_PDO_SQLITE_CLASS_CONST_LONG(const_name, value) \ + zend_declare_class_constant_long(pdosqlite_ce, const_name, sizeof(const_name)-1, (zend_long)value); + #endif diff --git a/ext/pdo_sqlite/tests/subclasses/gc.phpt b/ext/pdo_sqlite/tests/subclasses/gc.phpt new file mode 100644 index 0000000000000..e09b7dd48889f --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/gc.phpt @@ -0,0 +1,28 @@ +--TEST-- +GC support for PDO Sqlite driver data +--EXTENSIONS-- +pdo_sqlite +--FILE-- +a = Pdo::connect('sqlite::memory:'); + +if (!$obj->a instanceof PdoSqlite) { + echo "Wrong class type. Should be PdoSqlite but is [" . get_class($obj->a) . "]\n"; +} + +$obj->a->createFunction('func1', function() use ($obj) {}, 1); +$obj->a->createAggregate('func2', function() use ($obj) {}, function() use($obj) {}); +$obj->a->createCollation('col', function() use ($obj) {}); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt new file mode 100644 index 0000000000000..da48a02c7cfe8 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt @@ -0,0 +1,26 @@ +--TEST-- +PDO_sqlite: Testing constants exist +--EXTENSIONS-- +pdo_sqlite +--FILE-- + +--EXPECTF-- +Hello +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt new file mode 100644 index 0000000000000..cc4763e4dbd91 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt @@ -0,0 +1,32 @@ +--TEST-- +PDO_sqlite: Testing createAggregate() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +$db->createAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); + + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(2) "12" + [0]=> + string(2) "12" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt new file mode 100644 index 0000000000000..e4152c06e81e8 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +PDO_sqlite: Testing invalid callback for createAggregate() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +createAggregate('foo', 'a', ''); +} catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + $pdo->createAggregate('foo', 'strlen', ''); +} catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; +} + +?> +--EXPECT-- +PdoSqlite::createAggregate(): Argument #2 ($step) must be a valid callback, function "a" not found or invalid function name +PdoSqlite::createAggregate(): Argument #3 ($finalize) must be a valid callback, function "" not found or invalid function name diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt new file mode 100644 index 0000000000000..80344de0a59f2 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt @@ -0,0 +1,39 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateCollation() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "1")'); +$db->query('INSERT INTO foobar VALUES (NULL, "2")'); +$db->query('INSERT INTO foobar VALUES (NULL, "10")'); + +$db->createCollation('MYCOLLATE', function($a, $b) { return strnatcmp($a, $b); }); + +$result = $db->query('SELECT name FROM foobar ORDER BY name COLLATE MYCOLLATE'); +foreach ($result as $row) { + echo $row['name'] . "\n"; +} + +$result = $db->query('SELECT name FROM foobar ORDER BY name'); +foreach ($result as $row) { + echo $row['name'] . "\n"; +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +1 +2 +10 +1 +10 +2 diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt new file mode 100644 index 0000000000000..d29e79fc903df --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt @@ -0,0 +1,38 @@ +--TEST-- +PDO_sqlite: Testing createFunction() with flags +--EXTENSIONS-- +pdo_sqlite +--SKIPIF-- + +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt new file mode 100644 index 0000000000000..0fe00b15e4ef4 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +PdoSqlite basic +--EXTENSIONS-- +pdo +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoSqlite class exists. +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..606b2bb0ab726 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt @@ -0,0 +1,51 @@ +--TEST-- +PdoSqlite create through PDO::connect and function define. +--EXTENSIONS-- +pdo +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoSqlite class exists. +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..0befb1511fa0b --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt @@ -0,0 +1,67 @@ +--TEST-- +PdoSqlite load extension +--EXTENSIONS-- +pdo +--FILE-- +loadExtension($extension_location); + +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..256cea80ca812 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_004_blobopen.phpt @@ -0,0 +1,78 @@ +--TEST-- +PdoSqlite::blobOpen stream test +--EXTENSIONS-- +pdo +--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_load_extension_failure.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt new file mode 100644 index 0000000000000..2f550dce9d5b5 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt @@ -0,0 +1,43 @@ +--TEST-- +PdoSqlite load extension +--EXTENSIONS-- +pdo +--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 '%s: cannot open shared object file: No such file or directory' +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");