-
Notifications
You must be signed in to change notification settings - Fork 1
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
Restored alias to single, non-symbol attribute selects #14
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏 Great job troubleshooting and thanks for the PR!
Instead of a test that reciprocates your solution, would you be willing to write a test that reproduces the original problem? (Maybe one that uses COALESCE
twice — even if the scenario is fairly contrived!)
I'd be really interested to see if the failure is the same for all 3 supported databases and across all 5 supported versions of Rails.
Have you used Appraisal before? Did you try it with PluckMap? You can do
be appraisal install
to bundle all 5 gemfiles and thenbe appraisal rake test
to run your specs against all 5 versions of Rails.
It'd also be useful to pin down the problem being solved so we could explore different solutions. PluckMap
is in an ideal position to detect this scenario and maybe we could provide hints to the type_caster
or apply patches to Rails as in association_scope.rb
Is this an example of the original problem? Interestingly, it only happens for Rails' Postgres adapter. MySQL and SQLite are in the clear. |
Here's the output from each database in its REPL: SQLite
MySQL
PostgreSQL
I bet that's what Rails is getting too 🤔 |
Yeah, I thought after I'd signed off Friday that that test was probably not adequate and that I should probably write one that demonstrates the issue in the Rails type-caster. But then you wrote #17, which seems to exactly demonstrate the problem; I'm confident that this solution would make that test pass in Postgres (so interesting that it's a Postgres-only problem!). But then you went a step further and offered a solution to Rails! 😆 In light of all that, what's the best way forward on this PR? I can incorporate the test from #17 into this PR, which would give the solution a meaningful test. But the whole thing becomes moot if/when rails/rails#36186 is merged and released with Rails. Would there be a way to merge it now (since I doubt your Rails fix will make it back to pre-6 versions of Rails) but also make a note somewhere that it can be removed (hopefully) in future versions of Rails? |
879840d
to
b8b76de
Compare
👍 I see you've incorporated a test like the one in #17. I'll close #17. Regardless of the Rails fix, let's keep this change in PluckMap. But I think we can drop the test from And, in fact, let's move the implementation from |
lib/pluck_map/attribute.rb
Outdated
attr_accessor :indexes | ||
|
||
def initialize(id, model, options={}) | ||
@id = id | ||
@model = model | ||
@selects = Array(options.fetch(:select, id)) | ||
@name = options.fetch(:as, id) | ||
@alias = name.to_s.tr("_", " ") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need for .tr("_", " ")
(I think this might've been a vestigial convenience for Members' version of the to_csv
presenter, but it's inconsequential because we're not using alias in PluckMap's to_csv.rb
and as:
provides a way for clients to specify arbitrary human-readable column names); so when you move this to Presenter#selects
, we can just use Attribute#name
and don't have to reintroduce #alias
.
lib/pluck_map/attribute.rb
Outdated
@block = options[:map] | ||
|
||
if selects.length == 1 && !selects.first.is_a?(Symbol) | ||
@selects = [ Arel.sql("#{selects.first} AS \"#{@alias}\"") ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of 4d030c7, selects
will never contain a String
; all raw SQL values will already be wrapped in Arel.sql
.
Instead of this
Arel.sql("#{select} AS \"#{name}\"")
do this:
select.as(name)
Arel::Nodes::SqlLiteral
includes Arel::AliasPredication
, so we can use as
to set one up. That way our code isn't responsible for knowing
- whether the current database supports
AS
- whether the current database quotes values with
"
- whether we've escaped
name
(or@alias
) appropriately
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat! It's much more elegant to be assured of getting an Arel::Nodes::SqlLiteral
👍
What do we do in this scenario? PluckMap[Example].define do
field select: [
Arel.sql("COALESCE(NULL, 'four')"),
Arel.sql("COALESCE(NULL, 4)")
], map: ->(string, number) { "#{string}#{number}" }
end 🤔 Backing up ... Most of the time
So
🤔🤔 Time for me to go to work, but now I'm leaning back toward Can PluckMap backport my fix? or Can PluckMap apply a less-surgical patch that "tricks" Rails' buggy implementation into doing the right thing? Alternately, if my PR is accepted, my plan is to inquire whether I may create copies of the PR for older versions of Rails. |
b8b76de
to
6c635e1
Compare
I dropped the extraneous test and moved the aliasing into |
🤔 Hm, it looks like it's just the 4.2 build that's failing. Not sure why it's not properly converting the |
It turns out |
... but of course the |
You can use |
…stgres adapter (30m) This was a tricky one to track down, but [this commit](a399ead) introduces a subtle regression that actually seems related to some [odd behavior](rails/rails#36042) in Rails' type casting. From what I can gather, Rails' type casting maps results and what type they should be cast to by name; when each select's name is unique (or aliased to something unique), there's no problem. However, when two selects share a name, either by selecting the same-named column in joined tables (as in the linked issue) OR by calling the same SQL function (I ran into it by plucking `COALESCE()` functions), Rails will cast _all_ of them to the type of the _last_ one. This leads to unexpected results when, say, one `COALESCE` results in an array and another in a string. What you expected to be an array will instead show up as `"{value1,value2,value3}"` ... or more confoundingly `nil` instead of `[]`! 😱 This commit adds in aliasing of selects in the Presenter to mitigate the issue. `pluck` in Rails before 5.0 simply calls `.to_s` on each argument passed to it, resulting in it constructing invalid SQL when passed an aliased `Arel::Nodes::SqlLiteral`.
bccef3d
to
5e963e9
Compare
Yeah, that makes sense. This is what I get for working on this too late last night. 😅 |
👍I'm good with this. 🤔 I think I'll add a lengthy comment to it |
This was a tricky bug to track down, but this commit introduces a subtle regression that actually seems related to some odd behavior in Rails' type casting. From what I can gather, Rails' type casting maps results and what type they should be cast to by name; when each select's name is unique (or aliased to something unique), there's no problem. However, when two selects share a name, either by selecting the same-named column in joined tables (as in the linked issue) OR by calling the same SQL function (I ran into it by plucking
COALESCE()
functions), Rails will cast all of them to the type of the last one. This leads to unexpected results when, say, oneCOALESCE
results in an array and another in a string. What you expected to be an array will instead show up as"{value1,value2,value3}"
... or more confoundinglynil
instead of[]
! 😱This commit reverts the removal of built-in aliasing to mitigate the issue.