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

Add support for parsing MsSql alias with equals #1467

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

yoavcloud
Copy link
Contributor

This PR addresses the MsSql syntax for select item alias using the = operator, for example:

SELECT col_alias = col FROM tbl

src/parser/mod.rs Outdated Show resolved Hide resolved
/// Parse a [`SelectItem`] based on an MsSql syntax that uses the equal sign
/// to denote an alias, for example: SELECT col_alias = col FROM tbl
/// <https://learn.microsoft.com/en-us/sql/t-sql/queries/select-examples-transact-sql?view=sql-server-ver16#b-use-select-with-column-headings-and-calculations>
fn parse_mssql_alias_with_equal(&mut self, expr: &Expr) -> Option<SelectItem> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
fn parse_mssql_alias_with_equal(&mut self, expr: &Expr) -> Option<SelectItem> {
fn maybe_unpack_alias_assignment(expr: &Expr) -> Option<SelectItem> {

Thinking since this is only a helper function that it doesn't get confused for a parser/parsing method

Comment on lines 11191 to 11193
/// Parse a [`SelectItem`] based on an MsSql syntax that uses the equal sign
/// to denote an alias, for example: SELECT col_alias = col FROM tbl
/// <https://learn.microsoft.com/en-us/sql/t-sql/queries/select-examples-transact-sql?view=sql-server-ver16#b-use-select-with-column-headings-and-calculations>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Parse a [`SelectItem`] based on an MsSql syntax that uses the equal sign
/// to denote an alias, for example: SELECT col_alias = col FROM tbl
/// <https://learn.microsoft.com/en-us/sql/t-sql/queries/select-examples-transact-sql?view=sql-server-ver16#b-use-select-with-column-headings-and-calculations>
/// Parse a [`SelectItem`] based on an [MsSql] syntax that uses the equal sign
/// to denote an alias, for example: SELECT col_alias = col FROM tbl
/// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/queries/select-examples-transact-sql?view=sql-server-ver16#b-use-select-with-column-headings-and-calculations

Comment on lines 11195 to 11209
if let Expr::BinaryOp {
left, op, right, ..
} = expr
{
if op == &BinaryOperator::Eq {
if let Expr::Identifier(ref alias) = **left {
return Some(SelectItem::ExprWithAlias {
expr: *right.clone(),
alias: alias.clone(),
});
}
}
}

None
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if let Expr::BinaryOp {
left, op, right, ..
} = expr
{
if op == &BinaryOperator::Eq {
if let Expr::Identifier(ref alias) = **left {
return Some(SelectItem::ExprWithAlias {
expr: *right.clone(),
alias: alias.clone(),
});
}
}
}
None
if let Expr::BinaryOp {
left: Expr::Identifier(alias), op: BinaryOperator::Eq, right,
} = expr {
SelectItem::ExprWithAlias {
expr: right,
alias: Expr::Identifier(alias),
}
} else {
expr
}

Could be simplified to the following? I think no need to pass in a reference and return an option, we can move the value and return the same value if untouched?
Also we can skip the .. operator when deconstructing in this case, so that if the field set changes, we would be notified to ensure the logic remains correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But this would make the function return two different types, as SelectItem is not an Expr variant, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yeah that's correct, actually I'm thinking in that case we can inline the function since its small enough?

@yoavcloud
Copy link
Contributor Author

@iffyio I tried inlining the function, it's not as short as I wish it would, due to pattern matching the boxed fields. But maybe I'm missing something... Would love your feedback.

@yoavcloud yoavcloud requested a review from iffyio October 14, 2024 06:07
@@ -561,6 +561,11 @@ pub trait Dialect: Debug + Any {
fn supports_asc_desc_in_column_definition(&self) -> bool {
false
}

/// For example: SELECT col_alias = col FROM tbl
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// For example: SELECT col_alias = col FROM tbl
/// Returns true if this dialect supports treating the equals operator `=` within a [`SelectItem`]
/// as an alias assignment operator, rather than a boolean expression.
/// For example: the following statements are equivalent for such a dialect:
/// ```sql
/// SELECT col_alias = col FROM tbl;
/// SELECT col_alias AS col FROM tbl;
/// ```

Something like this to illustrate what the flag implies Im thinking?

Comment on lines +11177 to +11179
// Parse a [`SelectItem`] based on an [MsSql] syntax that uses the equal sign
// to denote an alias, for example: SELECT col_alias = col FROM tbl
// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/queries/select-examples-transact-sql?view=sql-server-ver16#b-use-select-with-column-headings-and-calculations
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Parse a [`SelectItem`] based on an [MsSql] syntax that uses the equal sign
// to denote an alias, for example: SELECT col_alias = col FROM tbl
// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/queries/select-examples-transact-sql?view=sql-server-ver16#b-use-select-with-column-headings-and-calculations

We can add the mssql link as part of the trait impl for the dialect instead? Then here I think we would be able to skip the comment altogether since the self.dialect.supports_eq_alias_assignment() along with the documentation on the trait definition for the method would suffice. It'll let us keep the doc for the feature centralized

Comment on lines +11180 to +11204
let expr = if self.dialect.supports_eq_alias_assigment() {
if let Expr::BinaryOp {
ref left,
op: BinaryOperator::Eq,
ref right,
} = expr
{
if let Expr::Identifier(alias) = left.as_ref() {
return Ok(SelectItem::ExprWithAlias {
expr: *right.clone(),
alias: alias.clone(),
});
}
}
expr
} else {
expr
};

// Parse the common AS keyword for aliasing a column
self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)
.map(|alias| match alias {
Some(alias) => SelectItem::ExprWithAlias { expr, alias },
None => SelectItem::UnnamedExpr(expr),
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let expr = if self.dialect.supports_eq_alias_assigment() {
if let Expr::BinaryOp {
ref left,
op: BinaryOperator::Eq,
ref right,
} = expr
{
if let Expr::Identifier(alias) = left.as_ref() {
return Ok(SelectItem::ExprWithAlias {
expr: *right.clone(),
alias: alias.clone(),
});
}
}
expr
} else {
expr
};
// Parse the common AS keyword for aliasing a column
self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)
.map(|alias| match alias {
Some(alias) => SelectItem::ExprWithAlias { expr, alias },
None => SelectItem::UnnamedExpr(expr),
})
let (expr, alias) = match expr {
Expr::BinaryOp {
left: Expr::Identifier(alias),
BinaryOperator::Eq,
right,
} if self.dialect.supports_eq_alias_assigment() => {
(expr, Some(alias))
}
expr => {
// Parse the common AS keyword for aliasing a column
self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)?
}
};
Ok(alias
.map(|alias| SelectItem::ExprWithAlias{ expr, alias })
.unwrap_or_else(|| SelectItem::UnnamedExpr(expr)))

The current inline looks reasonable, I think its mostly rustfmt that making it look a like it has bit more code than it does, but I think we could simplify the logic a bit as above to help with that and readability/intent as well? hopefully?

@@ -1024,6 +1024,17 @@ fn parse_create_table_with_identity_column() {
}
}

#[test]
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah sorry I forgot to mention this part earlier, the test looks reasonable but now with the method on the dialect, can we move the test to common.rs and write it in a similar style to this? that would allow any future dialects with the same support to be covered automatically by the test.

Also for this feature, can we include an assertion that for the dialects that don't support the flag, verifying that they indeed end up with an unnamed select item?

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