This is an automated email from the ASF dual-hosted git repository.

iffyio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 339239d0 Add support for PostgreSQL/Redshift geometric operators 
(#1723)
339239d0 is described below

commit 339239d0c54fd0fe12e05d8de868340d07030ffa
Author: benrsatori <[email protected]>
AuthorDate: Thu Feb 20 20:50:32 2025 +0200

    Add support for PostgreSQL/Redshift geometric operators (#1723)
---
 src/ast/data_type.rs        |  36 +++++
 src/ast/mod.rs              |  12 +-
 src/ast/operator.rs         |  84 +++++++++++
 src/dialect/mod.rs          |  36 ++++-
 src/dialect/postgresql.rs   |   4 +
 src/dialect/redshift.rs     |   4 +
 src/keywords.rs             |   6 +
 src/parser/mod.rs           | 107 ++++++++++++-
 src/tokenizer.rs            | 184 +++++++++++++++++++++--
 tests/sqlparser_common.rs   | 358 ++++++++++++++++++++++++++++++++++++++++++++
 tests/sqlparser_postgres.rs |   4 -
 11 files changed, 809 insertions(+), 26 deletions(-)

diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs
index bd46f8bd..cae8ca8f 100644
--- a/src/ast/data_type.rs
+++ b/src/ast/data_type.rs
@@ -386,6 +386,10 @@ pub enum DataType {
     ///
     /// [bigquery]: 
https://cloud.google.com/bigquery/docs/user-defined-functions#templated-sql-udf-parameters
     AnyType,
+    /// geometric type
+    ///
+    /// [Postgres]: https://www.postgresql.org/docs/9.5/functions-geometry.html
+    GeometricType(GeometricTypeKind),
 }
 
 impl fmt::Display for DataType {
@@ -639,6 +643,7 @@ impl fmt::Display for DataType {
             DataType::Trigger => write!(f, "TRIGGER"),
             DataType::AnyType => write!(f, "ANY TYPE"),
             DataType::Table(fields) => write!(f, "TABLE({})", 
display_comma_separated(fields)),
+            DataType::GeometricType(kind) => write!(f, "{}", kind),
         }
     }
 }
@@ -915,3 +920,34 @@ pub enum ArrayElemTypeDef {
     /// `Array(Int64)`
     Parenthesis(Box<DataType>),
 }
+
+/// Represents different types of geometric shapes which are commonly used in
+/// PostgreSQL/Redshift for spatial operations and geometry-related 
computations.
+///
+/// [Postgres]: https://www.postgresql.org/docs/9.5/functions-geometry.html
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum GeometricTypeKind {
+    Point,
+    Line,
+    LineSegment,
+    GeometricBox,
+    GeometricPath,
+    Polygon,
+    Circle,
+}
+
+impl fmt::Display for GeometricTypeKind {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            GeometricTypeKind::Point => write!(f, "point"),
+            GeometricTypeKind::Line => write!(f, "line"),
+            GeometricTypeKind::LineSegment => write!(f, "lseg"),
+            GeometricTypeKind::GeometricBox => write!(f, "box"),
+            GeometricTypeKind::GeometricPath => write!(f, "path"),
+            GeometricTypeKind::Polygon => write!(f, "polygon"),
+            GeometricTypeKind::Circle => write!(f, "circle"),
+        }
+    }
+}
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index efdad164..dc50871c 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -95,6 +95,8 @@ use crate::ast::helpers::stmt_data_loading::{
 #[cfg(feature = "visitor")]
 pub use visitor::*;
 
+pub use self::data_type::GeometricTypeKind;
+
 mod data_type;
 mod dcl;
 mod ddl;
@@ -1513,7 +1515,15 @@ impl fmt::Display for Expr {
             Expr::UnaryOp { op, expr } => {
                 if op == &UnaryOperator::PGPostfixFactorial {
                     write!(f, "{expr}{op}")
-                } else if op == &UnaryOperator::Not {
+                } else if matches!(
+                    op,
+                    UnaryOperator::Not
+                        | UnaryOperator::Hash
+                        | UnaryOperator::AtDashAt
+                        | UnaryOperator::DoubleAt
+                        | UnaryOperator::QuestionDash
+                        | UnaryOperator::QuestionPipe
+                ) {
                     write!(f, "{op} {expr}")
                 } else {
                     write!(f, "{op}{expr}")
diff --git a/src/ast/operator.rs b/src/ast/operator.rs
index 1f9a6b82..66a35fee 100644
--- a/src/ast/operator.rs
+++ b/src/ast/operator.rs
@@ -53,6 +53,21 @@ pub enum UnaryOperator {
     PGAbs,
     /// Unary logical not operator: e.g. `! false` (Hive-specific)
     BangNot,
+    /// `#` Number of points in path or polygon (PostgreSQL/Redshift geometric 
operator)
+    /// see <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    Hash,
+    /// `@-@` Length or circumference (PostgreSQL/Redshift geometric operator)
+    /// see <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    AtDashAt,
+    /// `@@` Center (PostgreSQL/Redshift geometric operator)
+    /// see <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    DoubleAt,
+    /// `?-` Is horizontal? (PostgreSQL/Redshift geometric operator)
+    /// see <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    QuestionDash,
+    /// `?|` Is vertical? (PostgreSQL/Redshift geometric operator)
+    /// see <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    QuestionPipe,
 }
 
 impl fmt::Display for UnaryOperator {
@@ -68,6 +83,11 @@ impl fmt::Display for UnaryOperator {
             UnaryOperator::PGPrefixFactorial => "!!",
             UnaryOperator::PGAbs => "@",
             UnaryOperator::BangNot => "!",
+            UnaryOperator::Hash => "#",
+            UnaryOperator::AtDashAt => "@-@",
+            UnaryOperator::DoubleAt => "@@",
+            UnaryOperator::QuestionDash => "?-",
+            UnaryOperator::QuestionPipe => "?|",
         })
     }
 }
@@ -253,6 +273,54 @@ pub enum BinaryOperator {
     /// Specifies a test for an overlap between two datetime periods:
     /// 
<https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#overlaps-predicate>
     Overlaps,
+    /// `##` Point of closest proximity (PostgreSQL/Redshift geometric 
operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    DoubleHash,
+    /// `<->` Distance between (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    LtDashGt,
+    /// `&<` Overlaps to left? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    AndLt,
+    /// `&>` Overlaps to right? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    AndGt,
+    /// `<<|` Is strictly below? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    LtLtPipe,
+    /// `|>>` Is strictly above? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    PipeGtGt,
+    /// `&<|` Does not extend above? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    AndLtPipe,
+    /// `|&>` Does not extend below? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    PipeAndGt,
+    /// `<^` Is below? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    LtCaret,
+    /// `>^` Is above? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    GtCaret,
+    /// `?#` Intersects? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    QuestionHash,
+    /// `?-` Is horizontal? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    QuestionDash,
+    /// `?-|` Is perpendicular? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    QuestionDashPipe,
+    /// `?||` Are Parallel? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    QuestionDoublePipe,
+    /// `@` Contained or on? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    At,
+    /// `~=` Same as? (PostgreSQL/Redshift geometric operator)
+    /// See <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    TildeEq,
 }
 
 impl fmt::Display for BinaryOperator {
@@ -310,6 +378,22 @@ impl fmt::Display for BinaryOperator {
                 write!(f, "OPERATOR({})", display_separated(idents, "."))
             }
             BinaryOperator::Overlaps => f.write_str("OVERLAPS"),
+            BinaryOperator::DoubleHash => f.write_str("##"),
+            BinaryOperator::LtDashGt => f.write_str("<->"),
+            BinaryOperator::AndLt => f.write_str("&<"),
+            BinaryOperator::AndGt => f.write_str("&>"),
+            BinaryOperator::LtLtPipe => f.write_str("<<|"),
+            BinaryOperator::PipeGtGt => f.write_str("|>>"),
+            BinaryOperator::AndLtPipe => f.write_str("&<|"),
+            BinaryOperator::PipeAndGt => f.write_str("|&>"),
+            BinaryOperator::LtCaret => f.write_str("<^"),
+            BinaryOperator::GtCaret => f.write_str(">^"),
+            BinaryOperator::QuestionHash => f.write_str("?#"),
+            BinaryOperator::QuestionDash => f.write_str("?-"),
+            BinaryOperator::QuestionDashPipe => f.write_str("?-|"),
+            BinaryOperator::QuestionDoublePipe => f.write_str("?||"),
+            BinaryOperator::At => f.write_str("@"),
+            BinaryOperator::TildeEq => f.write_str("~="),
         }
     }
 }
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index cf267a0f..86e23c86 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -599,18 +599,34 @@ pub trait Dialect: Debug + Any {
             | Token::ExclamationMarkDoubleTilde
             | Token::ExclamationMarkDoubleTildeAsterisk
             | Token::Spaceship => Ok(p!(Eq)),
-            Token::Pipe => Ok(p!(Pipe)),
+            Token::Pipe
+            | Token::QuestionMarkDash
+            | Token::DoubleSharp
+            | Token::Overlap
+            | Token::AmpersandLeftAngleBracket
+            | Token::AmpersandRightAngleBracket
+            | Token::QuestionMarkDashVerticalBar
+            | Token::AmpersandLeftAngleBracketVerticalBar
+            | Token::VerticalBarAmpersandRightAngleBracket
+            | Token::TwoWayArrow
+            | Token::LeftAngleBracketCaret
+            | Token::RightAngleBracketCaret
+            | Token::QuestionMarkSharp
+            | Token::QuestionMarkDoubleVerticalBar
+            | Token::QuestionPipe
+            | Token::TildeEqual
+            | Token::AtSign
+            | Token::ShiftLeftVerticalBar
+            | Token::VerticalBarShiftRight => Ok(p!(Pipe)),
             Token::Caret | Token::Sharp | Token::ShiftRight | Token::ShiftLeft 
=> Ok(p!(Caret)),
             Token::Ampersand => Ok(p!(Ampersand)),
             Token::Plus | Token::Minus => Ok(p!(PlusMinus)),
             Token::Mul | Token::Div | Token::DuckIntDiv | Token::Mod | 
Token::StringConcat => {
                 Ok(p!(MulDivModOp))
             }
-            Token::DoubleColon
-            | Token::ExclamationMark
-            | Token::LBracket
-            | Token::Overlap
-            | Token::CaretAt => Ok(p!(DoubleColon)),
+            Token::DoubleColon | Token::ExclamationMark | Token::LBracket | 
Token::CaretAt => {
+                Ok(p!(DoubleColon))
+            }
             Token::Arrow
             | Token::LongArrow
             | Token::HashArrow
@@ -622,7 +638,6 @@ pub trait Dialect: Debug + Any {
             | Token::AtAt
             | Token::Question
             | Token::QuestionAnd
-            | Token::QuestionPipe
             | Token::CustomBinaryOperator(_) => Ok(p!(PgOther)),
             _ => Ok(self.prec_unknown()),
         }
@@ -921,6 +936,13 @@ pub trait Dialect: Debug + Any {
     fn supports_array_typedef_size(&self) -> bool {
         false
     }
+    /// Returns true if the dialect supports geometric types.
+    ///
+    /// Postgres: <https://www.postgresql.org/docs/9.5/functions-geometry.html>
+    /// e.g. @@ circle '((0,0),10)'
+    fn supports_geometric_types(&self) -> bool {
+        false
+    }
 }
 
 /// This represents the operators for which precedence must be defined
diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs
index 3a3f0e4c..a20cfac4 100644
--- a/src/dialect/postgresql.rs
+++ b/src/dialect/postgresql.rs
@@ -250,4 +250,8 @@ impl Dialect for PostgreSqlDialect {
     fn supports_array_typedef_size(&self) -> bool {
         true
     }
+
+    fn supports_geometric_types(&self) -> bool {
+        true
+    }
 }
diff --git a/src/dialect/redshift.rs b/src/dialect/redshift.rs
index a4522bbf..3dda762c 100644
--- a/src/dialect/redshift.rs
+++ b/src/dialect/redshift.rs
@@ -113,4 +113,8 @@ impl Dialect for RedshiftSqlDialect {
     fn supports_string_escape_constant(&self) -> bool {
         true
     }
+
+    fn supports_geometric_types(&self) -> bool {
+        true
+    }
 }
diff --git a/src/keywords.rs b/src/keywords.rs
index a67f6bc7..46c40fba 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -141,6 +141,7 @@ define_keywords!(
     BOOL,
     BOOLEAN,
     BOTH,
+    BOX,
     BROWSE,
     BTREE,
     BUCKET,
@@ -175,6 +176,7 @@ define_keywords!(
     CHARSET,
     CHAR_LENGTH,
     CHECK,
+    CIRCLE,
     CLEAR,
     CLOB,
     CLONE,
@@ -478,6 +480,7 @@ define_keywords!(
     LIKE,
     LIKE_REGEX,
     LIMIT,
+    LINE,
     LINES,
     LIST,
     LISTEN,
@@ -499,6 +502,7 @@ define_keywords!(
     LOWER,
     LOW_PRIORITY,
     LS,
+    LSEG,
     MACRO,
     MANAGE,
     MANAGED,
@@ -653,7 +657,9 @@ define_keywords!(
     PLACING,
     PLAN,
     PLANS,
+    POINT,
     POLICY,
+    POLYGON,
     POOL,
     PORTION,
     POSITION,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 9c021d91..69268bc5 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -1220,7 +1220,17 @@ impl<'a> Parser<'a> {
             Keyword::MAP if *self.peek_token_ref() == Token::LBrace && 
self.dialect.support_map_literal_syntax() => {
                 Ok(Some(self.parse_duckdb_map_literal()?))
             }
-            _ => Ok(None)
+            _ if self.dialect.supports_geometric_types() => match w.keyword {
+                Keyword::CIRCLE => 
Ok(Some(self.parse_geometric_type(GeometricTypeKind::Circle)?)),
+                Keyword::BOX => 
Ok(Some(self.parse_geometric_type(GeometricTypeKind::GeometricBox)?)),
+                Keyword::PATH => 
Ok(Some(self.parse_geometric_type(GeometricTypeKind::GeometricPath)?)),
+                Keyword::LINE => 
Ok(Some(self.parse_geometric_type(GeometricTypeKind::Line)?)),
+                Keyword::LSEG => 
Ok(Some(self.parse_geometric_type(GeometricTypeKind::LineSegment)?)),
+                Keyword::POINT => 
Ok(Some(self.parse_geometric_type(GeometricTypeKind::Point)?)),
+                Keyword::POLYGON => 
Ok(Some(self.parse_geometric_type(GeometricTypeKind::Polygon)?)),
+                _ => Ok(None),
+            },
+            _ => Ok(None),
         }
     }
 
@@ -1400,6 +1410,33 @@ impl<'a> Parser<'a> {
                     ),
                 })
             }
+            tok @ Token::Sharp
+            | tok @ Token::AtDashAt
+            | tok @ Token::AtAt
+            | tok @ Token::QuestionMarkDash
+            | tok @ Token::QuestionPipe
+                if self.dialect.supports_geometric_types() =>
+            {
+                let op = match tok {
+                    Token::Sharp => UnaryOperator::Hash,
+                    Token::AtDashAt => UnaryOperator::AtDashAt,
+                    Token::AtAt => UnaryOperator::DoubleAt,
+                    Token::QuestionMarkDash => UnaryOperator::QuestionDash,
+                    Token::QuestionPipe => UnaryOperator::QuestionPipe,
+                    _ => {
+                        return Err(ParserError::ParserError(format!(
+                            "Unexpected token in unary operator parsing: {:?}",
+                            tok
+                        )))
+                    }
+                };
+                Ok(Expr::UnaryOp {
+                    op,
+                    expr: Box::new(
+                        
self.parse_subexpr(self.dialect.prec_value(Precedence::PlusMinus))?,
+                    ),
+                })
+            }
             Token::EscapedStringLiteral(_) if dialect_is!(dialect is 
PostgreSqlDialect | GenericDialect) =>
             {
                 self.prev_token();
@@ -1465,6 +1502,14 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn parse_geometric_type(&mut self, kind: GeometricTypeKind) -> 
Result<Expr, ParserError> {
+        let value: Value = self.parse_value()?;
+        Ok(Expr::TypedString {
+            data_type: DataType::GeometricType(kind),
+            value,
+        })
+    }
+
     /// Try to parse an [Expr::CompoundFieldAccess] like `a.b.c` or `a.b[1].c`.
     /// If all the fields are `Expr::Identifier`s, return an 
[Expr::CompoundIdentifier] instead.
     /// If only the root exists, return the root.
@@ -3070,15 +3115,18 @@ impl<'a> Parser<'a> {
             Token::DuckIntDiv if dialect_is!(dialect is DuckDbDialect | 
GenericDialect) => {
                 Some(BinaryOperator::DuckIntegerDivide)
             }
-            Token::ShiftLeft if dialect_is!(dialect is PostgreSqlDialect | 
DuckDbDialect | GenericDialect) => {
+            Token::ShiftLeft if dialect_is!(dialect is PostgreSqlDialect | 
DuckDbDialect | GenericDialect | RedshiftSqlDialect) => {
                 Some(BinaryOperator::PGBitwiseShiftLeft)
             }
-            Token::ShiftRight if dialect_is!(dialect is PostgreSqlDialect | 
DuckDbDialect | GenericDialect) => {
+            Token::ShiftRight if dialect_is!(dialect is PostgreSqlDialect | 
DuckDbDialect | GenericDialect | RedshiftSqlDialect) => {
                 Some(BinaryOperator::PGBitwiseShiftRight)
             }
-            Token::Sharp if dialect_is!(dialect is PostgreSqlDialect) => {
+            Token::Sharp if dialect_is!(dialect is PostgreSqlDialect | 
RedshiftSqlDialect) => {
                 Some(BinaryOperator::PGBitwiseXor)
             }
+            Token::Overlap if dialect_is!(dialect is PostgreSqlDialect | 
RedshiftSqlDialect) => {
+                Some(BinaryOperator::PGOverlap)
+            }
             Token::Overlap if dialect_is!(dialect is PostgreSqlDialect | 
GenericDialect) => {
                 Some(BinaryOperator::PGOverlap)
             }
@@ -3106,6 +3154,57 @@ impl<'a> Parser<'a> {
             Token::QuestionAnd => Some(BinaryOperator::QuestionAnd),
             Token::QuestionPipe => Some(BinaryOperator::QuestionPipe),
             Token::CustomBinaryOperator(s) => 
Some(BinaryOperator::Custom(s.clone())),
+            Token::DoubleSharp if self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::DoubleHash)
+            }
+
+            Token::AmpersandLeftAngleBracket if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::AndLt)
+            }
+            Token::AmpersandRightAngleBracket if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::AndGt)
+            }
+            Token::QuestionMarkDash if self.dialect.supports_geometric_types() 
=> {
+                Some(BinaryOperator::QuestionDash)
+            }
+            Token::AmpersandLeftAngleBracketVerticalBar
+                if self.dialect.supports_geometric_types() =>
+            {
+                Some(BinaryOperator::AndLtPipe)
+            }
+            Token::VerticalBarAmpersandRightAngleBracket
+                if self.dialect.supports_geometric_types() =>
+            {
+                Some(BinaryOperator::PipeAndGt)
+            }
+            Token::TwoWayArrow if self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::LtDashGt)
+            }
+            Token::LeftAngleBracketCaret if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::LtCaret)
+            }
+            Token::RightAngleBracketCaret if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::GtCaret)
+            }
+            Token::QuestionMarkSharp if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::QuestionHash)
+            }
+            Token::QuestionMarkDoubleVerticalBar if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::QuestionDoublePipe)
+            }
+            Token::QuestionMarkDashVerticalBar if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::QuestionDashPipe)
+            }
+            Token::TildeEqual if self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::TildeEq)
+            }
+            Token::ShiftLeftVerticalBar if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::LtLtPipe)
+            }
+            Token::VerticalBarShiftRight if 
self.dialect.supports_geometric_types() => {
+                Some(BinaryOperator::PipeGtGt)
+            }
+            Token::AtSign if self.dialect.supports_geometric_types() => 
Some(BinaryOperator::At),
 
             Token::Word(w) => match w.keyword {
                 Keyword::AND => Some(BinaryOperator::And),
diff --git a/src/tokenizer.rs b/src/tokenizer.rs
index d4e530c9..bc0f0efe 100644
--- a/src/tokenizer.rs
+++ b/src/tokenizer.rs
@@ -170,8 +170,10 @@ pub enum Token {
     RBrace,
     /// Right Arrow `=>`
     RArrow,
-    /// Sharp `#` used for PostgreSQL Bitwise XOR operator
+    /// Sharp `#` used for PostgreSQL Bitwise XOR operator, also 
PostgreSQL/Redshift geometrical unary/binary operator (Number of points in path 
or polygon/Intersection)
     Sharp,
+    /// `##` PostgreSQL/Redshift geometrical binary operator (Point of closest 
proximity)
+    DoubleSharp,
     /// Tilde `~` used for PostgreSQL Bitwise NOT operator or case sensitive 
match regular expression operator
     Tilde,
     /// `~*` , a case insensitive match regular expression operator in 
PostgreSQL
@@ -198,7 +200,7 @@ pub enum Token {
     ExclamationMark,
     /// Double Exclamation Mark `!!` used for PostgreSQL prefix factorial 
operator
     DoubleExclamationMark,
-    /// AtSign `@` used for PostgreSQL abs operator
+    /// AtSign `@` used for PostgreSQL abs operator, also PostgreSQL/Redshift 
geometrical unary/binary operator (Center, Contained or on)
     AtSign,
     /// `^@`, a "starts with" string operator in PostgreSQL
     CaretAt,
@@ -214,6 +216,36 @@ pub enum Token {
     LongArrow,
     /// `#>`, extracts JSON sub-object at the specified path
     HashArrow,
+    /// `@-@` PostgreSQL/Redshift geometrical unary operator (Length or 
circumference)
+    AtDashAt,
+    /// `?-` PostgreSQL/Redshift geometrical unary/binary operator (Is 
horizontal?/Are horizontally aligned?)
+    QuestionMarkDash,
+    /// `&<` PostgreSQL/Redshift geometrical binary operator (Overlaps to 
left?)
+    AmpersandLeftAngleBracket,
+    /// `&>` PostgreSQL/Redshift geometrical binary operator (Overlaps to 
right?)`
+    AmpersandRightAngleBracket,
+    /// `&<|` PostgreSQL/Redshift geometrical binary operator (Does not extend 
above?)`
+    AmpersandLeftAngleBracketVerticalBar,
+    /// `|&>` PostgreSQL/Redshift geometrical binary operator (Does not extend 
below?)`
+    VerticalBarAmpersandRightAngleBracket,
+    /// `<->` PostgreSQL/Redshift geometrical binary operator (Distance 
between)
+    TwoWayArrow,
+    /// `<^` PostgreSQL/Redshift geometrical binary operator (Is below?)
+    LeftAngleBracketCaret,
+    /// `>^` PostgreSQL/Redshift geometrical binary operator (Is above?)
+    RightAngleBracketCaret,
+    /// `?#` PostgreSQL/Redshift geometrical binary operator (Intersects or 
overlaps)
+    QuestionMarkSharp,
+    /// `?-|` PostgreSQL/Redshift geometrical binary operator (Is 
perpendicular?)
+    QuestionMarkDashVerticalBar,
+    /// `?||` PostgreSQL/Redshift geometrical binary operator (Are parallel?)
+    QuestionMarkDoubleVerticalBar,
+    /// `~=` PostgreSQL/Redshift geometrical binary operator (Same as)
+    TildeEqual,
+    /// `<<| PostgreSQL/Redshift geometrical binary operator (Is strictly 
below?)
+    ShiftLeftVerticalBar,
+    /// `|>> PostgreSQL/Redshift geometrical binary operator (Is strictly 
above?)
+    VerticalBarShiftRight,
     /// `#>>`, extracts JSON sub-object at the specified path as text
     HashLongArrow,
     /// jsonb @> jsonb -> boolean: Test whether left json contains the right 
