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

[RFC] Report an error when binding a column name in an ORDER BY clause #216

Merged
merged 3 commits into from
Oct 8, 2020

Conversation

eygraber
Copy link
Contributor

@eygraber eygraber commented Oct 7, 2020

Attempting to address sqldelight/sqldelight#1187

@AlecStrong Is this the right approach, and does it need to be broadened to other areas besides ORDER BY?

Comment on lines 88 to 96
DialectPreset.SQLITE_3_18 to arrayOf("src/test/fixtures", "src/test/fixtures_upsert_not_supported"),
DialectPreset.SQLITE_3_24 to arrayOf("src/test/fixtures", "src/test/fixtures_sqlite_3_24"),
DialectPreset.SQLITE_3_25 to arrayOf("src/test/fixtures", "src/test/fixtures_sqlite_3_24", "src/test/fixtures_sqlite_3_25"),
DialectPreset.MYSQL to arrayOf("src/test/fixtures_mysql"),
DialectPreset.HSQL to arrayOf("src/test/fixtures_hsql"),
DialectPreset.POSTGRESQL to arrayOf("src/test/fixtures_postgresql")
DialectPreset.SQLITE_3_18 to arrayOf("src/test/fixtures")
)

@Suppress("unused") // Used by Parameterized JUnit runner reflectively.
@Parameters(name = "{0}: {1}")
@JvmStatic fun parameters() = dialects.flatMap { (dialect, fixtureFolders) ->
fixtureFolders.flatMap { fixtureFolder ->
File(fixtureFolder).listFiles()
.filter { it.isDirectory }
.filter { it.isDirectory && it.name == "bind-failures" }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pardon my harness, i.e. is there a better way to do this?

Copy link
Collaborator

Choose a reason for hiding this comment

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

to run a single test what i usually do is run all tests, and then right click the specific one I want from the intellij run panel and run just the one test. It's not a great flow, this works too

Copy link
Collaborator

@AlecKazakova AlecKazakova left a comment

Choose a reason for hiding this comment

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

this is perfect! just the one comment and harness revert but looks great to me

Comment on lines 16 to 18
val childBindExpr = node.psi.children.find { it is SqlBindExpr }
if (childBindExpr != null) {
annotationHolder.createErrorAnnotation(childBindExpr, "Cannot bind the name of a column in an ORDER BY clause")
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

i think you might be able to be more explicit here

Suggested change
val childBindExpr = node.psi.children.find { it is SqlBindExpr }
if (childBindExpr != null) {
annotationHolder.createErrorAnnotation(childBindExpr, "Cannot bind the name of a column in an ORDER BY clause")
}
if (expr is SqlBindExpr) {
annotationHolder.createErrorAnnotation(expr, "Cannot bind the name of a column in an ORDER BY clause")
}

DialectPreset.MYSQL to arrayOf("src/test/fixtures_mysql"),
DialectPreset.HSQL to arrayOf("src/test/fixtures_hsql"),
DialectPreset.POSTGRESQL to arrayOf("src/test/fixtures_postgresql")
DialectPreset.SQLITE_3_18 to arrayOf("src/test/fixtures")
Copy link
Collaborator

Choose a reason for hiding this comment

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

revert

Comment on lines 88 to 96
DialectPreset.SQLITE_3_18 to arrayOf("src/test/fixtures", "src/test/fixtures_upsert_not_supported"),
DialectPreset.SQLITE_3_24 to arrayOf("src/test/fixtures", "src/test/fixtures_sqlite_3_24"),
DialectPreset.SQLITE_3_25 to arrayOf("src/test/fixtures", "src/test/fixtures_sqlite_3_24", "src/test/fixtures_sqlite_3_25"),
DialectPreset.MYSQL to arrayOf("src/test/fixtures_mysql"),
DialectPreset.HSQL to arrayOf("src/test/fixtures_hsql"),
DialectPreset.POSTGRESQL to arrayOf("src/test/fixtures_postgresql")
DialectPreset.SQLITE_3_18 to arrayOf("src/test/fixtures")
)

@Suppress("unused") // Used by Parameterized JUnit runner reflectively.
@Parameters(name = "{0}: {1}")
@JvmStatic fun parameters() = dialects.flatMap { (dialect, fixtureFolders) ->
fixtureFolders.flatMap { fixtureFolder ->
File(fixtureFolder).listFiles()
.filter { it.isDirectory }
.filter { it.isDirectory && it.name == "bind-failures" }
Copy link
Collaborator

Choose a reason for hiding this comment

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

to run a single test what i usually do is run all tests, and then right click the specific one I want from the intellij run panel and run just the one test. It's not a great flow, this works too

Comment on lines +58 to +61
val bindExpr = expr as? SqlBindExpr
if (bindExpr != null) {
annotationHolder.createErrorAnnotation(bindExpr, "Cannot bind the name of a column in a SELECT result-column")
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did it like this because smartcast can't be used on expr. I can do it as a let if you think that would look better.

Copy link
Collaborator

Choose a reason for hiding this comment

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

i think this is good 👍

@@ -46,6 +50,25 @@ internal abstract class SelectStmtMixin(
override fun annotate(annotationHolder: SqlAnnotationHolder) {
super.annotate(annotationHolder)

val invalidBinaryEqualityBindExpr = exprList.find { child ->
Copy link
Collaborator

Choose a reason for hiding this comment

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

its still valid to do WHERE ? = some_column so I wouldn't want to fail here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking about that. What if the failure only occurs if the RHS is also a SqlBindExpr?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I also updated to test for all binary expressions

Copy link
Collaborator

Choose a reason for hiding this comment

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

yep im cool with LHS and RHS cant both be a bind expr. I'd recommend just doing that in a new mixin for binary expressions then instead of having it live in SelectStmtMixin

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can add a mixin to all the binary comparison expressions, but is there a way to add something that includes all the different types of expressions in a synthetic one?

Something like:

binary_comparison_expr ::= [ binary_boolean_expr | binary_equality_expr | binary_like_expr | is_expr | between_expr | in_expr ]

Copy link
Collaborator

Choose a reason for hiding this comment

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

that would need a ton of changes downstream so probably not ideal. you could have a single mixin kotlin class like

abstract class BinaryMixin : SqlCompositeElement(node) {
  abstract fun getExprList(): List<SqlExpr>

  fun annotate() {
    ...
  }
}

and then you just have to repeat yourself in the bnf but i think thats fine

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just pushed that update

Comment on lines +58 to +61
val bindExpr = expr as? SqlBindExpr
if (bindExpr != null) {
annotationHolder.createErrorAnnotation(bindExpr, "Cannot bind the name of a column in a SELECT result-column")
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

i think this is good 👍

@eygraber eygraber force-pushed the binding-column-name-error branch 2 times, most recently from 34707a1 to 24e11af Compare October 8, 2020 04:11
@@ -259,12 +261,18 @@ collate_expr ::= expr COLLATE collation_name
binary_like_operator ::= ( LIKE | GLOB | REGEXP | MATCH )
binary_like_expr ::= expr [ NOT ] ( binary_like_operator ) expr [ ESCAPE expr ] {
mixin = "com.alecstrong.sql.psi.core.psi.mixins.BinaryLikeExprMixin"
mixin = "com.alecstrong.sql.psi.core.psi.mixins.BinaryComparisonExprMixin"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it OK to specify two mixins like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like it's not. Pushed some changes to address that.

@AlecKazakova AlecKazakova merged commit 5749520 into sqldelight:master Oct 8, 2020
@AlecKazakova
Copy link
Collaborator

awesome, thanks!

@AlecKazakova
Copy link
Collaborator

reverting the result_column part: sqldelight/sqldelight#631

@eygraber
Copy link
Contributor Author

eygraber commented Oct 8, 2020

Is it possible to suppress the error just for that scenario?

@AlecKazakova
Copy link
Collaborator

probably, i'm working on getting a release out so will wait till thats stable before working more on it

in general sql-psi should be as permissive as possible about whats allowed, and if we want to have more fine tuned errors they should land in sqldelight, in retrospect we might want to move these mixins over there so that sql-psi treats these as find but sqldelight complains for invalid arg types

@AlecKazakova
Copy link
Collaborator

also no idea why but i think this is causing memory leaks downstream: https://github.com/cashapp/sqldelight/pull/2038/checks?check_run_id=1227010895

going to revert for now to derisk the release but yea we can come back to it after

AlecKazakova added a commit that referenced this pull request Oct 8, 2020
@AlecKazakova
Copy link
Collaborator

nevermind that looks like it might be unrelated

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.

2 participants