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 05821cc7 Add support for PostgreSQL `LISTEN/NOTIFY` syntax (#1485)
05821cc7 is described below

commit 05821cc7db7b499528ac47a93d8d86a43c889933
Author: wugeer <[email protected]>
AuthorDate: Wed Nov 6 23:51:08 2024 +0800

    Add support for PostgreSQL `LISTEN/NOTIFY` syntax (#1485)
---
 src/ast/mod.rs            | 28 +++++++++++++++++
 src/dialect/mod.rs        | 10 +++++++
 src/dialect/postgresql.rs | 10 +++++++
 src/keywords.rs           |  2 ++
 src/parser/mod.rs         | 19 ++++++++++++
 tests/sqlparser_common.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 145 insertions(+)

diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index b2672552..2ef3a460 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -3295,6 +3295,23 @@ pub enum Statement {
         include_final: bool,
         deduplicate: Option<Deduplicate>,
     },
+    /// ```sql
+    /// LISTEN
+    /// ```
+    /// listen for a notification channel
+    ///
+    /// See Postgres <https://www.postgresql.org/docs/current/sql-listen.html>
+    LISTEN { channel: Ident },
+    /// ```sql
+    /// NOTIFY channel [ , payload ]
+    /// ```
+    /// send a notification event together with an optional “payload” string 
to channel
+    ///
+    /// See Postgres <https://www.postgresql.org/docs/current/sql-notify.html>
+    NOTIFY {
+        channel: Ident,
+        payload: Option<String>,
+    },
 }
 
 impl fmt::Display for Statement {
@@ -4839,6 +4856,17 @@ impl fmt::Display for Statement {
                 }
                 Ok(())
             }
+            Statement::LISTEN { channel } => {
+                write!(f, "LISTEN {channel}")?;
+                Ok(())
+            }
+            Statement::NOTIFY { channel, payload } => {
+                write!(f, "NOTIFY {channel}")?;
+                if let Some(payload) = payload {
+                    write!(f, ", '{payload}'")?;
+                }
+                Ok(())
+            }
         }
     }
 }
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 7e43439a..5abddba3 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -590,6 +590,16 @@ pub trait Dialect: Debug + Any {
     fn supports_try_convert(&self) -> bool {
         false
     }
+
+    /// Returns true if the dialect supports the `LISTEN` statement
+    fn supports_listen(&self) -> bool {
+        false
+    }
+
+    /// Returns true if the dialect supports the `NOTIFY` statement
+    fn supports_notify(&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 dc458ec5..c40c826c 100644
--- a/src/dialect/postgresql.rs
+++ b/src/dialect/postgresql.rs
@@ -191,6 +191,16 @@ impl Dialect for PostgreSqlDialect {
     fn supports_explain_with_utility_options(&self) -> bool {
         true
     }
+
+    /// see <https://www.postgresql.org/docs/current/sql-listen.html>
+    fn supports_listen(&self) -> bool {
+        true
+    }
+
+    /// see <https://www.postgresql.org/docs/current/sql-notify.html>
+    fn supports_notify(&self) -> bool {
+        true
+    }
 }
 
 pub fn parse_comment(parser: &mut Parser) -> Result<Statement, ParserError> {
diff --git a/src/keywords.rs b/src/keywords.rs
index e9830968..d60227c9 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -438,6 +438,7 @@ define_keywords!(
     LIKE_REGEX,
     LIMIT,
     LINES,
+    LISTEN,
     LN,
     LOAD,
     LOCAL,
@@ -513,6 +514,7 @@ define_keywords!(
     NOSUPERUSER,
     NOT,
     NOTHING,
+    NOTIFY,
     NOWAIT,
     NO_WRITE_TO_BINLOG,
     NTH_VALUE,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 1ad637f9..fd7d1c57 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -532,6 +532,10 @@ impl<'a> Parser<'a> {
                 Keyword::EXECUTE => self.parse_execute(),
                 Keyword::PREPARE => self.parse_prepare(),
                 Keyword::MERGE => self.parse_merge(),
+                // `LISTEN` and `NOTIFY` are Postgres-specific
+                // syntaxes. They are used for Postgres statement.
+                Keyword::LISTEN if self.dialect.supports_listen() => 
self.parse_listen(),
+                Keyword::NOTIFY if self.dialect.supports_notify() => 
self.parse_notify(),
                 // `PRAGMA` is sqlite specific 
https://www.sqlite.org/pragma.html
                 Keyword::PRAGMA => self.parse_pragma(),
                 Keyword::UNLOAD => self.parse_unload(),
@@ -946,6 +950,21 @@ impl<'a> Parser<'a> {
         Ok(Statement::ReleaseSavepoint { name })
     }
 
+    pub fn parse_listen(&mut self) -> Result<Statement, ParserError> {
+        let channel = self.parse_identifier(false)?;
+        Ok(Statement::LISTEN { channel })
+    }
+
+    pub fn parse_notify(&mut self) -> Result<Statement, ParserError> {
+        let channel = self.parse_identifier(false)?;
+        let payload = if self.consume_token(&Token::Comma) {
+            Some(self.parse_literal_string()?)
+        } else {
+            None
+        };
+        Ok(Statement::NOTIFY { channel, payload })
+    }
+
     /// Parse an expression prefix.
     pub fn parse_prefix(&mut self) -> Result<Expr, ParserError> {
         // allow the dialect to override prefix parsing
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 94dfcfec..334dae2b 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -11399,3 +11399,79 @@ fn test_show_dbs_schemas_tables_views() {
     verified_stmt("SHOW MATERIALIZED VIEWS FROM db1");
     verified_stmt("SHOW MATERIALIZED VIEWS FROM db1 'abc'");
 }
+
+#[test]
+fn parse_listen_channel() {
+    let dialects = all_dialects_where(|d| d.supports_listen());
+
+    match dialects.verified_stmt("LISTEN test1") {
+        Statement::LISTEN { channel } => {
+            assert_eq!(Ident::new("test1"), channel);
+        }
+        _ => unreachable!(),
+    };
+
+    assert_eq!(
+        dialects.parse_sql_statements("LISTEN *").unwrap_err(),
+        ParserError::ParserError("Expected: identifier, found: *".to_string())
+    );
+
+    let dialects = all_dialects_where(|d| !d.supports_listen());
+
+    assert_eq!(
+        dialects.parse_sql_statements("LISTEN test1").unwrap_err(),
+        ParserError::ParserError("Expected: an SQL statement, found: 
LISTEN".to_string())
+    );
+}
+
+#[test]
+fn parse_notify_channel() {
+    let dialects = all_dialects_where(|d| d.supports_notify());
+
+    match dialects.verified_stmt("NOTIFY test1") {
+        Statement::NOTIFY { channel, payload } => {
+            assert_eq!(Ident::new("test1"), channel);
+            assert_eq!(payload, None);
+        }
+        _ => unreachable!(),
+    };
+
+    match dialects.verified_stmt("NOTIFY test1, 'this is a test 
notification'") {
+        Statement::NOTIFY {
+            channel,
+            payload: Some(payload),
+        } => {
+            assert_eq!(Ident::new("test1"), channel);
+            assert_eq!("this is a test notification", payload);
+        }
+        _ => unreachable!(),
+    };
+
+    assert_eq!(
+        dialects.parse_sql_statements("NOTIFY *").unwrap_err(),
+        ParserError::ParserError("Expected: identifier, found: *".to_string())
+    );
+    assert_eq!(
+        dialects
+            .parse_sql_statements("NOTIFY test1, *")
+            .unwrap_err(),
+        ParserError::ParserError("Expected: literal string, found: 
*".to_string())
+    );
+
+    let sql_statements = [
+        "NOTIFY test1",
+        "NOTIFY test1, 'this is a test notification'",
+    ];
+    let dialects = all_dialects_where(|d| !d.supports_notify());
+
+    for &sql in &sql_statements {
+        assert_eq!(
+            dialects.parse_sql_statements(sql).unwrap_err(),
+            ParserError::ParserError("Expected: an SQL statement, found: 
NOTIFY".to_string())
+        );
+        assert_eq!(
+            dialects.parse_sql_statements(sql).unwrap_err(),
+            ParserError::ParserError("Expected: an SQL statement, found: 
NOTIFY".to_string())
+        );
+    }
+}


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

Reply via email to