Skip to content

Conversation

mbeccati
Copy link
Contributor

Created PR for review - I will commit to the appropriate branches when ready.

@mbeccati
Copy link
Contributor Author

Merged in 340a067

@mbeccati mbeccati closed this Apr 12, 2021
@mbeccati mbeccati deleted the bug80892 branch April 12, 2021 06:23
@taylorotwell
Copy link

taylorotwell commented Apr 28, 2021

@mbeccati @nikic

Hey all - we got a PR related to this change on Laravel: laravel/framework#37168

In summary, previously if you had a boolean column on a Postgres table, you could bind an integer value (0 or 1 typically) in the where condition against that column using PDO::PARAM_INT and everything was fine. However, you will now receive an error.

That's OK I guess, although fairly unexpected and large breaking change on a patch release?

In addition, if we try to adjust Laravel to bind all booleans using PDO::PARAM_BOOL that breaks applications that were previously doing something like querying a smallint column by binding a integer casted boolean value using PDO::PARAM_INT - since you can't bind a PDO::PARAM_BOOL binding when querying a smallint column. I don't think that is really PHP's problem because we were automatically casting booleans to integers when making Postgres queries and always binding using PDO::PARAM_INT.

But, it does put us in a pretty tough spot, since if we don't merge this PR change, people can no longer perform simple boolean queries in Laravel using PHP 8.0.5, while if we do merge it, we break all applications querying smallint columns via booleans - and we don't have a good way of determining what they are actually intended to do. Admittedly, it seems like querying a smallint column by passing true or false to represent 1 or 0 is not exactly the best approach to querying "boolean like" columns in Postgres, but people do it. 🤷

Ultimately, if this PR stands, we would just need to remove this convenience boolean to integer casting when working with Postgres and people could no longer pass booleans in our query builders when querying if a smallint column is 0 or 1 - and that's generally fine I guess - but just wanted to check with y'all first on if you consider this breaking change of not being being able to bind a PDO::PARAM_INT binding against a boolean column as too large of a breaking change to let pass on a patch release.

Any thoughts?

/cc @driesvints