json
@@ -303,6 +335,7 @@ impl fmt::Display for Token {
             Token::RBrace => f.write_str("}"),
             Token::RArrow => f.write_str("=>"),
             Token::Sharp => f.write_str("#"),
+            Token::DoubleSharp => f.write_str("##"),
             Token::ExclamationMark => f.write_str("!"),
             Token::DoubleExclamationMark => f.write_str("!!"),
             Token::Tilde => f.write_str("~"),
@@ -320,6 +353,21 @@ impl fmt::Display for Token {
             Token::Overlap => f.write_str("&&"),
             Token::PGSquareRoot => f.write_str("|/"),
             Token::PGCubeRoot => f.write_str("||/"),
+            Token::AtDashAt => f.write_str("@-@"),
+            Token::QuestionMarkDash => f.write_str("?-"),
+            Token::AmpersandLeftAngleBracket => f.write_str("&<"),
+            Token::AmpersandRightAngleBracket => f.write_str("&>"),
+            Token::AmpersandLeftAngleBracketVerticalBar => f.write_str("&<|"),
+            Token::VerticalBarAmpersandRightAngleBracket => f.write_str("|&>"),
+            Token::TwoWayArrow => f.write_str("<->"),
+            Token::LeftAngleBracketCaret => f.write_str("<^"),
+            Token::RightAngleBracketCaret => f.write_str(">^"),
+            Token::QuestionMarkSharp => f.write_str("?#"),
+            Token::QuestionMarkDashVerticalBar => f.write_str("?-|"),
+            Token::QuestionMarkDoubleVerticalBar => f.write_str("?||"),
+            Token::TildeEqual => f.write_str("~="),
+            Token::ShiftLeftVerticalBar => f.write_str("<<|"),
+            Token::VerticalBarShiftRight => f.write_str("|>>"),
             Token::Placeholder(ref s) => write!(f, "{s}"),
             Token::Arrow => write!(f, "->"),
             Token::LongArrow => write!(f, "->>"),
@@ -1308,6 +1356,28 @@ impl<'a> Tokenizer<'a> {
                                 _ => self.start_binop(chars, "||", 
Token::StringConcat),
                             }
                         }
+                        Some('&') if self.dialect.supports_geometric_types() 
=> {
+                            chars.next(); // consume
+                            match chars.peek() {
+                                Some('>') => self.consume_for_binop(
+                                    chars,
+                                    "|&>",
+                                    
Token::VerticalBarAmpersandRightAngleBracket,
+                                ),
+                                _ => self.start_binop_opt(chars, "|&", None),
+                            }
+                        }
+                        Some('>') if self.dialect.supports_geometric_types() 
=> {
+                            chars.next(); // consume
+                            match chars.peek() {
+                                Some('>') => self.consume_for_binop(
+                                    chars,
+                                    "|>>",
+                                    Token::VerticalBarShiftRight,
+                                ),
+                                _ => self.start_binop_opt(chars, "|>", None),
+                            }
+                        }
                         // Bitshift '|' operator
                         _ => self.start_binop(chars, "|", Token::Pipe),
                     }
