Add GC support for PDO driver data

Add a get_gc method that can be implemented by drivers, which can
be used to add additional zvals to the GC buffer.

Implement GC support for PDO SQLite callbacks in particular.

Closes GH-6262.
This commit is contained in:
Nikita Popov 2020-10-02 10:53:21 +02:00
parent d731b764e5
commit e735de6eae
10 changed files with 75 additions and 10 deletions

View file

@ -1291,8 +1291,12 @@ static int dbh_compare(zval *object1, zval *object2)
static HashTable *dbh_get_gc(zend_object *object, zval **gc_data, int *gc_count)
{
pdo_dbh_t *dbh = php_pdo_dbh_fetch_inner(object);
*gc_data = &dbh->def_stmt_ctor_args;
*gc_count = 1;
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
zend_get_gc_buffer_add_zval(gc_buffer, &dbh->def_stmt_ctor_args);
if (dbh->methods->get_gc) {
dbh->methods->get_gc(dbh, gc_buffer);
}
zend_get_gc_buffer_use(gc_buffer, gc_data, gc_count);
return zend_std_get_properties(object);
}

View file

@ -286,6 +286,10 @@ typedef int (*pdo_dbh_check_liveness_func)(pdo_dbh_t *dbh);
* scope */
typedef void (*pdo_dbh_request_shutdown)(pdo_dbh_t *dbh);
/* Called when the PDO handle is scanned for GC. Should populate the get_gc buffer
* with any zvals in the driver_data that would be freed if the handle is destroyed. */
typedef void (*pdo_dbh_get_gc_func)(pdo_dbh_t *dbh, zend_get_gc_buffer *buffer);
/* for adding methods to the dbh or stmt objects
pointer to a list of driver specific functions. The convention is
to prefix the function names using the PDO driver name; this will
@ -316,6 +320,7 @@ struct pdo_dbh_methods {
pdo_dbh_get_driver_methods_func get_driver_methods;
pdo_dbh_request_shutdown persistent_shutdown;
pdo_dbh_txn_func in_transaction;
pdo_dbh_get_gc_func get_gc;
};
/* }}} */

View file

@ -417,7 +417,8 @@ static const struct pdo_dbh_methods dblib_methods = {
NULL, /* check liveness */
NULL, /* get driver methods */
NULL, /* request shutdown */
NULL /* in transaction */
NULL, /* in transaction */
NULL /* get gc */
};
static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options)

View file

@ -1005,7 +1005,11 @@ static const struct pdo_dbh_methods firebird_methods = { /* {{{ */
NULL, /* last_id not supported */
pdo_firebird_fetch_error_func,
firebird_handle_get_attribute,
NULL /* check_liveness */
NULL, /* check_liveness */
NULL, /* get driver methods */
NULL, /* request shutdown */
NULL, /* in transaction */
NULL /* get gc */
};
/* }}} */

View file

@ -563,7 +563,8 @@ static const struct pdo_dbh_methods mysql_methods = {
pdo_mysql_check_liveness,
NULL,
pdo_mysql_request_shutdown,
pdo_mysql_in_transaction
pdo_mysql_in_transaction,
NULL /* get_gc */
};
/* }}} */

View file

@ -705,9 +705,10 @@ static const struct pdo_dbh_methods oci_methods = {
pdo_oci_fetch_error_func,
oci_handle_get_attribute,
pdo_oci_check_liveness, /* check_liveness */
NULL, /* get_driver_methods */
NULL,
NULL
NULL, /* get_driver_methods */
NULL, /* request_shutdown */
NULL, /* in_transaction */
NULL /* get_gc */
};
static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */

View file

@ -384,7 +384,11 @@ static const struct pdo_dbh_methods odbc_methods = {
NULL, /* last id */
pdo_odbc_fetch_error_func,
odbc_handle_get_attr, /* get attr */
NULL, /* check_liveness */
NULL, /* check_liveness */
NULL, /* get_driver_methods */
NULL, /* request_shutdown */
NULL, /* in_transaction */
NULL /* get_gc */
};
static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */

View file

@ -1179,6 +1179,7 @@ static const struct pdo_dbh_methods pgsql_methods = {
pdo_pgsql_get_driver_methods, /* get_driver_methods */
NULL,
pgsql_handle_in_transaction,
NULL /* get_gc */
};
static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */

View file

@ -695,6 +695,25 @@ static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh)
}
}
static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer)
{
pdo_sqlite_db_handle *H = dbh->driver_data;
struct pdo_sqlite_func *func = H->funcs;
while (func) {
zend_get_gc_buffer_add_zval(gc_buffer, &func->func);
zend_get_gc_buffer_add_zval(gc_buffer, &func->step);
zend_get_gc_buffer_add_zval(gc_buffer, &func->fini);
func = func->next;
}
struct pdo_sqlite_collation *collation = H->collations;
while (collation) {
zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback);
collation = collation->next;
}
}
static const struct pdo_dbh_methods sqlite_methods = {
sqlite_handle_closer,
sqlite_handle_preparer,
@ -710,7 +729,8 @@ static const struct pdo_dbh_methods sqlite_methods = {
NULL, /* check_liveness: not needed */
get_driver_methods,
pdo_sqlite_request_shutdown,
NULL
NULL, /* in_transaction */
pdo_sqlite_get_gc
};
static char *make_filename_safe(const char *filename)

View file

@ -0,0 +1,24 @@
--TEST--
GC support for PDO Sqlite driver data
--SKIPIF--
<?php
if (!extension_loaded('pdo_sqlite')) print 'skip not loaded';
?>
--FILE--
<?php
class Obj {
public $a;
public function callback() { }
}
$obj = new Obj;
$obj->a = new PDO('sqlite::memory:');
$obj->a->sqliteCreateFunction('func1', function() use ($obj) {}, 1);
$obj->a->sqliteCreateAggregate('func2', function() use ($obj) {}, function() use($obj) {});
$obj->a->sqliteCreateCollation('col', function() use ($obj) {});
?>
===DONE===
--EXPECT--
===DONE===