if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT) {
/* we need to check if the number requires bigints */
long long val = strtoll(Z_STRVAL_P(parameter), NULL, 10);
Copy link
Member

Choose a reason for hiding this comment

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

Without having looked into Taylor's issue yet, this code is clearly broken :( If the original value was null or bool, then it will stay as such, while this code assumes the value is always a string. Passing a null to a PARAM_INT binding causes a segfault now (bool doesn't for some reason).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed I thought I had better coverage :(

@mbeccati
Copy link
Contributor Author

Thanks @taylorotwell - I will have a better look tomorrow. Also cc-ing @derickr @carusogabriel as RMs

@kamil-tekiela
Copy link
Member

Was this PR merged without a review?

@mbeccati
Copy link
Contributor Author

@kamil-tekiela The bug report hit internals. The PR has been available for several days before I merged it in. In fact the strtoll usage itself had been reviewed and approved. In an ideal world everyone tests the RCs with their applications and this kind of BC breaks are intercepted.

In an ideal world also I wouldn't have shuffled the code out of the previous else block above thining "yeah, it's ok, tests are green", but sadly that happened.

That said, I'm currently working to fix the issue and making sure there are no regressions.

@mbeccati
Copy link
Contributor Author

mbeccati@ff69c9e is my tentative fix. It surely does fix the segmentation fault and leaves booleans out of the actual change. I don't know however if I've fully covered Laravel's test scenario. TBH I'm a bit on the fence on this one and I would also consider reverting the change.

@nikic
Copy link
Member

nikic commented Apr 29, 2021

@mbeccati That patch looks reasonable as far as the segfault is concerned, but I don't think it would address Laravel's issue, which is the other way around: A boolean column addressed through PARAM_INT.

@mbeccati
Copy link
Contributor Author

@nikic mbeccati@ebf1f5e

Unfortunately the new test fails due to:
SQLSTATE[42804]: Datatype mismatch: 7 ERROR: column "c" is of type boolean but expression is of type integer

The same test passes on 7.3.

Segfault aside, fixing the the PARAM_INT bug brings a significant and unexpected behaviour change that doesn't belong to a minor release.

If you agree, I will proceed with the revert.

@nikic
Copy link
Member

nikic commented Apr 29, 2021

@mbeccati Yeah, I think we should revert this from the stable releases. That would still leave the question of whether this change should be made for PHP 8.1, or whether this should be left alone altogether.

@mbeccati
Copy link
Contributor Author

Reverted from 7.4 to master. I think the matter requires some more thoughts about pros and cons to apply a similar bug fix to 8.1+.

Thanks again @taylorotwell for reporting.

@mvorisek
Copy link
Contributor

mvorisek commented Apr 30, 2021

This PR has broken also atk4/dsql and atk4/data tests.

I fixed it in atk4/dsql#299 . For some reasons, PARAM_INT (with this fix) can not be used with boolean columns, but PARAM_STR can.

But this PR makes sense to me for PHP 8.1.

FYI PARAM_BOOL in Oracle should on the other side map to PARAM_INT, as PARAM_BOOL does not work, see atk4/dsql#299 (comment) and based on https://stackoverflow.com/questions/3726758/is-there-any-boolean-type-in-oracle-databases it is not even supported by Oracle

@hansgv
Copy link

hansgv commented May 2, 2021

@mbeccati According to https://www.php.net/ChangeLog-7.php this ended up in the 7.4.18 release. Was that intentional?

@mbeccati
Copy link
Contributor Author

mbeccati commented May 3, 2021

@hansgv Yes, it ended in 7.4.18 and 8.0.5... that's unfortunate. I wish more poeple would also test release candidates so that this kind of issues could be intercepted, reviewed and eventually rolled back before the final release.

@mbeccati
Copy link
Contributor Author

mbeccati commented May 3, 2021

With that said, I think the actual bug is in the software trying to bind an integer for a boolean column and I hope we'll have a chance to fix binding in 8.1, even if that means people need to adjust their own code to avoid depending on PHP bugs.

@tbleckert
Copy link

@mbeccati Will a new patch be released without this? If so, do you know when? Haven't found a way to downgrade to 7.4.16 on Ubuntu.

@mbeccati
Copy link
Contributor Author

mbeccati commented May 3, 2021

@tbleckert The bug fix has been reverted on git and the revert will be in next point releases, that's all I know 🤷

@nikic
Copy link
Member

nikic commented May 3, 2021

@tbleckert Assuming you are using Ondrej's PPA, you might be able to request a backport of the revert at https://github.com/oerdnj/deb.sury.org. Not sure what their policy on this is.

@nikic
Copy link
Member

nikic commented May 3, 2021

https://bugs.php.net/bug.php?id=80892#1620030664 indicates that this change probably isn't viable even for 8.1. The pgsql type system is too strict for this to work.

@mbeccati
Copy link
Contributor Author

mbeccati commented May 3, 2021

@nikic Yes, it is possible that we can't fix the original issue without introducing other problems. That sucks, but I'm more and more inclined to drop all of this to avoid wasting time doing pointless research for 8.1.

@mvorisek
Copy link
Contributor

mvorisek commented May 3, 2021

In atk4/dsql we run tests across all DB vendors and fix was needed, see https://github.com/atk4/dsql/pull/299/files#diff-cac2b61f4ef042b487661e448d66b69392b1de2f82c4a0e1de1a2fd1be614bffR532, however this change is welcomed from my side in PHP 8.1.

I also find out that Oracle does not support boolean type at at, but php oci driver does not convert PARAM_BOOL to PARAM_INT. This is why many packages converts bool types to int. So I think we need to fix support of PARAM_BOOL across all DB drivers first and then we can merge this PR (into next minor release).

@nikic
Copy link
Member

nikic commented May 3, 2021

Looks like https://bugs.php.net/bug.php?id=81006 is people hitting the segfault part of this issue.

@hansgv
Copy link

hansgv commented May 3, 2021

@tbleckert Assuming you are using Ondrej's PPA, you might be able to request a backport of the revert at https://github.com/oerdnj/deb.sury.org. Not sure what their policy on this is.

It would appear Ondrej has released 7.4.18-2. Anyone know if this issue was fixed?

@driesvints
Copy link

@hansgv seems like 8.0.5-2 at least solved it: laravel/framework#37215 (comment)

@yalsicor
Copy link

yalsicor commented May 3, 2021

@hansgv Ondrej reverted the PR that was causing the pgsql package to break installations (eg. laravel). That works as far as I am concerned.

The subject of this issue is another and does not seem to have a solution yet (and might not get one soon).

@Gemorroj
Copy link
Contributor

Gemorroj commented May 3, 2021

for centos (remi repo):

yum downgrade php-* --exclude=php-pecl-*

@NeilJ247
Copy link

NeilJ247 commented May 6, 2021

I have had to set our docker image version php:7.4.16-fpm-alpine which "fixes" the is of type boolean but expression is of type integer error I was seeing yesterday.

@nikic
Copy link
Member

nikic commented May 6, 2021

For the record, PHP 7.4.19 / PHP 8.0.6 have been / will be released outside the usual schedule, with a fix for this regression only.

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

Successfully merging this pull request may close these issues.