-
Notifications
You must be signed in to change notification settings - Fork 623
DuckDB, Postgres, SQLite: NOT NULL and NOTNULL expressions #1927
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
base: main
Are you sure you want to change the base?
DuckDB, Postgres, SQLite: NOT NULL and NOTNULL expressions #1927
Conversation
src/dialect/mod.rs
Outdated
|
||
/// Returns true if the dialect supports the passed in alias. | ||
/// See [IsNotNullAlias]. | ||
fn supports_is_not_null_alias(&self, alias: IsNotNullAlias) -> bool { |
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.
would it make sense to have this behavior dialect agnostic and let the parser always accept any variant that shows up? it would let us skip this dialect method as a result
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.
Hmm, the main issue I see w/ that approach is that technically x NOTNULL
is shorthand for x AS NOTNULL
for dialects that don't support NOTNULL
but do support aliases w/o AS
, so this could be considered a breaking change if a query happened to by using NOTNULL
as a column 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.
Ah makes sense! That behavior would only be applicable to the x NOTNULL
scenario or? thinking if so, for x NOT NULL
we should be able to parse that unambiguously as most other operators? Thinking if so then we could get away with only introducing a dialect method supports_notnull_operator()
and parse the latter unconditionally?
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.
Thinking if so then we could get away with only introducing a dialect method supports_notnull_operator() and parse the latter unconditionally?
Sounds good, I like that change, it gets rid of the new enum
as well, I'll re-request review when it's ready, thanks!
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.
Hmm, I made the change but now some of the CREATE TABLE
tests are failing around the IsNotNull
column options, so I think I broke something w/ precedence afterall so need to take a closer look at the parsing.
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.
Hmm, what about adding a ParserState::ColumnDefinition
and using the state in get_next_precedence_default()
to decide if NOT NULL
should have Is
precedence? If you're ok with this approach I think it's the cleanest way to handle it.
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.
Right yeah seems its a bit tricky with the NOT NULL
scenario in create statements, the parser state looks clean but not sure if it can correctly handle the different scenarios? thinking for example, the following if I understand the syntax correctly should be equivalent:
verified_stmt("CREATE TABLE foo (abc INT DEFAULT (42 IS NOT NULL) NOT NULL)");
verified_stmt("CREATE TABLE foo (abc INT DEFAULT (42 NOT NULL) NOT NULL)");
so that only setting the state once on the outer expression/clause wouldn't be correct. not sure if there's a clean way that doesn't require an invasive change here 🤔 it might indeed be that we need to explicitly configure the parse_expr function depending on the context
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.
Hmm good point, I'll experiment with some possibilities and see if I find a clean solution.
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.
Ok @iffyio, in bf25f42 I followed the pattern of a couple other spots in that fn and in the Keyword::DEFAULT
handler I peek the next token, and if it's a LParen then switch back to ParserState::Normal
while parsing that.
I see a couple other spots where this function calls parse_expr
:
ColumnOption::Materialized
ColumnOption::Alias
ColumnOption::Ephemeral
ColumnOption::Check
ColumnOption::Srid
I'll be honest I'm not familiar with most of these, so will need to look to see if any of them need the same treatment.
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.
Ok in bc454c6 I added support for NOT NULL
inside MATERIALIZED
and CHECK
options as well. Let me know what you think!
src/dialect/mod.rs
Outdated
Token::Word(w) | ||
if w.keyword == Keyword::NULL | ||
&& self.supports_is_not_null_alias(NotSpaceNull) => | ||
{ | ||
Ok(p!(Is)) | ||
} | ||
_ => Ok(self.prec_unknown()), | ||
}, | ||
Token::Word(w) | ||
if w.keyword == Keyword::NOTNULL && self.supports_is_not_null_alias(NotNull) => | ||
{ | ||
Ok(p!(Is)) | ||
} |
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.
for the new operators can we add a test case that cover their precedence behavior? see here for example. It would be good if we don't already have coverage to also ensure that we aren't also inadvertently changing the precedence of the IS NOT NULL
operator as well, so we could also include a test case demonstrating that if lacking
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.
Added in 637fb69
tests/sqlparser_common.rs
Outdated
// All other dialects consider `x NOTNULL` like `x AS NOTNULL` and thus | ||
// consider `NOTNULL` an alias for x. | ||
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOTNULL FROM t"#; | ||
let canonical = r#"WITH t AS (SELECT NULL AS x) SELECT x AS NOTNULL FROM t"#; |
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.
I think we can simplify this test, since we're only interested in testing the expression, doing so via a CTE makes the test verbose and harder to maintain going forward without providing additional coverage (note we also have verified_expr()
helper functions to make expression testing easier)
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.
Thanks! I simplified the tests in 431fca3, thanks for the pointers they are much more readable now!
307c465
to
ab6607b
Compare
Rebased against main to resolve conflicts. |
@iffyio I went with the new ParserState::ColumnDefinition idea mentioned here: #1927 (comment) I think it leads to the cleanest code changes, let me know what you think! Also merged in main and resolved conflicts. |
Fixes #1920 by adding
NOT NULL
generally as an alias forIS NOT NULL
, (only inParserState::Normal
) and adds the non-standardNOTNULL
keyword supported by DuckDB, SQLite, and Postgres. It also adds a newParserState::ColumnDefinition
to avoid incorrectly parsingNOT NULL
asIS NOT NULL
in column definitions.Since this is my first non-trivial PR please provide any feedback! I confirmed all tests pass w/
cargo test
andcargo test --all-features
but if there's anything else I need to do please let me know, thanks!