iffyio commented on code in PR #2096:
URL:
https://github.com/apache/datafusion-sqlparser-rs/pull/2096#discussion_r2522651342
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
Review Comment:
does this differ from `parse_object_name`?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_parts))
+ }
+
+ /// Parse a CREATE OPERATOR statement
Review Comment:
```suggestion
/// Parse a `CREATE OPERATOR` statement
```
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html)
+ pub fn parse_create_operator_class(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ let default = self.parse_keyword(Keyword::DEFAULT);
+ self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+ let for_type = self.parse_data_type()?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ let family = if self.parse_keyword(Keyword::FAMILY) {
+ Some(self.parse_object_name(false)?)
+ } else {
+ None
Review Comment:
Can we add a test case for a statement without a family?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html)
+ pub fn parse_create_operator_class(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ let default = self.parse_keyword(Keyword::DEFAULT);
+ self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+ let for_type = self.parse_data_type()?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ let family = if self.parse_keyword(Keyword::FAMILY) {
+ Some(self.parse_object_name(false)?)
+ } else {
+ None
+ };
+
+ self.expect_keyword(Keyword::AS)?;
+
+ let mut items = vec![];
+ loop {
+ if self.parse_keyword(Keyword::OPERATOR) {
+ let strategy_number = self.parse_literal_uint()? as u32;
+ let operator_name = self.parse_operator_name()?;
+
+ // Optional operator argument types
+ let op_types = if self.consume_token(&Token::LParen) {
+ let left = self.parse_data_type()?;
+ self.expect_token(&Token::Comma)?;
+ let right = self.parse_data_type()?;
+ self.expect_token(&Token::RParen)?;
+ Some(OperatorArgTypes { left, right })
Review Comment:
Are we missing test coverage for this block?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
Review Comment:
I think since we're only expecting one of each clause, we might need to add
guards to the match clauses - e.g. `Keyword::Hashes if !hashes => {...}
Keyword::LEFTARG if left_arg.is_none() => {...}`
it would be good to add negative tests that exercise such behavior.
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
Review Comment:
can we add test cases that use qualified operator names where applicable?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
Review Comment:
Can we return an error here instead of crashing in the case of a bug?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html)
+ pub fn parse_create_operator_class(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ let default = self.parse_keyword(Keyword::DEFAULT);
+ self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+ let for_type = self.parse_data_type()?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ let family = if self.parse_keyword(Keyword::FAMILY) {
+ Some(self.parse_object_name(false)?)
+ } else {
+ None
+ };
+
+ self.expect_keyword(Keyword::AS)?;
+
+ let mut items = vec![];
+ loop {
+ if self.parse_keyword(Keyword::OPERATOR) {
+ let strategy_number = self.parse_literal_uint()? as u32;
+ let operator_name = self.parse_operator_name()?;
+
+ // Optional operator argument types
+ let op_types = if self.consume_token(&Token::LParen) {
+ let left = self.parse_data_type()?;
+ self.expect_token(&Token::Comma)?;
+ let right = self.parse_data_type()?;
+ self.expect_token(&Token::RParen)?;
+ Some(OperatorArgTypes { left, right })
+ } else {
+ None
+ };
+
+ // Optional purpose
+ let purpose = if self.parse_keyword(Keyword::FOR) {
+ if self.parse_keyword(Keyword::SEARCH) {
+ Some(OperatorPurpose::ForSearch)
+ } else if self.parse_keywords(&[Keyword::ORDER,
Keyword::BY]) {
+ let sort_family = self.parse_object_name(false)?;
+ Some(OperatorPurpose::ForOrderBy { sort_family })
+ } else {
+ return self.expected("SEARCH or ORDER BY after FOR",
self.peek_token());
+ }
Review Comment:
Are we missing test coverage for this block?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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 `CREATE OPERATOR FAMILY` statement
```
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html)
+ pub fn parse_create_operator_class(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ let default = self.parse_keyword(Keyword::DEFAULT);
+ self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+ let for_type = self.parse_data_type()?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ let family = if self.parse_keyword(Keyword::FAMILY) {
+ Some(self.parse_object_name(false)?)
+ } else {
+ None
+ };
+
+ self.expect_keyword(Keyword::AS)?;
+
+ let mut items = vec![];
+ loop {
+ if self.parse_keyword(Keyword::OPERATOR) {
+ let strategy_number = self.parse_literal_uint()? as u32;
+ let operator_name = self.parse_operator_name()?;
+
+ // Optional operator argument types
+ let op_types = if self.consume_token(&Token::LParen) {
+ let left = self.parse_data_type()?;
+ self.expect_token(&Token::Comma)?;
+ let right = self.parse_data_type()?;
+ self.expect_token(&Token::RParen)?;
+ Some(OperatorArgTypes { left, right })
+ } else {
+ None
+ };
+
+ // Optional purpose
+ let purpose = if self.parse_keyword(Keyword::FOR) {
+ if self.parse_keyword(Keyword::SEARCH) {
+ Some(OperatorPurpose::ForSearch)
+ } else if self.parse_keywords(&[Keyword::ORDER,
Keyword::BY]) {
+ let sort_family = self.parse_object_name(false)?;
+ Some(OperatorPurpose::ForOrderBy { sort_family })
+ } else {
+ return self.expected("SEARCH or ORDER BY after FOR",
self.peek_token());
+ }
+ } else {
+ None
+ };
+
+ items.push(OperatorClassItem::Operator {
+ strategy_number,
+ operator_name,
+ op_types,
+ purpose,
+ });
+ } else if self.parse_keyword(Keyword::FUNCTION) {
+ let support_number = self.parse_literal_uint()? as u32;
+
+ // Optional operator types
+ let op_types =
+ if self.consume_token(&Token::LParen) && self.peek_token()
!= Token::RParen {
+ let mut types = vec![];
+ loop {
+ types.push(self.parse_data_type()?);
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+ self.expect_token(&Token::RParen)?;
+ Some(types)
Review Comment:
can we add test coverage for this block?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html)
+ pub fn parse_create_operator_class(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ let default = self.parse_keyword(Keyword::DEFAULT);
+ self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+ let for_type = self.parse_data_type()?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ let family = if self.parse_keyword(Keyword::FAMILY) {
+ Some(self.parse_object_name(false)?)
+ } else {
+ None
+ };
+
+ self.expect_keyword(Keyword::AS)?;
+
+ let mut items = vec![];
+ loop {
+ if self.parse_keyword(Keyword::OPERATOR) {
+ let strategy_number = self.parse_literal_uint()? as u32;
+ let operator_name = self.parse_operator_name()?;
+
+ // Optional operator argument types
+ let op_types = if self.consume_token(&Token::LParen) {
+ let left = self.parse_data_type()?;
+ self.expect_token(&Token::Comma)?;
+ let right = self.parse_data_type()?;
+ self.expect_token(&Token::RParen)?;
+ Some(OperatorArgTypes { left, right })
+ } else {
+ None
+ };
+
+ // Optional purpose
+ let purpose = if self.parse_keyword(Keyword::FOR) {
+ if self.parse_keyword(Keyword::SEARCH) {
+ Some(OperatorPurpose::ForSearch)
+ } else if self.parse_keywords(&[Keyword::ORDER,
Keyword::BY]) {
+ let sort_family = self.parse_object_name(false)?;
+ Some(OperatorPurpose::ForOrderBy { sort_family })
+ } else {
+ return self.expected("SEARCH or ORDER BY after FOR",
self.peek_token());
+ }
+ } else {
+ None
+ };
+
+ items.push(OperatorClassItem::Operator {
+ strategy_number,
+ operator_name,
+ op_types,
+ purpose,
+ });
+ } else if self.parse_keyword(Keyword::FUNCTION) {
+ let support_number = self.parse_literal_uint()? as u32;
+
+ // Optional operator types
+ let op_types =
+ if self.consume_token(&Token::LParen) && self.peek_token()
!= Token::RParen {
Review Comment:
I think there might be a bug in the conditions due to the use of
`consume_token()` here - note that the `else if` branch is also trying to
consume `LParen`, so that unless `(()` is valid syntax, the else block looks
ineffectual. We probably want to instead do something like
```rust
if self.parse_keywords([LParen, RParen]) { ... }
else if self.parse_keyword(LParen) { ... }
else { None }
```
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html)
+ pub fn parse_create_operator_class(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ let default = self.parse_keyword(Keyword::DEFAULT);
+ self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+ let for_type = self.parse_data_type()?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ let family = if self.parse_keyword(Keyword::FAMILY) {
+ Some(self.parse_object_name(false)?)
+ } else {
+ None
+ };
+
+ self.expect_keyword(Keyword::AS)?;
+
+ let mut items = vec![];
+ loop {
+ if self.parse_keyword(Keyword::OPERATOR) {
+ let strategy_number = self.parse_literal_uint()? as u32;
+ let operator_name = self.parse_operator_name()?;
+
+ // Optional operator argument types
+ let op_types = if self.consume_token(&Token::LParen) {
+ let left = self.parse_data_type()?;
+ self.expect_token(&Token::Comma)?;
+ let right = self.parse_data_type()?;
+ self.expect_token(&Token::RParen)?;
+ Some(OperatorArgTypes { left, right })
+ } else {
+ None
+ };
+
+ // Optional purpose
+ let purpose = if self.parse_keyword(Keyword::FOR) {
+ if self.parse_keyword(Keyword::SEARCH) {
+ Some(OperatorPurpose::ForSearch)
+ } else if self.parse_keywords(&[Keyword::ORDER,
Keyword::BY]) {
+ let sort_family = self.parse_object_name(false)?;
+ Some(OperatorPurpose::ForOrderBy { sort_family })
+ } else {
+ return self.expected("SEARCH or ORDER BY after FOR",
self.peek_token());
+ }
+ } else {
+ None
+ };
+
+ items.push(OperatorClassItem::Operator {
+ strategy_number,
+ operator_name,
+ op_types,
+ purpose,
+ });
+ } else if self.parse_keyword(Keyword::FUNCTION) {
+ let support_number = self.parse_literal_uint()? as u32;
+
+ // Optional operator types
+ let op_types =
+ if self.consume_token(&Token::LParen) && self.peek_token()
!= Token::RParen {
+ let mut types = vec![];
+ loop {
+ types.push(self.parse_data_type()?);
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+ self.expect_token(&Token::RParen)?;
+ Some(types)
+ } else if self.consume_token(&Token::LParen) {
+ self.expect_token(&Token::RParen)?;
+ Some(vec![])
+ } else {
+ None
+ };
+
+ let function_name = self.parse_object_name(false)?;
+
+ // Function argument types
+ let argument_types = if self.consume_token(&Token::LParen) {
+ let mut types = vec![];
+ loop {
+ if self.peek_token() == Token::RParen {
+ break;
+ }
+ types.push(self.parse_data_type()?);
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+ self.expect_token(&Token::RParen)?;
+ types
+ } else {
+ vec![]
Review Comment:
Can we add test coverage for this block?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html)
+ pub fn parse_create_operator_class(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ let default = self.parse_keyword(Keyword::DEFAULT);
+ self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+ let for_type = self.parse_data_type()?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ let family = if self.parse_keyword(Keyword::FAMILY) {
+ Some(self.parse_object_name(false)?)
+ } else {
+ None
+ };
+
+ self.expect_keyword(Keyword::AS)?;
+
+ let mut items = vec![];
+ loop {
+ if self.parse_keyword(Keyword::OPERATOR) {
+ let strategy_number = self.parse_literal_uint()? as u32;
+ let operator_name = self.parse_operator_name()?;
+
+ // Optional operator argument types
+ let op_types = if self.consume_token(&Token::LParen) {
+ let left = self.parse_data_type()?;
+ self.expect_token(&Token::Comma)?;
+ let right = self.parse_data_type()?;
+ self.expect_token(&Token::RParen)?;
+ Some(OperatorArgTypes { left, right })
+ } else {
+ None
+ };
+
+ // Optional purpose
+ let purpose = if self.parse_keyword(Keyword::FOR) {
+ if self.parse_keyword(Keyword::SEARCH) {
+ Some(OperatorPurpose::ForSearch)
+ } else if self.parse_keywords(&[Keyword::ORDER,
Keyword::BY]) {
+ let sort_family = self.parse_object_name(false)?;
+ Some(OperatorPurpose::ForOrderBy { sort_family })
+ } else {
+ return self.expected("SEARCH or ORDER BY after FOR",
self.peek_token());
+ }
+ } else {
+ None
+ };
+
+ items.push(OperatorClassItem::Operator {
+ strategy_number,
+ operator_name,
+ op_types,
+ purpose,
+ });
+ } else if self.parse_keyword(Keyword::FUNCTION) {
+ let support_number = self.parse_literal_uint()? as u32;
+
+ // Optional operator types
+ let op_types =
+ if self.consume_token(&Token::LParen) && self.peek_token()
!= Token::RParen {
+ let mut types = vec![];
+ loop {
+ types.push(self.parse_data_type()?);
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+ self.expect_token(&Token::RParen)?;
+ Some(types)
+ } else if self.consume_token(&Token::LParen) {
+ self.expect_token(&Token::RParen)?;
+ Some(vec![])
Review Comment:
similarly, can we add test coverage for this block?
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
Review Comment:
Can we add test coverage for hashes, merges etc? we can do a pass through
the clauses to ensure that they're properly covered
##########
src/parser/mod.rs:
##########
@@ -6422,6 +6431,291 @@ impl<'a> Parser<'a> {
}))
}
+ /// Helper function to parse an operator name (which can contain special
characters)
+ /// Operator names can be schema-qualified (e.g., schema.operator)
+ fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> {
+ let mut name_parts = vec![];
+ loop {
+ let token = self.next_token();
+ let part =
ObjectNamePart::Identifier(Ident::new(token.to_string()));
+ name_parts.push(part);
+
+ if !self.consume_token(&Token::Period) {
+ break;
+ }
+ }
+ Ok(ObjectName(name_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> {
+ // Parse the operator name (can be schema-qualified)
+ // Operators can contain special characters like +, -, *, /, <, >, =,
~, !, @, #, %, ^, &, |, `, ?
+ // See https://www.postgresql.org/docs/current/sql-createoperator.html
+ let name = self.parse_operator_name()?;
+
+ // Expect opening parenthesis
+ 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 {
+ // Parse parameter name as keyword
+ 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,
+ ])?;
+
+ // Check if this is a flag (HASHES or MERGES) - no '=' expected
+ match keyword {
+ Keyword::HASHES => {
+ hashes = true;
+ }
+ Keyword::MERGES => {
+ merges = true;
+ }
+ Keyword::FUNCTION | Keyword::PROCEDURE => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ function = Some(func_name);
+ is_procedure = keyword == Keyword::PROCEDURE;
+ }
+ Keyword::LEFTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ left_arg = Some(data_type);
+ }
+ Keyword::RIGHTARG => {
+ self.expect_token(&Token::Eq)?;
+ let data_type = self.parse_data_type()?;
+ right_arg = Some(data_type);
+ }
+ Keyword::COMMUTATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ commutator = Some(op_name);
+ }
+ Keyword::NEGATOR => {
+ self.expect_token(&Token::Eq)?;
+ let op_name = if self.parse_keyword(Keyword::OPERATOR) {
+ self.expect_token(&Token::LParen)?;
+ let op = self.parse_operator_name()?;
+ self.expect_token(&Token::RParen)?;
+ op
+ } else {
+ self.parse_operator_name()?
+ };
+ negator = Some(op_name);
+ }
+ Keyword::RESTRICT => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ restrict = Some(func_name);
+ }
+ Keyword::JOIN => {
+ self.expect_token(&Token::Eq)?;
+ let func_name = self.parse_object_name(false)?;
+ join = Some(func_name);
+ }
+ _ => unreachable!("unexpected keyword in CREATE OPERATOR"),
+ }
+
+ // Check for comma or closing parenthesis
+ 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
+ ///
+ /// [PostgreSQL
Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html)
+ pub fn parse_create_operator_class(&mut self) -> Result<Statement,
ParserError> {
+ let name = self.parse_object_name(false)?;
+ let default = self.parse_keyword(Keyword::DEFAULT);
+ self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+ let for_type = self.parse_data_type()?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+
+ let family = if self.parse_keyword(Keyword::FAMILY) {
+ Some(self.parse_object_name(false)?)
+ } else {
+ None
+ };
+
+ self.expect_keyword(Keyword::AS)?;
+
+ let mut items = vec![];
+ loop {
+ if self.parse_keyword(Keyword::OPERATOR) {
+ let strategy_number = self.parse_literal_uint()? as u32;
+ let operator_name = self.parse_operator_name()?;
+
+ // Optional operator argument types
+ let op_types = if self.consume_token(&Token::LParen) {
+ let left = self.parse_data_type()?;
+ self.expect_token(&Token::Comma)?;
+ let right = self.parse_data_type()?;
+ self.expect_token(&Token::RParen)?;
+ Some(OperatorArgTypes { left, right })
+ } else {
+ None
+ };
+
+ // Optional purpose
+ let purpose = if self.parse_keyword(Keyword::FOR) {
+ if self.parse_keyword(Keyword::SEARCH) {
+ Some(OperatorPurpose::ForSearch)
+ } else if self.parse_keywords(&[Keyword::ORDER,
Keyword::BY]) {
+ let sort_family = self.parse_object_name(false)?;
+ Some(OperatorPurpose::ForOrderBy { sort_family })
+ } else {
+ return self.expected("SEARCH or ORDER BY after FOR",
self.peek_token());
+ }
+ } else {
+ None
+ };
+
+ items.push(OperatorClassItem::Operator {
+ strategy_number,
+ operator_name,
+ op_types,
+ purpose,
+ });
+ } else if self.parse_keyword(Keyword::FUNCTION) {
+ let support_number = self.parse_literal_uint()? as u32;
+
+ // Optional operator types
+ let op_types =
+ if self.consume_token(&Token::LParen) && self.peek_token()
!= Token::RParen {
+ let mut types = vec![];
+ loop {
+ types.push(self.parse_data_type()?);
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+ self.expect_token(&Token::RParen)?;
+ Some(types)
+ } else if self.consume_token(&Token::LParen) {
+ self.expect_token(&Token::RParen)?;
+ Some(vec![])
+ } else {
+ None
+ };
+
+ let function_name = self.parse_object_name(false)?;
+
+ // Function argument types
+ let argument_types = if self.consume_token(&Token::LParen) {
+ let mut types = vec![];
+ loop {
+ if self.peek_token() == Token::RParen {
+ break;
+ }
+ types.push(self.parse_data_type()?);
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+ self.expect_token(&Token::RParen)?;
+ types
+ } else {
+ vec![]
+ };
+
+ items.push(OperatorClassItem::Function {
+ support_number,
+ op_types,
+ function_name,
+ argument_types,
+ });
+ } else if self.parse_keyword(Keyword::STORAGE) {
+ let storage_type = self.parse_data_type()?;
+ items.push(OperatorClassItem::Storage { storage_type });
Review Comment:
do we have tests for this part of the syntax?
--
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]