@@ -1356,8 +1426,34 @@ impl<'a> Tokenizer<'a> {
                                 _ => self.start_binop(chars, "<=", 
Token::LtEq),
                             }
                         }
+                        Some('|') if self.dialect.supports_geometric_types() 
=> {
+                            self.consume_for_binop(chars, "<<|", 
Token::ShiftLeftVerticalBar)
+                        }
                         Some('>') => self.consume_for_binop(chars, "<>", 
Token::Neq),
+                        Some('<') if self.dialect.supports_geometric_types() 
=> {
+                            chars.next(); // consume
+                            match chars.peek() {
+                                Some('|') => self.consume_for_binop(
+                                    chars,
+                                    "<<|",
+                                    Token::ShiftLeftVerticalBar,
+                                ),
+                                _ => self.start_binop(chars, "<<", 
Token::ShiftLeft),
+                            }
+                        }
                         Some('<') => self.consume_for_binop(chars, "<<", 
Token::ShiftLeft),
+                        Some('-') if self.dialect.supports_geometric_types() 
=> {
+                            chars.next(); // consume
+                            match chars.peek() {
+                                Some('>') => {
+                                    self.consume_for_binop(chars, "<->", 
Token::TwoWayArrow)
+                                }
+                                _ => self.start_binop_opt(chars, "<-", None),
+                            }
+                        }
+                        Some('^') if self.dialect.supports_geometric_types() 
=> {
+                            self.consume_for_binop(chars, "<^", 
Token::LeftAngleBracketCaret)
+                        }
                         Some('@') => self.consume_for_binop(chars, "<@", 
Token::ArrowAt),
                         _ => self.start_binop(chars, "<", Token::Lt),
                     }
