From 44bee97de31bdac816bee009202d18f5477ffbfe Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 5 Aug 2022 13:27:41 -0400 Subject: [PATCH] Parse `OPERATOR` as Vec --- src/ast/mod.rs | 2 +- src/ast/operator.rs | 26 +++++++++----------------- src/parser.rs | 32 +++++++++++--------------------- tests/sqlparser_postgres.rs | 29 +++++++++++++++++++++-------- 4 files changed, 42 insertions(+), 47 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 8286e233b..c3dd65782 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -33,7 +33,7 @@ pub use self::ddl::{ AlterColumnOperation, AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, ReferentialAction, TableConstraint, }; -pub use self::operator::{BinaryOperator, PGCustomOperator, UnaryOperator}; +pub use self::operator::{BinaryOperator, UnaryOperator}; pub use self::query::{ Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType, Offset, OffsetRows, OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, TableAlias, diff --git a/src/ast/operator.rs b/src/ast/operator.rs index dfbde965a..a274a0acc 100644 --- a/src/ast/operator.rs +++ b/src/ast/operator.rs @@ -17,6 +17,8 @@ use alloc::string::String; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use super::display_separated; + /// Unary operators #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -88,17 +90,11 @@ pub enum BinaryOperator { PGRegexIMatch, PGRegexNotMatch, PGRegexNotIMatch, - PGCustomBinaryOperator(PGCustomOperator), -} - -/// PostgreSQL-specific custom operator. -/// -/// See [CREATE OPERATOR](https://www.postgresql.org/docs/current/sql-createoperator.html) for more information. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PGCustomOperator { - pub schema: Option, - pub name: String, + /// PostgreSQL-specific custom operator. + /// + /// See [CREATE OPERATOR](https://www.postgresql.org/docs/current/sql-createoperator.html) + /// for more information. + PGCustomBinaryOperator(Vec), } impl fmt::Display for BinaryOperator { @@ -134,12 +130,8 @@ impl fmt::Display for BinaryOperator { BinaryOperator::PGRegexIMatch => f.write_str("~*"), BinaryOperator::PGRegexNotMatch => f.write_str("!~"), BinaryOperator::PGRegexNotIMatch => f.write_str("!~*"), - BinaryOperator::PGCustomBinaryOperator(ref custom_operator) => { - write!(f, "OPERATOR(")?; - if let Some(ref schema) = custom_operator.schema { - write!(f, "{}.", schema)?; - } - write!(f, "{})", custom_operator.name) + BinaryOperator::PGCustomBinaryOperator(idents) => { + write!(f, "OPERATOR({})", display_separated(idents, ".")) } } } diff --git a/src/parser.rs b/src/parser.rs index 8eb637ca1..00ee87c03 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1161,29 +1161,19 @@ impl<'a> Parser<'a> { Keyword::XOR => Some(BinaryOperator::Xor), Keyword::OPERATOR if dialect_of!(self is PostgreSqlDialect | GenericDialect) => { self.expect_token(&Token::LParen)?; - let token_1 = self.peek_nth_token(1); - - let custom_operator = match token_1 { - Token::Period => { - let schema = self.parse_identifier()?; - self.expect_token(&Token::Period)?; - let operator = self.next_token(); - PGCustomOperator { - schema: Some(schema.value), - name: operator.to_string(), - } - } - _ => { - let operator = self.next_token(); - PGCustomOperator { - schema: None, - name: operator.to_string(), - } + // there are special rules for operator names in + // postgres so we can not use 'parse_object' + // or similar. + // See https://www.postgresql.org/docs/current/sql-createoperator.html + let mut idents = vec![]; + loop { + idents.push(self.next_token().to_string()); + if !self.consume_token(&Token::Period) { + break; } - }; - + } self.expect_token(&Token::RParen)?; - Some(BinaryOperator::PGCustomBinaryOperator(custom_operator)) + Some(BinaryOperator::PGCustomBinaryOperator(idents)) } _ => None, }, diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 662f92833..a3002b089 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1567,6 +1567,25 @@ fn parse_fetch() { #[test] fn parse_custom_operator() { + // operator with a database and schema + let sql = r#"SELECT * FROM events WHERE relname OPERATOR(database.pg_catalog.~) '^(table)$'"#; + let select = pg().verified_only_select(sql); + assert_eq!( + select.selection, + Some(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident { + value: "relname".into(), + quote_style: None, + })), + op: BinaryOperator::PGCustomBinaryOperator(vec![ + "database".into(), + "pg_catalog".into(), + "~".into() + ]), + right: Box::new(Expr::Value(Value::SingleQuotedString("^(table)$".into()))) + }) + ); + // operator with a schema let sql = r#"SELECT * FROM events WHERE relname OPERATOR(pg_catalog.~) '^(table)$'"#; let select = pg().verified_only_select(sql); @@ -1577,10 +1596,7 @@ fn parse_custom_operator() { value: "relname".into(), quote_style: None, })), - op: BinaryOperator::PGCustomBinaryOperator(PGCustomOperator { - schema: Some("pg_catalog".into()), - name: "~".into(), - }), + op: BinaryOperator::PGCustomBinaryOperator(vec!["pg_catalog".into(), "~".into()]), right: Box::new(Expr::Value(Value::SingleQuotedString("^(table)$".into()))) }) ); @@ -1595,10 +1611,7 @@ fn parse_custom_operator() { value: "relname".into(), quote_style: None, })), - op: BinaryOperator::PGCustomBinaryOperator(PGCustomOperator { - schema: None, - name: "~".into(), - }), + op: BinaryOperator::PGCustomBinaryOperator(vec!["~".into()]), right: Box::new(Expr::Value(Value::SingleQuotedString("^(table)$".into()))) }) );