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

NULL literals in prepared statements #132

Closed
marshall-mcmullen opened this issue Aug 15, 2019 · 8 comments
Closed

NULL literals in prepared statements #132

marshall-mcmullen opened this issue Aug 15, 2019 · 8 comments

Comments

@marshall-mcmullen
Copy link
Contributor

marshall-mcmullen commented Aug 15, 2019

goqu rocks :-)

So..I may be doing this wrong or have incorrect expectations and if so please set me straight :-).

I'm trying to use goqu to create prepared statements. I saw #127 which was super helpful in getting me going. However, it seems that UpdateDataset is interpolating NULL values even though I used .Prepared(true) which as I understand it should cause it to not interpolate the values so the expression can be reused with different parameters in the future. Here's a concrete example:

func TestUpdatePreparedWithNullValue(t *testing.T) {

    dialect := goqu.Dialect("postgres")

    var value1 *int = nil 

    updateDataset := dialect.Update("foo").Prepared(true).Set(map[string]interface{}{
        "value1": value1,
    })  

    sql, _, _ := updateDataset.ToSQL()
    assert.Equal(t, `UPDATE "foo" SET "value1"=$1`, sql)
}

And here's the test output:

        	Error Trace:	user_test.go:95
        	Error:      	Not equal: 
        	            	expected: "UPDATE \"foo\" SET \"value1\"=$1"
        	            	actual  : "UPDATE \"foo\" SET \"value1\"=NULL"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-UPDATE "foo" SET "value1"=$1
        	            	+UPDATE "foo" SET "value1"=NULL
        	Test:       	TestUpdatePreparedWithNullValue

@doug-martin
Copy link
Owner

Yeah it should be changed to not interpolate nil, or boolean values.

This is an artifact approaching the builder from interpolation first.

@marshall-mcmullen
Copy link
Contributor Author

That makes sense. Thanks for the confirmation @doug-martin. I've worked around this locally again so I'm not blocked on this... But would definitely like to see it fixed as the workaround is kinda gross :)

@marshall-mcmullen
Copy link
Contributor Author

Any chance of this getting fixed in the next week or so @doug-martin ? I'd like to start memoizing prepared statements in our system and I think that this will be a blocker for that. If you don't think you can get to it, but can point me in the right direction I'd be happy to start working on a fix.

@doug-martin
Copy link
Owner

@marshallmcmullen Yeah Im not sure how soon Ill be able to get to this.

If you want to take a stab at this most of the changes should be isolated to sqlgen/expression_sql_generator.go.

To get started you should take a look at

  1. placeHolderSQL this method is what will generate the place holder for a type.
  2. literalNil This method is what serialized nil into NULL.
  3. literalBool This method turns bools into TRUE or FALSE.

You can take a look at literalInt for an example of how to use placeholders instead of the interpolated value.

Now for the tricky part :) Currently goqu will turn any (in)equality on nil or bool value into an IS expression. I think if goqu continues to use this strategy users may end up with unintended SQL generation when the value could be a non-nil value. To change this behavior checkBoolExpType should be changed or potentially removed, not sure yet.

Changing this will warrant a major version change as it wont be backwards compatible.

If you have any ideas please share them!

@marshall-mcmullen
Copy link
Contributor Author

@doug-martin Your instructions were spot-on. I think I've gotten a PR ready that addresses this issue based on my understanding: #151

The harder part, as you envisioned, is what to do about checkBoolExpType. I modified it to no longer turn an (in)equality on nil or bool into an IS expression. This seemed like the most obvious path forward but I can see how this might be a big change to swallow. FWIW, I do feel like this led to more consistent SQL generation.

marshall-mcmullen pushed a commit to marshall-mcmullen/goqu that referenced this issue Sep 10, 2019
This addresses doug-martin#132

It is a reset of closed PR: doug-martin#151

The gist of this fix is to modify the builders to not interpolate
nil and boolean values so that prepared statements can work properly.
This makes prepared=true work more consistently across all types.

The scope of this change:

(1) Modify literalNil to use a placeholder if prepared=true similar to
literalBool and literalInt

(2) Modify checkBoolExpType to not turn (in)equality on nil or bool
values to an IS expression.

(3) LOTS of tests and examples updated to reflect the new behavior.

(4) Fix bugs from doug-martin#151 found by
@doug-martin to preserve correct behavior with Is/IsNot and not
incorrectly map them to Eq/NotEq.
marshall-mcmullen pushed a commit to marshall-mcmullen/goqu that referenced this issue Sep 10, 2019
This addresses doug-martin#132

It is a reset of closed PR: doug-martin#151

The gist of this fix is to modify the builders to not interpolate
nil and boolean values so that prepared statements can work properly.
This makes prepared=true work more consistently across all types.

The scope of this change:

(1) Modify literalNil to use a placeholder if prepared=true similar to
literalBool and literalInt

(2) Modify checkBoolExpType to not turn (in)equality on nil or bool
values to an IS expression.

(3) LOTS of tests and examples updated to reflect the new behavior.

(4) Fix bugs from doug-martin#151 found by
@doug-martin to preserve correct behavior with Is/IsNot and not
incorrectly map them to Eq/NotEq.

(5) Fix incorrect interpoloation for DEFAULT also.
marshall-mcmullen pushed a commit to marshall-mcmullen/goqu that referenced this issue Sep 10, 2019
This addresses doug-martin#132

It is a reset of closed PR: doug-martin#151

The gist of this fix is to modify the builders to not interpolate
nil and boolean values so that prepared statements can work properly.
This makes prepared=true work more consistently across all types.

The scope of this change:

(1) Modify literalNil to use a placeholder if prepared=true similar to
literalBool and literalInt

(2) Modify checkBoolExpType to not turn (in)equality on nil or bool
values to an IS expression.

(3) LOTS of tests and examples updated to reflect the new behavior.

(4) Fix bugs from doug-martin#151 found by
@doug-martin to preserve correct behavior with Is/IsNot and not
incorrectly map them to Eq/NotEq.
marshall-mcmullen pushed a commit to marshall-mcmullen/goqu that referenced this issue Sep 10, 2019
This addresses doug-martin#132

It is a reset of closed PR: doug-martin#151

The gist of this fix is to modify the builders to not interpolate
nil and boolean values so that prepared statements can work properly.
This makes prepared=true work more consistently across all types.

The scope of this change:

(1) Modify literalNil to use a placeholder if prepared=true similar to
literalBool and literalInt

(2) Modify checkBoolExpType to not turn (in)equality on nil or bool
values to an IS expression.

(3) LOTS of tests and examples updated to reflect the new behavior.

(4) Fix bugs from doug-martin#151 found by
@doug-martin to preserve correct behavior with Is/IsNot and not
incorrectly map them to Eq/NotEq.
@marshall-mcmullen
Copy link
Contributor Author

@doug-martin I've pushed some updates to a new PR for this. If you can take a look when you have time I would appreciate it greatly. Thanks!

doug-martin added a commit that referenced this issue Sep 17, 2019
#132 - do not interpolate nil or boolean values
@doug-martin doug-martin mentioned this issue Sep 18, 2019
@doug-martin
Copy link
Owner

Published!

@marshall-mcmullen
Copy link
Contributor Author

@doug-martin woohoo! Very glad we got this one pushed across the finish line. Thanks so much for your help! I just pulled this new tagged version into our product and verified everything is working as expected now. Awesome!!

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

2 participants