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]