@@ -1367,6 +1463,9 @@ impl<'a> Tokenizer<'a> {
                     match chars.peek() {
                         Some('=') => self.consume_for_binop(chars, ">=", 
Token::GtEq),
                         Some('>') => self.consume_for_binop(chars, ">>", 
Token::ShiftRight),
+                        Some('^') if self.dialect.supports_geometric_types() 
=> {
+                            self.consume_for_binop(chars, ">^", 
Token::RightAngleBracketCaret)
+                        }
                         _ => self.start_binop(chars, ">", Token::Gt),
                     }
                 }
@@ -1385,6 +1484,22 @@ impl<'a> Tokenizer<'a> {
                 '&' => {
                     chars.next(); // consume the '&'
                     match chars.peek() {
+                        Some('>') if self.dialect.supports_geometric_types() 
=> {
+                            chars.next();
+                            self.consume_and_return(chars, 
Token::AmpersandRightAngleBracket)
+                        }
+                        Some('<') if self.dialect.supports_geometric_types() 
=> {
+                            chars.next(); // consume
+                            match chars.peek() {
+                                Some('|') => self.consume_and_return(
+                                    chars,
+                                    
Token::AmpersandLeftAngleBracketVerticalBar,
+                                ),
+                                _ => {
+                                    self.start_binop(chars, "&<", 
Token::AmpersandLeftAngleBracket)
+                                }
+                            }
+                        }
                         Some('&') => {
                             chars.next(); // consume the second '&'
                             self.start_binop(chars, "&&", Token::Overlap)
@@ -1415,6 +1530,9 @@ impl<'a> Tokenizer<'a> {
                     chars.next(); // consume
                     match chars.peek() {
                         Some('*') => self.consume_for_binop(chars, "~*", 
Token::TildeAsterisk),
+                        Some('=') if self.dialect.supports_geometric_types() 
=> {
+                            self.consume_for_binop(chars, "~=", 
Token::TildeEqual)
+                        }
                         Some('~') => {
                             chars.next();
                             match chars.peek() {
@@ -1441,6 +1559,9 @@ impl<'a> Tokenizer<'a> {
                             }
                         }
                         Some(' ') => Ok(Some(Token::Sharp)),
+                        Some('#') if self.dialect.supports_geometric_types() 
=> {
+                            self.consume_for_binop(chars, "##", 
Token::DoubleSharp)
+                        }
                         Some(sch) if self.dialect.is_identifier_start('#') => {
                             self.tokenize_identifier_or_keyword([ch, *sch], 
chars)
                         }
@@ -1450,6 +1571,16 @@ impl<'a> Tokenizer<'a> {
                 '@' => {
                     chars.next();
                     match chars.peek() {
+                        Some('@') if self.dialect.supports_geometric_types() 
=> {
+                            self.consume_and_return(chars, Token::AtAt)
+                        }
+                        Some('-') if self.dialect.supports_geometric_types() 
=> {
+                            chars.next();
+                            match chars.peek() {
+                                Some('@') => self.consume_and_return(chars, 
Token::AtDashAt),
+                                _ => self.start_binop_opt(chars, "@-", None),
+                            }
+                        }
                         Some('>') => self.consume_and_return(chars, 
Token::AtArrow),
                         Some('?') => self.consume_and_return(chars, 
Token::AtQuestion),
                         Some('@') => {
@@ -1482,11 +1613,30 @@ impl<'a> Tokenizer<'a> {
                     }
                 }
                 // Postgres uses ? for jsonb operators, not prepared statements
-                '?' if dialect_of!(self is PostgreSqlDialect) => {
-                    chars.next();
+                '?' if self.dialect.supports_geometric_types() => {
+                    chars.next(); // consume
                     match chars.peek() {
-                        Some('|') => self.consume_and_return(chars, 
Token::QuestionPipe),
+                        Some('|') => {
+                            chars.next();
+                            match chars.peek() {
+                                Some('|') => self.consume_and_return(
+                                    chars,
+                                    Token::QuestionMarkDoubleVerticalBar,
+                                ),
+                                _ => Ok(Some(Token::QuestionPipe)),
+                            }
+                        }
+
                         Some('&') => self.consume_and_return(chars, 
Token::QuestionAnd),
+                        Some('-') => {
+                            chars.next(); // consume
+                            match chars.peek() {
+                                Some('|') => self
+                                    .consume_and_return(chars, 
Token::QuestionMarkDashVerticalBar),
+                                _ => Ok(Some(Token::QuestionMarkDash)),
+                            }
+                        }
+                        Some('#') => self.consume_and_return(chars, 
Token::QuestionMarkSharp),
                         _ => self.consume_and_return(chars, Token::Question),
                     }
                 }
@@ -1520,7 +1670,7 @@ impl<'a> Tokenizer<'a> {
         default: Token,
     ) -> Result<Option<Token>, TokenizerError> {
         chars.next(); // consume the first char
-        self.start_binop(chars, prefix, default)
+        self.start_binop_opt(chars, prefix, Some(default))
     }
 
     /// parse a custom binary operator
@@ -1529,6 +1679,16 @@ impl<'a> Tokenizer<'a> {
         chars: &mut State,
         prefix: &str,
         default: Token,
+    ) -> Result<Option<Token>, TokenizerError> {
+        self.start_binop_opt(chars, prefix, Some(default))
+    }
+
+    /// parse a custom binary operator
+    fn start_binop_opt(
+        &self,
+        chars: &mut State,
+        prefix: &str,
+        default: Option<Token>,
     ) -> Result<Option<Token>, TokenizerError> {
         let mut custom = None;
         while let Some(&ch) = chars.peek() {
@@ -1539,10 +1699,14 @@ impl<'a> Tokenizer<'a> {
             custom.get_or_insert_with(|| prefix.to_string()).push(ch);
             chars.next();
         }
-
-        Ok(Some(
-            custom.map(Token::CustomBinaryOperator).unwrap_or(default),
-        ))
+        match (custom, default) {
+            (Some(custom), _) => 
Ok(Token::CustomBinaryOperator(custom).into()),
+            (None, Some(tok)) => Ok(Some(tok)),
+            (None, None) => self.tokenizer_error(
+                chars.location(),
+                format!("Expected a valid binary operator after '{}'", prefix),
+            ),
+        }
     }
 
     /// Tokenize dollar preceded value (i.e: a string/placeholder)
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index a4e83be0..653142dc 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -13860,3 +13860,361 @@ fn test_select_from_first() {
         assert_eq!(ast.to_string(), q);
     }
 }
+
+#[test]
+fn test_geometric_unary_operators() {
+    // Number of points in path or polygon
+    let sql = "# path '((1,0),(0,1),(-1,0))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::UnaryOp {
+            op: UnaryOperator::Hash,
+            ..
+        }
+    ));
+
+    // Length or circumference
+    let sql = "@-@ path '((0,0),(1,0))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::UnaryOp {
+            op: UnaryOperator::AtDashAt,
+            ..
+        }
+    ));
+
+    // Center
+    let sql = "@@ circle '((0,0),10)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::UnaryOp {
+            op: UnaryOperator::DoubleAt,
+            ..
+        }
+    ));
+    // Is horizontal?
+    let sql = "?- lseg '((-1,0),(1,0))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::UnaryOp {
+            op: UnaryOperator::QuestionDash,
+            ..
+        }
+    ));
+
+    // Is vertical?
+    let sql = "?| lseg '((-1,0),(1,0))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::UnaryOp {
+            op: UnaryOperator::QuestionPipe,
+            ..
+        }
+    ));
+}
+
+#[test]
+fn test_geomtery_type() {
+    let sql = "point '1,2'";
+    assert_eq!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::TypedString {
+            data_type: DataType::GeometricType(GeometricTypeKind::Point),
+            value: Value::SingleQuotedString("1,2".to_string()),
+        }
+    );
+
+    let sql = "line '1,2,3,4'";
+    assert_eq!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::TypedString {
+            data_type: DataType::GeometricType(GeometricTypeKind::Line),
+            value: Value::SingleQuotedString("1,2,3,4".to_string()),
+        }
+    );
+
+    let sql = "path '1,2,3,4'";
+    assert_eq!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::TypedString {
+            data_type: 
DataType::GeometricType(GeometricTypeKind::GeometricPath),
+            value: Value::SingleQuotedString("1,2,3,4".to_string()),
+        }
+    );
+    let sql = "box '1,2,3,4'";
+    assert_eq!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::TypedString {
+            data_type: 
DataType::GeometricType(GeometricTypeKind::GeometricBox),
+            value: Value::SingleQuotedString("1,2,3,4".to_string()),
+        }
+    );
+
+    let sql = "circle '1,2,3'";
+    assert_eq!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::TypedString {
+            data_type: DataType::GeometricType(GeometricTypeKind::Circle),
+            value: Value::SingleQuotedString("1,2,3".to_string()),
+        }
+    );
+
+    let sql = "polygon '1,2,3,4'";
+    assert_eq!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::TypedString {
+            data_type: DataType::GeometricType(GeometricTypeKind::Polygon),
+            value: Value::SingleQuotedString("1,2,3,4".to_string()),
+        }
+    );
+    let sql = "lseg '1,2,3,4'";
+    assert_eq!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::TypedString {
+            data_type: DataType::GeometricType(GeometricTypeKind::LineSegment),
+            value: Value::SingleQuotedString("1,2,3,4".to_string()),
+        }
+    );
+}
+#[test]
+fn test_geometric_binary_operators() {
+    // Translation plus
+    let sql = "box '((0,0),(1,1))' + point '(2.0,0)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::Plus,
+            ..
+        }
+    ));
+    // Translation minus
+    let sql = "box '((0,0),(1,1))' - point '(2.0,0)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::Minus,
+            ..
+        }
+    ));
+
+    // Scaling multiply
+    let sql = "box '((0,0),(1,1))' * point '(2.0,0)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::Multiply,
+            ..
+        }
+    ));
+
+    // Scaling divide
+    let sql = "box '((0,0),(1,1))' / point '(2.0,0)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::Divide,
+            ..
+        }
+    ));
+
+    // Intersection
+    let sql = "'((1,-1),(-1,1))' # '((1,1),(-1,-1))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::PGBitwiseXor,
+            ..
+        }
+    ));
+
+    //Point of closest proximity
+    let sql = "point '(0,0)' ## lseg '((2,0),(0,2))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::DoubleHash,
+            ..
+        }
+    ));
+
+    // Point of closest proximity
+    let sql = "box '((0,0),(1,1))' && box '((0,0),(2,2))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::PGOverlap,
+            ..
+        }
+    ));
+
+    // Overlaps to left?
+    let sql = "box '((0,0),(1,1))' &< box '((0,0),(2,2))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::AndLt,
+            ..
+        }
+    ));
+
+    // Overlaps to right?
+    let sql = "box '((0,0),(3,3))' &> box '((0,0),(2,2))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::AndGt,
+            ..
+        }
+    ));
+
+    // Distance between
+    let sql = "circle '((0,0),1)' <-> circle '((5,0),1)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::LtDashGt,
+            ..
+        }
+    ));
+
+    // Is left of?
+    let sql = "circle '((0,0),1)' << circle '((5,0),1)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::PGBitwiseShiftLeft,
+            ..
+        }
+    ));
+
+    // Is right of?
+    let sql = "circle '((5,0),1)' >> circle '((0,0),1)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::PGBitwiseShiftRight,
+            ..
+        }
+    ));
+
+    // Is below?
+    let sql = "circle '((0,0),1)' <^ circle '((0,5),1)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::LtCaret,
+            ..
+        }
+    ));
+
+    //         Intersects or overlaps
+    let sql = "lseg '((-1,0),(1,0))' ?# box '((-2,-2),(2,2))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::QuestionHash,
+            ..
+        }
+    ));
+
+    // Is horizontal?
+    let sql = "point '(1,0)' ?- point '(0,0)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::QuestionDash,
+            ..
+        }
+    ));
+
+    // Is perpendicular?
+    let sql = "lseg '((0,0),(0,1))' ?-| lseg '((0,0),(1,0))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::QuestionDashPipe,
+            ..
+        }
+    ));
+
+    // Is vertical?
+    let sql = "point '(0,1)' ?| point '(0,0)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::QuestionPipe,
+            ..
+        }
+    ));
+
+    // Are parallel?
+    let sql = "lseg '((-1,0),(1,0))' ?|| lseg '((-1,2),(1,2))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::QuestionDoublePipe,
+            ..
+        }
+    ));
+
+    // Contained or on?
+    let sql = "point '(1,1)' @ circle '((0,0),2)'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::At,
+            ..
+        }
+    ));
+
+    //
+    // Same as?
+    let sql = "polygon '((0,0),(1,1))' ~= polygon '((1,1),(0,0))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::TildeEq,
+            ..
+        }
+    ));
+
+    // Is strictly below?
+    let sql = "box '((0,0),(3,3))' <<| box '((3,4),(5,5))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::LtLtPipe,
+            ..
+        }
+    ));
+
+    // Is strictly above?
+    let sql = "box '((3,4),(5,5))' |>> box '((0,0),(3,3))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::PipeGtGt,
+            ..
+        }
+    ));
+
+    // Does not extend above?
+    let sql = "box '((0,0),(1,1))' &<| box '((0,0),(2,2))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::AndLtPipe,
+            ..
+        }
+    ));
+
+    // Does not extend below?
+    let sql = "box '((0,0),(3,3))' |&> box '((0,0),(2,2))'";
+    assert!(matches!(
+        all_dialects_where(|d| 
d.supports_geometric_types()).verified_expr(sql),
+        Expr::BinaryOp {
+            op: BinaryOperator::PipeAndGt,
+            ..
+        }
+    ));
+}
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index d400792d..312ce118 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -2066,12 +2066,8 @@ fn parse_pg_custom_binary_ops() {
     let operators = [
         // PostGIS
         "&&&",   // n-D bounding boxes intersect
-        "&<",    // (is strictly to the left of)
-        "&>",    // (is strictly to the right of)
         "|=|",   //  distance between A and B trajectories at their closest 
point of approach
         "<<#>>", // n-D distance between A and B bounding boxes
-        "|>>",   // A's bounding box is strictly above B's.
-        "~=",    // bounding box is the same
         // PGroonga
         "&@",   // Full text search by a keyword
         "&@~",  // Full text search by easy to use query language


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to