Skip to content

Implement PDO driver specific sub-classes #12804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from

Conversation

kocsismate
Copy link
Member

@kocsismate
Copy link
Member Author

kocsismate commented Nov 27, 2023

@Danack I'll try to take care of your RFC :) For now, I only managed to rebase your PR on master, so I didn't change it much yet.

@kocsismate kocsismate marked this pull request as draft November 27, 2023 22:07
@SakiTakamachi
Copy link
Member

Since it will be unbundled, it seems possible to exclude OCI.

@kocsismate kocsismate force-pushed the pdo_sub_classing branch 14 times, most recently from e740462 to 08e50ef Compare December 6, 2023 22:05
Copy link
Member

@devnexen devnexen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LCTM for the pgsql part.

Copy link
Member

@SakiTakamachi SakiTakamachi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made some comments.
Most are minor nits.

@@ -1334,6 +1410,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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just for SQLite?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah no, I'm not sure how it ended up being here... But it's unused, nothing uses this struct.

Comment on lines 1431 to 1436
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Comment on lines 16 to 17
$db->query("CREATE TABLE #pdo_dblib_001(name varchar(32)); ");
$db->query("INSERT INTO #pdo_dblib_001 values('PHP'), ('PHP6');");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits

Suggested change
$db->query("CREATE TABLE #pdo_dblib_001(name varchar(32)); ");
$db->query("INSERT INTO #pdo_dblib_001 values('PHP'), ('PHP6');");
$db->query("CREATE TABLE #pdo_dblib_001(name VARCHAR(32)); ");
$db->query("INSERT INTO #pdo_dblib_001 VALUES('PHP'), ('PHP6');");

Comment on lines 20 to 21
$db->query("CREATE TABLE #pdo_dblib_002(name varchar(32))");
$db->query("INSERT INTO #pdo_dblib_002 values('PHP'), ('PHP6')");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto


$db = getDbConnection();

$db->query('CREATE TABLE pdofirebird_001 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits

Suggested change
$db->query('CREATE TABLE pdofirebird_001 (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))');
$db->query('CREATE TABLE pdofirebird_001 (idx INT NOT NULL PRIMARY KEY, name VARCHAR(20))');

echo "Wrong class type. Should be PdoMysql but is " . get_class($db) . "\n";
}

$db->exec('CREATE TABLE pdomysql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits

Suggested change
$db->exec('CREATE TABLE pdomysql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))');
$db->exec('CREATE TABLE pdomysql_002(id INT NOT NULL PRIMARY KEY, name VARCHAR(10))');

echo "Wrong class type. Should be PdoOdbc but is " . get_class($db) . "\n";
}

$db->exec('CREATE TABLE pdoodbc_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits

Suggested change
$db->exec('CREATE TABLE pdoodbc_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))');
$db->exec('CREATE TABLE pdoodbc_002(id INT NOT NULL PRIMARY KEY, name VARCHAR(10))');

echo "Wrong class type. Should be PdoPgsql but is " . get_class($db) . "\n";
}

$db->exec('CREATE TABLE pdopgsql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits

Suggested change
$db->exec('CREATE TABLE pdopgsql_002(id int NOT NULL PRIMARY KEY, name VARCHAR(10))');
$db->exec('CREATE TABLE pdopgsql_002(id INT NOT NULL PRIMARY KEY, name VARCHAR(10))');

$db = new PdoSqlite('sqlite::memory:');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$db->query('CREATE TABLE IF NOT EXISTS test_pdo_sqlite_createcollation (id INT AUTO INCREMENT, name TEXT)');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it need IF NOT EXISTS? Maybe forgot to fix this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I forgot it previously.


$db = new PdoSqlite('sqlite::memory:');

$db->query('CREATE TABLE test_pdo_sqlite_createcollation_trampoline (s varchar(4))');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits

Suggested change
$db->query('CREATE TABLE test_pdo_sqlite_createcollation_trampoline (s varchar(4))');
$db->query('CREATE TABLE test_pdo_sqlite_createcollation_trampoline (s VARCHAR(4))');

Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments of things I noticed in quick glance.

I dislike the 1:1 code copies from driver files, e.g. sqlite_driver.c contains from a quick glance the exact same implementation for createFunction as PHP_METHOD(PdoSqlite, createFunction), this is risky as they can get out of sync and are bad for maintenance.

/* proto string PDO::mysqlGetWarningCount()
* Returns the number of SQL warnings during the execution of the last statement
*/
PHP_METHOD(PdoMysql, getWarningCount)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you need a ZPP check here to parse no parameters?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh, ZPP is definitely needed here... Unfortunately I couldn't spot this bug from the original implementation

@@ -27,6 +27,8 @@
#include "php_pdo_odbc_int.h"
#include "pdo_odbc_arginfo.h"

zend_class_entry *pdo_odbc_ce;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a global?

Copy link
Member Author

@kocsismate kocsismate Jan 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coincidentally, I had just noticed that these class entries had been accidentally declared global, so I have already added the static modifier locally. :) Anyway, thanks for the reminder!

@@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make static perhaps?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is exposed as it's needed by pdo_dbh.c.

