-
Notifications
You must be signed in to change notification settings - Fork 625
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
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. |
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 @ryanschneider! The changes look reasonable to me overall, left some comments
src/parser/mod.rs
Outdated
/// In those cases we use the following macro to parse instead of calling [parse_expr] directly. | ||
macro_rules! parse_expr_normal { | ||
($option:expr) => { | ||
if matches!(self.peek_token().token, Token::LParen) { |
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.
if matches!(self.peek_token().token, Token::LParen) { | |
if self.peek_token_ref().token == Token::LParen { |
Thanks for the latest round of feedback @iffyio, I've been busy with some unrelated work so haven't had time to look at it all yet or start on the changes but hopefully will have some space cycles the next couple days, I'll re-request review when it's ready! |
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
- use `concat!` for longer SQL strings - replace macro w/ `parse_column_option_expr()` - add SQL examples to docstrings - use `peek_token_ref()` instead of `matches!` - `in_normal_state` -> `pub(crate) in_column_definition_state` - revert fmt change that moved use statements - add `GENERATED` test and use [ParserState::Normal] for parsing inner expr.
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 @ryanschneider! Could we do a pass through the options mentioned here and here to extend them with similar support for the NOT NULL
clause? Beyond that I think the changes look good! (Also headsup that there's currently a docs failure in ci on the branch)
tests/sqlparser_common.rs
Outdated
#[test] | ||
fn parse_notnull_unsupported() { | ||
// Only Postgres, DuckDB, and SQLite support `x NOTNULL` as an expression | ||
// All other dialects consider `x NOTNULL` like `x AS NOTNULL` and thus | ||
// consider `NOTNULL` an alias for x. | ||
let dialects = all_dialects_except(|d| d.supports_notnull_operator()); | ||
let _ = dialects | ||
.verified_only_select_with_canonical("SELECT NULL NOTNULL", "SELECT NULL AS NOTNULL"); | ||
} | ||
|
||
#[test] | ||
fn parse_notnull_supported() { | ||
// Postgres, DuckDB and SQLite support `x NOTNULL` as an alias for `x IS NOT NULL` | ||
let dialects = all_dialects_where(|d| d.supports_notnull_operator()); | ||
let _ = dialects.expr_parses_to("x NOTNULL", "x IS NOT NULL"); | ||
} | ||
|
||
#[test] | ||
fn test_notnull_precedence() { | ||
// For dialects which support it, `NOT NULL NOTNULL` should | ||
// parse as `(NOT (NULL IS NOT NULL))` | ||
let supported_dialects = all_dialects_where(|d| d.supports_notnull_operator()); | ||
let unsupported_dialects = all_dialects_except(|d| d.supports_notnull_operator()); | ||
|
||
assert_matches!( | ||
supported_dialects.expr_parses_to("NOT NULL NOTNULL", "NOT NULL IS NOT NULL"), | ||
Expr::UnaryOp { | ||
op: UnaryOperator::Not, | ||
.. | ||
} | ||
); | ||
|
||
// for unsupported dialects, parsing should stop at `NOT NULL` | ||
unsupported_dialects.expr_parses_to("NOT NULL NOTNULL", "NOT NULL"); | ||
} |
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.
Since they're the same feature, can we merge these scenarios into the same fn parse_notnull(){}
test function?.
Also for the comments, ideally we skip mentioning the individual dialects (Postgres, duckdb etc), so that the comment doesnt' become stale as other/new dialects are extended with support for this feature.
tests/sqlparser_common.rs
Outdated
#[test] | ||
fn parse_not_null_supported() { | ||
let _ = all_dialects().expr_parses_to("x NOT NULL", "x IS NOT NULL"); | ||
let _ = all_dialects().expr_parses_to("NULL NOT NULL", "NULL IS NOT NULL"); | ||
} | ||
|
||
#[test] | ||
fn test_not_null_precedence() { | ||
assert_matches!( | ||
all_dialects().expr_parses_to("NOT NULL NOT NULL", "NOT NULL IS NOT NULL"), | ||
Expr::UnaryOp { | ||
op: UnaryOperator::Not, | ||
.. | ||
} | ||
); | ||
assert_matches!( | ||
all_dialects().expr_parses_to("NOT x NOT NULL", "NOT x IS NOT NULL"), | ||
Expr::UnaryOp { | ||
op: UnaryOperator::Not, | ||
.. | ||
} | ||
); | ||
} |
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.
Similar to the notnull
scenarios, can we merge these into the same function?
Sorry I should've mentioned that I did check the other ColumnOptions and confirmed that they don't need to use |
Ok, I consolidated the tests, fixed the |
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!