BigQuery: Parse WITH CONNECTION on CREATE EXTERNAL TABLE (#2326)
diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs
index 62a8a71..0a93f5b 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -3020,6 +3020,9 @@
     /// Snowflake "EXTERNAL_VOLUME" clause for Iceberg tables
     /// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
     pub external_volume: Option<String>,
+    /// `WITH CONNECTION` clause.
+    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_external_table_statement)
+    pub with_connection: Option<ObjectName>,
     /// Snowflake "BASE_LOCATION" clause for Iceberg tables
     /// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
     pub base_location: Option<String>,
@@ -3269,6 +3272,9 @@
         if let Some(cluster_by) = self.cluster_by.as_ref() {
             write!(f, " CLUSTER BY {cluster_by}")?;
         }
+        if let Some(with_connection) = &self.with_connection {
+            write!(f, " WITH CONNECTION {with_connection}")?;
+        }
         if let options @ CreateTableOptions::Options(_) = &self.table_options {
             write!(f, " {options}")?;
         }
diff --git a/src/ast/helpers/stmt_create_table.rs b/src/ast/helpers/stmt_create_table.rs
index fc81d3b..9ec9ab2 100644
--- a/src/ast/helpers/stmt_create_table.rs
+++ b/src/ast/helpers/stmt_create_table.rs
@@ -157,6 +157,9 @@
     pub base_location: Option<String>,
     /// Optional external volume identifier.
     pub external_volume: Option<String>,
+    /// `WITH CONNECTION` clause.
+    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_external_table_statement)
+    pub with_connection: Option<ObjectName>,
     /// Optional catalog name.
     pub catalog: Option<String>,
     /// Optional catalog synchronization option.
@@ -241,6 +244,7 @@
             with_tags: None,
             base_location: None,
             external_volume: None,
+            with_connection: None,
             catalog: None,
             catalog_sync: None,
             storage_serialization_policy: None,
@@ -497,6 +501,11 @@
         self.external_volume = external_volume;
         self
     }
+    /// Set the `WITH CONNECTION` clause.
+    pub fn with_connection(mut self, with_connection: Option<ObjectName>) -> Self {
+        self.with_connection = with_connection;
+        self
+    }
     /// Set the catalog name for the table.
     pub fn catalog(mut self, catalog: Option<String>) -> Self {
         self.catalog = catalog;
@@ -630,6 +639,7 @@
             with_tags: self.with_tags,
             base_location: self.base_location,
             external_volume: self.external_volume,
+            with_connection: self.with_connection,
             catalog: self.catalog,
             catalog_sync: self.catalog_sync,
             storage_serialization_policy: self.storage_serialization_policy,
@@ -714,6 +724,7 @@
             with_tags: table.with_tags,
             base_location: table.base_location,
             external_volume: table.external_volume,
+            with_connection: table.with_connection,
             catalog: table.catalog,
             catalog_sync: table.catalog_sync,
             storage_serialization_policy: table.storage_serialization_policy,
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 343d99e..f6ba895 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -589,6 +589,7 @@
             with_storage_lifecycle_policy: _,   // todo, Snowflake specific
             with_tags: _,                       // todo, Snowflake specific
             external_volume: _,                 // todo, Snowflake specific
+            with_connection: _,                 // todo, BigQuery external table connection
             base_location: _,                   // todo, Snowflake specific
             catalog: _,                         // todo, Snowflake specific
             catalog_sync: _,                    // todo, Snowflake specific
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 3d4b309..07497b0 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -6418,6 +6418,12 @@
             None
         };
         let location = hive_formats.as_ref().and_then(|hf| hf.location.clone());
+
+        let with_connection = if self.parse_keywords(&[Keyword::WITH, Keyword::CONNECTION]) {
+            Some(self.parse_object_name(false)?)
+        } else {
+            None
+        };
         let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?;
         let table_options = if !table_properties.is_empty() {
             CreateTableOptions::TableProperties(table_properties)
@@ -6432,6 +6438,7 @@
             .hive_distribution(hive_distribution)
             .hive_formats(hive_formats)
             .table_options(table_options)
+            .with_connection(with_connection)
             .or_replace(or_replace)
             .if_not_exists(if_not_exists)
             .external(true)
diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs
index 212607e..f6d4483 100644
--- a/tests/sqlparser_bigquery.rs
+++ b/tests/sqlparser_bigquery.rs
@@ -2203,6 +2203,31 @@
     );
 }
 
+#[test]
+fn parse_bigquery_create_external_table_with_connection() {
+    bigquery().one_statement_parses_to(
+        concat!(
+            "CREATE OR REPLACE EXTERNAL TABLE `proj.ds.tbl` ",
+            "WITH CONNECTION `projects/proj/locations/us/connections/c` ",
+            r#"OPTIONS(format = "ICEBERG", uris = ["gs://b/m.json"])"#,
+        ),
+        concat!(
+            "CREATE OR REPLACE EXTERNAL TABLE `proj`.`ds`.`tbl` () ",
+            "WITH CONNECTION `projects/proj/locations/us/connections/c` ",
+            r#"OPTIONS(format = "ICEBERG", uris = ["gs://b/m.json"])"#,
+        ),
+    );
+    bigquery().one_statement_parses_to(
+        "CREATE EXTERNAL TABLE t WITH CONNECTION c",
+        "CREATE EXTERNAL TABLE t () WITH CONNECTION c",
+    );
+    bigquery().verified_stmt(concat!(
+        "CREATE EXTERNAL TABLE t (a INT64, b STRING) ",
+        r#"WITH CONNECTION c OPTIONS(uris = ["gs://x"])"#,
+    ));
+    bigquery().verified_stmt(r#"CREATE EXTERNAL TABLE t (a INT64) OPTIONS(uris = ["gs://x"])"#);
+}
+
 fn bigquery() -> TestedDialects {
     TestedDialects::new(vec![Box::new(BigQueryDialect {})])
 }
diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs
index 548ad27..86c7658 100644
--- a/tests/sqlparser_duckdb.rs
+++ b/tests/sqlparser_duckdb.rs
@@ -780,6 +780,7 @@
             with_tags: Default::default(),
             base_location: Default::default(),
             external_volume: Default::default(),
+            with_connection: Default::default(),
             catalog: Default::default(),
             catalog_sync: Default::default(),
             storage_serialization_policy: Default::default(),
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index d784c74..6e86674 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -1999,6 +1999,7 @@
                 with_tags: None,
                 base_location: None,
                 external_volume: None,
+                with_connection: None,
                 catalog: None,
                 catalog_sync: None,
                 storage_serialization_policy: None,
@@ -2176,6 +2177,7 @@
                 with_tags: None,
                 base_location: None,
                 external_volume: None,
+                with_connection: None,
                 catalog: None,
                 catalog_sync: None,
                 storage_serialization_policy: None,
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index 369e7fc..11e76db 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -6702,6 +6702,7 @@
             with_tags: None,
             base_location: None,
             external_volume: None,
+            with_connection: None,
             catalog: None,
             catalog_sync: None,
             storage_serialization_policy: None,