iffyio commented on code in PR #2096:
URL:
https://github.com/apache/datafusion-sqlparser-rs/pull/2096#discussion_r2536925036
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,314 @@ impl<'a> Parser<'a> {
}))
}
+ /// Parse an operator name, which can contain special characters like +,
-, <, >, =
+ /// that are tokenized as operator tokens rather than identifiers.
+ /// This is used for PostgreSQL CREATE OPERATOR statements.
+ ///
+ /// Examples: `+`, `myschema.+`, `pg_catalog.<=`
+ pub fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
Review Comment:
```suggestion
fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
```
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,314 @@ impl<'a> Parser<'a> {
}))
}
+ /// Parse an operator name, which can contain special characters like +,
-, <, >, =
+ /// that are tokenized as operator tokens rather than identifiers.
+ /// This is used for PostgreSQL CREATE OPERATOR statements.
+ ///
+ /// Examples: `+`, `myschema.+`, `pg_catalog.<=`
+ pub fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut parts = vec![];
+ loop {
+ parts.push(ObjectNamePart::Identifier(Ident::new(
+ self.next_token().to_string(),
+ )));
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(parts))
+ }
+
+ /// Parse a `CREATE OPERATOR` statement
Review Comment:
```suggestion
/// Parse a [Statement::CreateOperator]
```
thinking this style would be preferrable to link to what the function returns
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,314 @@ impl<'a> Parser<'a> {
}))
}
+ /// Parse an operator name, which can contain special characters like +,
-, <, >, =
+ /// that are tokenized as operator tokens rather than identifiers.
+ /// This is used for PostgreSQL CREATE OPERATOR statements.
+ ///
+ /// Examples: `+`, `myschema.+`, `pg_catalog.<=`
+ pub fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut parts = vec![];
+ loop {
+ parts.push(ObjectNamePart::Identifier(Ident::new(
+ self.next_token().to_string(),
+ )));
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(parts))
+ }
+
+ /// Parse a `CREATE OPERATOR` statement
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createoperator.html)
+ pub fn parse_create_operator(&mut self) -> Result<Statement, ParserError> {
+ macro_rules! dup_err {
+ ($name:expr) => {
+ ParserError::ParserError(format!("Duplicate {} clause in
CREATE OPERATOR", $name))
+ };
+ }
+
+ let name = self.parse_operator_name()?;
+ self.expect_token(&Token::LParen)?;
+
+ let mut function: Option<ObjectName> = None;
+ let mut is_procedure = false;
+ let mut left_arg: Option<DataType> = None;
+ let mut right_arg: Option<DataType> = None;
+ let mut commutator: Option<ObjectName> = None;
+ let mut negator: Option<ObjectName> = None;
+ let mut restrict: Option<ObjectName> = None;
+ let mut join: Option<ObjectName> = None;
+ let mut hashes = false;
+ let mut merges = false;
+
+ loop {
+ let keyword = self.expect_one_of_keywords(&[
+ Keyword::FUNCTION,
+ Keyword::PROCEDURE,
+ Keyword::LEFTARG,
+ Keyword::RIGHTARG,
+ Keyword::COMMUTATOR,
+ Keyword::NEGATOR,
+ Keyword::RESTRICT,
+ Keyword::JOIN,
+ Keyword::HASHES,
+ Keyword::MERGES,
+ ])?;
+
+ match keyword {
+ Keyword::HASHES => {
+ if hashes {
+ return Err(dup_err!("HASHES"));
+ }
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ if merges {
+ return Err(dup_err!("MERGES"));
+ }
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ if function.is_some() {
+ return Err(dup_err!("FUNCTION/PROCEDURE"));
+ }
+ self.expect_token(&Token::Eq)?;
+ function = Some(self.parse_object_name(false)?);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ if left_arg.is_some() {
+ return Err(dup_err!("LEFTARG"));
+ }
+ self.expect_token(&Token::Eq)?;
+ left_arg = Some(self.parse_data_type()?);
+ }
+ Keyword::RIGHTARG => {
+ if right_arg.is_some() {
+ return Err(dup_err!("RIGHTARG"));
+ }
+ self.expect_token(&Token::Eq)?;
+ right_arg = Some(self.parse_data_type()?);
+ }
+ Keyword::COMMUTATOR => {
+ if commutator.is_some() {
+ return Err(dup_err!("COMMUTATOR"));
+ }
+ self.expect_token(&Token::Eq)?;
+ if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ commutator = Some(self.parse_operator_name()?);
+ self.expect_token(&Token::RParen)?;
+ } else {
+ commutator = Some(self.parse_operator_name()?);
+ }
+ }
+ Keyword::NEGATOR => {
+ if negator.is_some() {
+ return Err(dup_err!("NEGATOR"));
+ }
+ self.expect_token(&Token::Eq)?;
+ if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ negator = Some(self.parse_operator_name()?);
+ self.expect_token(&Token::RParen)?;
+ } else {
+ negator = Some(self.parse_operator_name()?);
+ }
+ }
+ Keyword::RESTRICT => {
+ if restrict.is_some() {
+ return Err(dup_err!("RESTRICT"));
+ }
+ self.expect_token(&Token::Eq)?;
+ restrict = Some(self.parse_object_name(false)?);
+ }
+ Keyword::JOIN => {
+ if join.is_some() {
+ return Err(dup_err!("JOIN"));
+ }
+ self.expect_token(&Token::Eq)?;
+ join = Some(self.parse_object_name(false)?);
+ }
+ _ => {
+ return Err(ParserError::ParserError(format!(
+ "Unexpected keyword {:?} in CREATE OPERATOR",
+ keyword
+ )))
+ }
+ }
+
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+
+ // Expect closing parenthesis
+ self.expect_token(&Token::RParen)?;
+
+ // FUNCTION is required
+ let function = function.ok_or_else(|| {
+ ParserError::ParserError("CREATE OPERATOR requires FUNCTION
parameter".to_string())
+ })?;
+
+ Ok(Statement::CreateOperator(CreateOperator {
+ name,
+ function,
+ is_procedure,
+ left_arg,
+ right_arg,
+ commutator,
+ negator,
+ restrict,
+ join,
+ hashes,
+ merges,
+ }))
+ }
+
+ /// Parse a `CREATE OPERATOR FAMILY` statement
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopfamily.html)
+ pub fn parse_create_operator_family(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ Ok(Statement::CreateOperatorFamily(CreateOperatorFamily {
+ name,
+ using,
+ }))
+ }
+
+ /// Parse a CREATE OPERATOR CLASS statement
Review Comment:
```suggestion
/// Parse a [Statement::CreateOperatorClass]
```
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,314 @@ impl<'a> Parser<'a> {
}))
}
+ /// Parse an operator name, which can contain special characters like +,
-, <, >, =
+ /// that are tokenized as operator tokens rather than identifiers.
+ /// This is used for PostgreSQL CREATE OPERATOR statements.
+ ///
+ /// Examples: `+`, `myschema.+`, `pg_catalog.<=`
+ pub fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut parts = vec![];
+ loop {
+ parts.push(ObjectNamePart::Identifier(Ident::new(
+ self.next_token().to_string(),
+ )));
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(parts))
+ }
+
+ /// Parse a `CREATE OPERATOR` statement
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createoperator.html)
+ pub fn parse_create_operator(&mut self) -> Result<Statement, ParserError> {
+ macro_rules! dup_err {
+ ($name:expr) => {
+ ParserError::ParserError(format!("Duplicate {} clause in
CREATE OPERATOR", $name))
+ };
+ }
Review Comment:
we can convert this to either a function or inline the usages (we try to
avoid macros where possible since they're not maintenance friendly).
But actually I think a simpler way of writing the loop logic would be to
rely on rust's match conditions
i.e. `Keyword::HASHES if !hashes => { hashes = true }`
Then any duplicates are implicitly handled once by the match all clause
`_ => { return Err('unexpected keyword {} ...') }`
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,314 @@ impl<'a> Parser<'a> {
}))
}
+ /// Parse an operator name, which can contain special characters like +,
-, <, >, =
+ /// that are tokenized as operator tokens rather than identifiers.
+ /// This is used for PostgreSQL CREATE OPERATOR statements.
+ ///
+ /// Examples: `+`, `myschema.+`, `pg_catalog.<=`
+ pub fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut parts = vec![];
+ loop {
+ parts.push(ObjectNamePart::Identifier(Ident::new(
+ self.next_token().to_string(),
+ )));
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(parts))
+ }
+
+ /// Parse a `CREATE OPERATOR` statement
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createoperator.html)
+ pub fn parse_create_operator(&mut self) -> Result<Statement, ParserError> {
+ macro_rules! dup_err {
+ ($name:expr) => {
+ ParserError::ParserError(format!("Duplicate {} clause in
CREATE OPERATOR", $name))
+ };
+ }
+
+ let name = self.parse_operator_name()?;
+ self.expect_token(&Token::LParen)?;
+
+ let mut function: Option<ObjectName> = None;
+ let mut is_procedure = false;
+ let mut left_arg: Option<DataType> = None;
+ let mut right_arg: Option<DataType> = None;
+ let mut commutator: Option<ObjectName> = None;
+ let mut negator: Option<ObjectName> = None;
+ let mut restrict: Option<ObjectName> = None;
+ let mut join: Option<ObjectName> = None;
+ let mut hashes = false;
+ let mut merges = false;
+
+ loop {
+ let keyword = self.expect_one_of_keywords(&[
+ Keyword::FUNCTION,
+ Keyword::PROCEDURE,
+ Keyword::LEFTARG,
+ Keyword::RIGHTARG,
+ Keyword::COMMUTATOR,
+ Keyword::NEGATOR,
+ Keyword::RESTRICT,
+ Keyword::JOIN,
+ Keyword::HASHES,
+ Keyword::MERGES,
+ ])?;
+
+ match keyword {
+ Keyword::HASHES => {
+ if hashes {
+ return Err(dup_err!("HASHES"));
+ }
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ if merges {
+ return Err(dup_err!("MERGES"));
+ }
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ if function.is_some() {
+ return Err(dup_err!("FUNCTION/PROCEDURE"));
+ }
+ self.expect_token(&Token::Eq)?;
+ function = Some(self.parse_object_name(false)?);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ if left_arg.is_some() {
+ return Err(dup_err!("LEFTARG"));
+ }
+ self.expect_token(&Token::Eq)?;
+ left_arg = Some(self.parse_data_type()?);
+ }
+ Keyword::RIGHTARG => {
+ if right_arg.is_some() {
+ return Err(dup_err!("RIGHTARG"));
+ }
+ self.expect_token(&Token::Eq)?;
+ right_arg = Some(self.parse_data_type()?);
+ }
+ Keyword::COMMUTATOR => {
+ if commutator.is_some() {
+ return Err(dup_err!("COMMUTATOR"));
+ }
+ self.expect_token(&Token::Eq)?;
+ if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ commutator = Some(self.parse_operator_name()?);
+ self.expect_token(&Token::RParen)?;
+ } else {
+ commutator = Some(self.parse_operator_name()?);
+ }
+ }
+ Keyword::NEGATOR => {
+ if negator.is_some() {
+ return Err(dup_err!("NEGATOR"));
+ }
+ self.expect_token(&Token::Eq)?;
+ if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ negator = Some(self.parse_operator_name()?);
+ self.expect_token(&Token::RParen)?;
+ } else {
+ negator = Some(self.parse_operator_name()?);
+ }
+ }
+ Keyword::RESTRICT => {
+ if restrict.is_some() {
+ return Err(dup_err!("RESTRICT"));
+ }
+ self.expect_token(&Token::Eq)?;
+ restrict = Some(self.parse_object_name(false)?);
+ }
+ Keyword::JOIN => {
+ if join.is_some() {
+ return Err(dup_err!("JOIN"));
+ }
+ self.expect_token(&Token::Eq)?;
+ join = Some(self.parse_object_name(false)?);
+ }
+ _ => {
+ return Err(ParserError::ParserError(format!(
+ "Unexpected keyword {:?} in CREATE OPERATOR",
+ keyword
+ )))
+ }
+ }
+
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+
+ // Expect closing parenthesis
+ self.expect_token(&Token::RParen)?;
+
+ // FUNCTION is required
+ let function = function.ok_or_else(|| {
+ ParserError::ParserError("CREATE OPERATOR requires FUNCTION
parameter".to_string())
+ })?;
+
+ Ok(Statement::CreateOperator(CreateOperator {
+ name,
+ function,
+ is_procedure,
+ left_arg,
+ right_arg,
+ commutator,
+ negator,
+ restrict,
+ join,
+ hashes,
+ merges,
+ }))
+ }
+
+ /// Parse a `CREATE OPERATOR FAMILY` statement
Review Comment:
```suggestion
/// Parse a [Statement::CreateOperatorFamily]
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]