Skip to content
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

res.ftype(column_number) returns corrupted answer for data types with high OIDs #187

Closed
ged opened this issue Jul 14, 2014 · 10 comments
Closed

Comments

@ged
Copy link
Owner

ged commented Jul 14, 2014

Original report by Mina Naguib (Bitbucket: [Mina Naguib](https://bitbucket.org/Mina Naguib), ).


The postgresql documentation states "The oid type is currently implemented as an unsigned four-byte integer". Its C typedef in postgres_ext.h is:

#!c
typedef unsigned int Oid;

We've hit a case in our DB where a data type (hstore) in pg_type has OID 2163584298, which is below 2^32 but slightly higher than 2^31

When we do a select on a table which returns columns of that type, res.ftype( column_number ) returns a corrupted negative number

I believe the bug is in pg_result.c function pgresult_ftype:

#!c
     ...
     return INT2NUM(PQftype(result, i));

This takes the 32-bit unsigned Oid and passes it to INT2NUM which treats it as a signed int, causing the corruption.

The fix I've applied locally is very simply to call UINT2NUM instead. #ftype then returns the correct positive Oid (which is needed in rails as it joins it on data collected from pg_type)

@ged
Copy link
Owner Author

ged commented Jul 16, 2014

Original comment by Mina Naguib (Bitbucket: [Mina Naguib](https://bitbucket.org/Mina Naguib), ).


The actual patch with the fix is at:

samsung-ads-grave-yard@3fd2a4f

@ged
Copy link
Owner Author

ged commented Jul 16, 2014

Original comment by Mina Naguib (Bitbucket: [Mina Naguib](https://bitbucket.org/Mina Naguib), ).


I went through the code looking for other obvious places where an Oid is passed to a function expecting a signed int, and fixed a few more spots.

Second commit is at:

samsung-ads-grave-yard@cc8513a

@ged
Copy link
Owner Author

ged commented Jul 18, 2014

Original comment by Michael Granger (Bitbucket: ged, GitHub: ged).


Great! Thanks for tracking this down. I'll get your fixes pulled in this weekend.

@ged
Copy link
Owner Author

ged commented Jul 21, 2014

Original comment by Mina Naguib (Bitbucket: [Mina Naguib](https://bitbucket.org/Mina Naguib), ).


Cool - let me know if you'd prefer I submit it in any particular format.

@ged
Copy link
Owner Author

ged commented Jul 23, 2014

Original comment by Michael Granger (Bitbucket: ged, GitHub: ged).


Fixed in 738d4c4, and also corrected docs for a few methods that can return Bignums.

@ged
Copy link
Owner Author

ged commented Jul 23, 2014

Original comment by Michael Granger (Bitbucket: ged, GitHub: ged).


I couldn't come up with a spec that tested this other than creating an extension that set its own type Oid, but I'll keep trying. In the meantime, it's a fairly straightforward change, so I'm calling it fixed.

@ged ged closed this as completed Jul 23, 2014
@ged
Copy link
Owner Author

ged commented Jul 23, 2014

Original comment by Mina Naguib (Bitbucket: [Mina Naguib](https://bitbucket.org/Mina Naguib), ).


Cool. Thanks. Any chance of a release ? The git repo doesn't have a Gemfile, so it's near impossible to use bundler's ":git => url" syntax to depend on the project from source instead of a public release.

Also regarding actually replicating the issue, you can create a table in postgres specifying "with oids". Insert 2.2 billion rows (doesn't have to be cumulative - delete/truncate every few), then issue a "create type".

@ged
Copy link
Owner Author

ged commented Jul 23, 2014

Original comment by Mina Naguib (Bitbucket: [Mina Naguib](https://bitbucket.org/Mina Naguib), ).


Concretely:

#!sql
psql> create table test (x char(1)) with oids;

Then

#!shell
 $ (yes | head -n 2147483647; echo "\.") | psql -c "\copy test from stdin"
 # This will take a while. in another connection "\dt+ test" to check progress - finishes around 90GB

Then

#!sql
  psql> create type foo as enum ('foo', 'bar');
  psql> select oid, typname from pg_type order by oid desc limit 1;
  // Verify high OID above for type foo
  psql> insert into test2 values('bar');

Finally "select f from test2" with ruby-pg and check #ftype(1)

@ged
Copy link
Owner Author

ged commented Jul 23, 2014

Original comment by Mina Naguib (Bitbucket: [Mina Naguib](https://bitbucket.org/Mina Naguib), ).


If you'll do the above test, do it on a disposable db (initdb foo; postgres -D foo), since you won't be able to rewind the high Oid.

@ged
Copy link
Owner Author

ged commented Jul 25, 2014

Original comment by Michael Granger (Bitbucket: ged, GitHub: ged).


Okay. I hesitate to try to do this in the regular unit test suite, but thanks for the example.

I've pushed a prerelease up to Rubygems; you should be able to use it either by passing --pre to the gem command, or specifying the 0.17.2.pre.546 version in your Gemfile. Let me know how it goes.

@ged ged added this to the Pending milestone Oct 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant