Add support for PostgreSQL ^@ starts-with operator (#1091)

This commit is contained in:
Alexander Beedie 2024-01-15 14:42:03 +04:00 committed by GitHub
parent a71b3f5e82
commit 7cb1654d81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 26 additions and 8 deletions

View file

@ -131,6 +131,8 @@ pub enum BinaryOperator {
PGRegexNotMatch, PGRegexNotMatch,
/// String does not match regular expression (case insensitively), e.g. `a !~* b` (PostgreSQL-specific) /// String does not match regular expression (case insensitively), e.g. `a !~* b` (PostgreSQL-specific)
PGRegexNotIMatch, PGRegexNotIMatch,
/// String "starts with", eg: `a ^@ b` (PostgreSQL-specific)
PGStartsWith,
/// PostgreSQL-specific custom operator. /// PostgreSQL-specific custom operator.
/// ///
/// See [CREATE OPERATOR](https://www.postgresql.org/docs/current/sql-createoperator.html) /// See [CREATE OPERATOR](https://www.postgresql.org/docs/current/sql-createoperator.html)
@ -172,6 +174,7 @@ impl fmt::Display for BinaryOperator {
BinaryOperator::PGRegexIMatch => f.write_str("~*"), BinaryOperator::PGRegexIMatch => f.write_str("~*"),
BinaryOperator::PGRegexNotMatch => f.write_str("!~"), BinaryOperator::PGRegexNotMatch => f.write_str("!~"),
BinaryOperator::PGRegexNotIMatch => f.write_str("!~*"), BinaryOperator::PGRegexNotIMatch => f.write_str("!~*"),
BinaryOperator::PGStartsWith => f.write_str("^@"),
BinaryOperator::PGCustomBinaryOperator(idents) => { BinaryOperator::PGCustomBinaryOperator(idents) => {
write!(f, "OPERATOR({})", display_separated(idents, ".")) write!(f, "OPERATOR({})", display_separated(idents, "."))
} }

View file

@ -2196,6 +2196,9 @@ impl<'a> Parser<'a> {
Token::Overlap if dialect_of!(self is PostgreSqlDialect | GenericDialect) => { Token::Overlap if dialect_of!(self is PostgreSqlDialect | GenericDialect) => {
Some(BinaryOperator::PGOverlap) Some(BinaryOperator::PGOverlap)
} }
Token::CaretAt if dialect_of!(self is PostgreSqlDialect | GenericDialect) => {
Some(BinaryOperator::PGStartsWith)
}
Token::Tilde => Some(BinaryOperator::PGRegexMatch), Token::Tilde => Some(BinaryOperator::PGRegexMatch),
Token::TildeAsterisk => Some(BinaryOperator::PGRegexIMatch), Token::TildeAsterisk => Some(BinaryOperator::PGRegexIMatch),
Token::ExclamationMarkTilde => Some(BinaryOperator::PGRegexNotMatch), Token::ExclamationMarkTilde => Some(BinaryOperator::PGRegexNotMatch),
@ -2630,6 +2633,7 @@ impl<'a> Parser<'a> {
| Token::LongArrow | Token::LongArrow
| Token::Arrow | Token::Arrow
| Token::Overlap | Token::Overlap
| Token::CaretAt
| Token::HashArrow | Token::HashArrow
| Token::HashLongArrow | Token::HashLongArrow
| Token::AtArrow | Token::AtArrow

View file

@ -60,7 +60,8 @@ pub enum Token {
DoubleQuotedString(String), DoubleQuotedString(String),
/// Dollar quoted string: i.e: $$string$$ or $tag_name$string$tag_name$ /// Dollar quoted string: i.e: $$string$$ or $tag_name$string$tag_name$
DollarQuotedString(DollarQuotedString), DollarQuotedString(DollarQuotedString),
/// Byte string literal: i.e: b'string' or B'string' /// Byte string literal: i.e: b'string' or B'string' (note that some backends, such as
/// PostgreSQL, may treat this syntax as a bit string literal instead, i.e: b'10010101')
SingleQuotedByteStringLiteral(String), SingleQuotedByteStringLiteral(String),
/// Byte string literal: i.e: b"string" or B"string" /// Byte string literal: i.e: b"string" or B"string"
DoubleQuotedByteStringLiteral(String), DoubleQuotedByteStringLiteral(String),
@ -114,7 +115,7 @@ pub enum Token {
Period, Period,
/// Colon `:` /// Colon `:`
Colon, Colon,
/// DoubleColon `::` (used for casting in postgresql) /// DoubleColon `::` (used for casting in PostgreSQL)
DoubleColon, DoubleColon,
/// Assignment `:=` (used for keyword argument in DuckDB macros) /// Assignment `:=` (used for keyword argument in DuckDB macros)
DuckAssignment, DuckAssignment,
@ -152,7 +153,7 @@ pub enum Token {
ShiftLeft, ShiftLeft,
/// `>>`, a bitwise shift right operator in PostgreSQL /// `>>`, a bitwise shift right operator in PostgreSQL
ShiftRight, ShiftRight,
/// '&&', an overlap operator in PostgreSQL /// `&&`, an overlap operator in PostgreSQL
Overlap, Overlap,
/// Exclamation Mark `!` used for PostgreSQL factorial operator /// Exclamation Mark `!` used for PostgreSQL factorial operator
ExclamationMark, ExclamationMark,
@ -160,19 +161,21 @@ pub enum Token {
DoubleExclamationMark, DoubleExclamationMark,
/// AtSign `@` used for PostgreSQL abs operator /// AtSign `@` used for PostgreSQL abs operator
AtSign, AtSign,
/// `^@`, a "starts with" string operator in PostgreSQL
CaretAt,
/// `|/`, a square root math operator in PostgreSQL /// `|/`, a square root math operator in PostgreSQL
PGSquareRoot, PGSquareRoot,
/// `||/`, a cube root math operator in PostgreSQL /// `||/`, a cube root math operator in PostgreSQL
PGCubeRoot, PGCubeRoot,
/// `?` or `$` , a prepared statement arg placeholder /// `?` or `$` , a prepared statement arg placeholder
Placeholder(String), Placeholder(String),
/// ->, used as a operator to extract json field in PostgreSQL /// `->`, used as a operator to extract json field in PostgreSQL
Arrow, Arrow,
/// ->>, used as a operator to extract json field as text in PostgreSQL /// `->>`, used as a operator to extract json field as text in PostgreSQL
LongArrow, LongArrow,
/// #> Extracts JSON sub-object at the specified path /// `#>`, extracts JSON sub-object at the specified path
HashArrow, HashArrow,
/// #>> Extracts JSON sub-object at the specified path as text /// `#>>`, extracts JSON sub-object at the specified path as text
HashLongArrow, HashLongArrow,
/// jsonb @> jsonb -> boolean: Test whether left json contains the right json /// jsonb @> jsonb -> boolean: Test whether left json contains the right json
AtArrow, AtArrow,
@ -247,6 +250,7 @@ impl fmt::Display for Token {
Token::ExclamationMarkTilde => f.write_str("!~"), Token::ExclamationMarkTilde => f.write_str("!~"),
Token::ExclamationMarkTildeAsterisk => f.write_str("!~*"), Token::ExclamationMarkTildeAsterisk => f.write_str("!~*"),
Token::AtSign => f.write_str("@"), Token::AtSign => f.write_str("@"),
Token::CaretAt => f.write_str("^@"),
Token::ShiftLeft => f.write_str("<<"), Token::ShiftLeft => f.write_str("<<"),
Token::ShiftRight => f.write_str(">>"), Token::ShiftRight => f.write_str(">>"),
Token::Overlap => f.write_str("&&"), Token::Overlap => f.write_str("&&"),
@ -940,7 +944,13 @@ impl<'a> Tokenizer<'a> {
_ => Ok(Some(Token::Ampersand)), _ => Ok(Some(Token::Ampersand)),
} }
} }
'^' => self.consume_and_return(chars, Token::Caret), '^' => {
chars.next(); // consume the '^'
match chars.peek() {
Some('@') => self.consume_and_return(chars, Token::CaretAt),
_ => Ok(Some(Token::Caret)),
}
}
'{' => self.consume_and_return(chars, Token::LBrace), '{' => self.consume_and_return(chars, Token::LBrace),
'}' => self.consume_and_return(chars, Token::RBrace), '}' => self.consume_and_return(chars, Token::RBrace),
'#' if dialect_of!(self is SnowflakeDialect) => { '#' if dialect_of!(self is SnowflakeDialect) => {

View file

@ -1728,6 +1728,7 @@ fn parse_pg_binary_ops() {
(">>", BinaryOperator::PGBitwiseShiftRight, pg_and_generic()), (">>", BinaryOperator::PGBitwiseShiftRight, pg_and_generic()),
("<<", BinaryOperator::PGBitwiseShiftLeft, pg_and_generic()), ("<<", BinaryOperator::PGBitwiseShiftLeft, pg_and_generic()),
("&&", BinaryOperator::PGOverlap, pg()), ("&&", BinaryOperator::PGOverlap, pg()),
("^@", BinaryOperator::PGStartsWith, pg()),
]; ];
for (str_op, op, dialects) in binary_ops { for (str_op, op, dialects) in binary_ops {