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

github-bot pushed a commit to branch 
gh-readonly-queue/main/pr-2187-23acd2376698badf0d7f4e5ed818ff606b5357a4
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit 0924f3a9b28cf79e7ef49819dabc719523f9aa8b
Author: Guan-Ming (Wesley) Chiu <[email protected]>
AuthorDate: Fri Feb 6 23:41:38 2026 +0800

    PostgreSQL: Support PostgreSQL ANALYZE with optional table and column 
(#2187)
    
    Signed-off-by: Guan-Ming (Wesley) Chiu 
<[email protected]>
---
 src/ast/mod.rs              | 34 +++++++++++++++++++---------------
 src/ast/spans.rs            |  4 +++-
 src/parser/mod.rs           |  9 ++++++++-
 tests/sqlparser_postgres.rs | 25 +++++++++++++++++++++++++
 4 files changed, 55 insertions(+), 17 deletions(-)

diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index a5951969..010a8189 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -3326,19 +3326,24 @@ impl Display for ExceptionWhen {
     }
 }
 
-/// ANALYZE TABLE statement (Hive-specific)
+/// ANALYZE statement
+///
+/// Supported syntax varies by dialect:
+/// - Hive: `ANALYZE TABLE t [PARTITION (...)] COMPUTE STATISTICS [NOSCAN] 
[FOR COLUMNS [col1, ...]] [CACHE METADATA]`
+/// - PostgreSQL: `ANALYZE [VERBOSE] [t [(col1, ...)]]` See 
<https://www.postgresql.org/docs/current/sql-analyze.html>
+/// - General: `ANALYZE [TABLE] t`
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub struct Analyze {
     #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
-    /// Name of the table to analyze.
-    pub table_name: ObjectName,
+    /// Name of the table to analyze. `None` for bare `ANALYZE`.
+    pub table_name: Option<ObjectName>,
     /// Optional partition expressions to restrict the analysis.
     pub partitions: Option<Vec<Expr>>,
-    /// `true` when analyzing specific columns.
+    /// `true` when analyzing specific columns (Hive `FOR COLUMNS` syntax).
     pub for_columns: bool,
-    /// Columns to analyze when `for_columns` is `true`.
+    /// Columns to analyze.
     pub columns: Vec<Ident>,
     /// Whether to cache metadata before analyzing.
     pub cache_metadata: bool,
@@ -3352,22 +3357,21 @@ pub struct Analyze {
 
 impl fmt::Display for Analyze {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(
-            f,
-            "ANALYZE{}{table_name}",
+        write!(f, "ANALYZE")?;
+        if let Some(ref table_name) = self.table_name {
             if self.has_table_keyword {
-                " TABLE "
-            } else {
-                " "
-            },
-            table_name = self.table_name
-        )?;
+                write!(f, " TABLE")?;
+            }
+            write!(f, " {table_name}")?;
+        }
+        if !self.for_columns && !self.columns.is_empty() {
+            write!(f, " ({})", display_comma_separated(&self.columns))?;
+        }
         if let Some(ref parts) = self.partitions {
             if !parts.is_empty() {
                 write!(f, " PARTITION ({})", display_comma_separated(parts))?;
             }
         }
-
         if self.compute_statistics {
             write!(f, " COMPUTE STATISTICS")?;
         }
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 16a9a926..bdd430e7 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -841,7 +841,9 @@ impl Spanned for ConstraintCharacteristics {
 impl Spanned for Analyze {
     fn span(&self) -> Span {
         union_spans(
-            core::iter::once(self.table_name.span())
+            self.table_name
+                .iter()
+                .map(|t| t.span())
                 .chain(
                     self.partitions
                         .iter()
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 14ddd2b5..585242a8 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -1193,13 +1193,20 @@ impl<'a> Parser<'a> {
     /// Parse `ANALYZE` statement.
     pub fn parse_analyze(&mut self) -> Result<Analyze, ParserError> {
         let has_table_keyword = self.parse_keyword(Keyword::TABLE);
-        let table_name = self.parse_object_name(false)?;
+        let table_name = self.maybe_parse(|parser| 
parser.parse_object_name(false))?;
         let mut for_columns = false;
         let mut cache_metadata = false;
         let mut noscan = false;
         let mut partitions = None;
         let mut compute_statistics = false;
         let mut columns = vec![];
+
+        // PostgreSQL syntax: ANALYZE t (col1, col2)
+        if table_name.is_some() && self.consume_token(&Token::LParen) {
+            columns = self.parse_comma_separated(|p| p.parse_identifier())?;
+            self.expect_token(&Token::RParen)?;
+        }
+
         loop {
             match self.parse_one_of_keywords(&[
                 Keyword::PARTITION,
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index 5853be7e..cfb03737 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -8511,3 +8511,28 @@ fn parse_create_table_partition_of_errors() {
         "Expected error about empty TO list, got: {err}"
     );
 }
+
+#[test]
+fn parse_pg_analyze() {
+    // Bare ANALYZE
+    pg_and_generic().verified_stmt("ANALYZE");
+
+    // ANALYZE with table name
+    pg_and_generic().verified_stmt("ANALYZE t");
+
+    // ANALYZE with column specification
+    pg_and_generic().verified_stmt("ANALYZE t (col1, col2)");
+
+    // Verify AST for column specification
+    let stmt = pg().verified_stmt("ANALYZE t (col1, col2)");
+    match &stmt {
+        Statement::Analyze(analyze) => {
+            assert_eq!(analyze.table_name.as_ref().unwrap().to_string(), "t");
+            assert_eq!(analyze.columns.len(), 2);
+            assert_eq!(analyze.columns[0].to_string(), "col1");
+            assert_eq!(analyze.columns[1].to_string(), "col2");
+            assert!(!analyze.for_columns);
+        }
+        _ => panic!("Expected Analyze, got: {stmt:?}"),
+    }
+}


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

Reply via email to