#include "pdo/php_pdo_driver.h"
#include "php_pdo_pgsql.h"
#include "php_pdo_pgsql_int.h"
#include "pdo_pgsql_arginfo.h"

zend_class_entry *PdoPgsql_ce;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same "why is this a global" question here. Actually each driver seems to have their own global CE, so the question could be repeated for each of them :P

@SakiTakamachi
Copy link
Member

I dislike the 1:1 code copies from driver files, e.g. sqlite_driver.c contains from a quick glance the exact same implementation for createFunction as PHP_METHOD(PdoSqlite, createFunction), this is risky as they can get out of sync and are bad for maintenance.

According to the RFC section "When to deprecate old function on PDO", these functions in the PDO Core should be removed at some point.

There is no mention of how it will be maintained in the interim, so that is up for debate.

@nielsdos
Copy link
Member

nielsdos commented Jan 8, 2024

I dislike the 1:1 code copies from driver files, e.g. sqlite_driver.c contains from a quick glance the exact same implementation for createFunction as PHP_METHOD(PdoSqlite, createFunction), this is risky as they can get out of sync and are bad for maintenance.

According to the RFC section "When to deprecate old function on PDO", these functions in the PDO Core should be removed at some point.

There is no mention of how it will be maintained in the interim, so that is up for debate.

That doesn't stop us from using an implementation alias or moving the code to a common place. We should still follow best practices during the time the code is not yet deprecated.

@SakiTakamachi
Copy link
Member

That doesn't stop us from using an implementation alias or moving the code to a common place. We should still follow best practices during the time the code is not yet deprecated.

I agree that we shouldn't have duplicate implementations.

Besides SQLite, pgsql is the only driver with a unique method in the existing implementation, and pgsql seems to have a good idea about this problem.

Personally, I think it's enough to make SQLite work like pgsql.

Maybe I misunderstood your point. I think this PR's way of implementing pgsql's unique methods makes sense in terms of duplication, how do you think of this?

@kocsismate
Copy link
Member Author

I dislike the 1:1 code copies from driver files, e.g. sqlite_driver.c contains from a quick glance the exact same implementation for createFunction as PHP_METHOD(PdoSqlite, createFunction), this is risky as they can get out of sync and are bad for maintenance.

Yes, I agree with you. I forgot to unify the implementations indeed. Now, I double checked the duplications, and fixed them for pdo_sqlite.

Additionally, I tried to address Saki's review comments.

@SakiTakamachi
Copy link
Member

Thank you, LGTM!

@kocsismate
Copy link
Member Author

I found a few more things which can be deduplicated, so I pushed yet another commit. Sorry for the noise!

@nielsdos
Copy link
Member

nielsdos commented Jan 8, 2024

Personally, I think it's enough to make SQLite work like pgsql.

Maybe I misunderstood your point. I think this PR's way of implementing pgsql's unique methods makes sense in terms of duplication, how do you think of this?

Yep, I was specifically talking about sqlite, which Máté has cleaned up nicely now :-)
Thanks for fixing that.

I do still see from a quick glance there are still global zend_class_entry vars which could either be static or locals (e.g. PdoDblib_ce)

@kocsismate
Copy link
Member Author

kocsismate commented Jan 9, 2024

I do still see from a quick glance there are still global zend_class_entry vars which could either be static or locals (e.g. PdoDblib_ce)

Ah yes, indeed! Fixed now :)

@nielsdos
Copy link
Member

nielsdos commented Jan 9, 2024

I do still see from a quick glance there are still global zend_class_entry vars which could either be static or locals (e.g. PdoDblib_ce)

Ah yes, indeed! Fixed now :)

Cool, thanks! I didn't take a deep look, only a glance, nothing else caught my eye :-)

@kocsismate
Copy link
Member Author

@Girgias Are you also fine with the merge?

Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes :)

pdo_firebird
--SKIPIF--
<?php require(__DIR__ . '/skipif.inc'); ?>
--FILE--
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iluuu1994
ah! I knew about the firebird leak, but I overlooked it in the review.

@kocsismate
This and 002 also require XLEAK.

@iluuu1994
Copy link
Member

@SakiTakamachi
Copy link
Member

This also broke https://github.com/php/php-src/actions/runs/7509344302/job/20446344121. @kocsismate Can you have a look?

I took a look and found that this constant is only defined when using mysqlnd, so it should use #ifdef PDO_USE_MYSQLND.
(libmysql is only used nightly test so I couldn't found that in normal testing)

@kocsismate kocsismate deleted the pdo_sub_classing branch January 14, 2024 07:07
@kocsismate
Copy link
Member Author

@SakiTakamachi Thanks for looking into it! @iluuu1994 Thanks for letting me know about the failure! My recent commit should fix the problem: 9c7b391

@iluuu1994
Copy link
Member

Cool, @kocsismate thanks for taking care of it and thanks @SakiTakamachi for investigating.

@janedbal
Copy link

Would it be possible to add also some getWarnings() (beside the new getWarningCount) method to PdoMysql similary like mysqli has it?

@SakiTakamachi
Copy link
Member

@janedbal
Hi, it is preferable to open a new issue rather than posting new suggestions in this thread.

And perhaps your suggestion would be better discussed on the mailing list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants