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

[5.1] Update hydrate to handle binary stream resources #10857

Closed
wants to merge 2 commits into from
Closed

[5.1] Update hydrate to handle binary stream resources #10857

wants to merge 2 commits into from

Conversation

jaw-sh
Copy link

@jaw-sh jaw-sh commented Nov 9, 2015

Eloquent->hydrate now runs stream_get_contents on resource columns before passing them into the model.
This resolves an issue where it is impossible to adequately deal with binary data from PostgreSQL.
#10847

`Eloquent->hydrate` now runs `stream_get_contents` on resource columns before passing them into the model.
This resolves an issue where it is impossible to adequately deal with binary data from PostgreSQL.
#10847
// type casting without any headache or checking which database
// system we are using before doing business logic.
//
// See: https://github.com/laravel/framework/issues/10847
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 link to issues please.

@GrahamCampbell GrahamCampbell changed the title Update hydrate to handle binary stream resources. [5.1] Update hydrate to handle binary stream resources Nov 9, 2015
@jaw-sh
Copy link
Author

jaw-sh commented Nov 10, 2015

@GrahamCampbell thanks for the feedback mate.

@crynobone
Copy link
Member

What if we handle stream/resource using cast. Would it do the trick?

@jaw-sh
Copy link
Author

jaw-sh commented Nov 10, 2015

@crynobone No, I don't think so. Review my ticket and the trouble I'm having with PostgreSQL and binary.

To get binary working with PostgreSQL and Laravel I have to ...

  1. Write helper methods to unwrap and escape values. See helpers.php
  2. Write mutators to read and store information correctly. See BoardSetting.php
  3. (This is the big one) Ensure that the Eloquent->attributes[] value IS NOT a stream resource or the Cache facade will cache it as 0 and not the value of its stream. See EloquentBinary.php, my hack

The easiest, Occam's razor approach is what I've done. This simply unwraps the data from the stream so that the cache driver works without any problems, but forces the user to make sure that the data in the attributes array is what should be sent to the database using Eloquent mutators.

The best possible fix for this would be to have a database driver method that can be used to wrap and unwrap information, possibly using type casting. The problem with type casting is that it's basically just a streamlined attribute mutator that doesn't stop the caching issue. The difference between binary for MySQL and binary for PostgreSQL is a great deal (because you have to wrap/unwrap and unstream as I described). However, I don't see a way for that to be done with existing architecture.

I didn't want to go ahead and rewrite your PostgreSQL driver code for fear of wasting a lot of time I don't have to give only to have it rejected. I could do that later, but for right now, what I need is in that trait I linked. This code would help a lot of PostgreSQL people in the mean time.

I really hope all this makes sense. It's taken me a week to get this far.

@jaw-sh
Copy link
Author

jaw-sh commented Nov 10, 2015

To elaborate, you would send ip 192.168.1.10 to MySQL (and probably all other drivers) as this binary string using inet_pton:
b"ˬ\x01\n"

This, inserted into PostgreSQL, causes issues. It throws an error. To get it to work, you must use pg_encode_bytea. The output for that same string is:
\300\250\001\012

THIS is what must be inserted into PostgreSQL. However, on select, it returns a bizarre stream resource. To get an actual value, you must use stream_get_contents (my fix). Now you'll be back to the encoded string
\300\250\001\012

This is unusable, obviously, so we must also pg_unencode_bytea it.
b"ˬ\x01\n"

Now that we're back to binary, we can use inet_ntop to get 192.168.1.10 again. The issue is that ONLY PGSQL stores Binary like this.

My fix doesn't deal with most of that, it only deals with making sure that we never have a stream resource value in Eloquent, because that breaks the cache driver and there is no other way to handle it other than interrupting hydrate to make sure that Eloquent never sees that value.

@taylorotwell
Copy link
Member

Seems like you could use a mutator and be more efficient here and achieve the same purpose. We don't want to spin through every attribute of every result looking for resource types.

@jaw-sh
Copy link
Author

jaw-sh commented Nov 13, 2015

@taylorotwell No mate, read what I wrote. The Cache object does not call mutators. You have to either update your Cache manager to unpack streams, or improve your Database interfaces so they unpack streams (which is the best option).

@taylorotwell
Copy link
Member

How does the cache play into this?

On Fri, Nov 13, 2015 at 4:25 PM, Josh notifications@github.com wrote:

@taylorotwell https://github.com/taylorotwell No mate, read what I
wrote. The Cache object does not call mutators.


Reply to this email directly or view it on GitHub
#10857 (comment).

@jaw-sh
Copy link
Author

jaw-sh commented Nov 13, 2015

Because the stream resource will cache as the integer value 0 for some reason, no matter what the actual binary data is. And, since the Cache tool doesn't call mutators, there's nothing I can do at a mutator level to fix it. I have to change hydrate() to unpack resources.

I really tried everything to avoid changing framework functions, but binary values collected from PostgreSQL are completely worthless in Laravel. They have to be inserted with pg_encode_bytea, selected and unpacked with stream_get_contents, and then finally decoded with pg_decode_bytea. This would all be much better done on a Framework level, but even with mutators doing some heavy lifting, you have to make absolutely sure that stream_get_contents is ran against the raw ->attributes items before any caching is done or it breaks.

@taylorotwell
Copy link
Member

Post your actual code that is calling the cache. I want to see how you are
interacting with it.

On Fri, Nov 13, 2015 at 4:43 PM, Josh notifications@github.com wrote:

Because the stream resource will cache as the integer value 0 for some
reason, no matter what the actual binary data is. And, since the Cache tool
doesn't call mutators, there's nothing I can do at a mutator level to fix
it. I have to change hydrate() to unpack resources.

I really tried everything to avoid changing framework functions, but
binary values collected from PostgreSQL are completely worthless in
Laravel. They have to be inserted with pg_encode_bytea, selected and
unpacked with stream_get_contents, and then finally decoded with
pg_decode_bytea. This would all be much better done on a Framework level,
but even with mutators doing some heavy lifting, you have to make
absolutely sure that stream_get_contents is ran against the raw
->attributes items before any caching is done or it breaks.


Reply to this email directly or view it on GitHub
#10857 (comment).

@jaw-sh
Copy link
Author

jaw-sh commented Nov 13, 2015

@taylorotwell See: #10847 (comment)

I wrote out the entire exploration of this issue.

@ryanwinchester
Copy link
Contributor

So, has this still never been fixed?

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.

5 participants