diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 346247e2..a22dcc04 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1537,6 +1537,20 @@ pub enum Statement { /// Only for mysql priority: Option, }, + /// ```sql + /// INSTALL + /// ``` + Install { + /// Only for DuckDB + extension_name: Ident, + }, + /// ```sql + /// LOAD + /// ``` + Load { + /// Only for DuckDB + extension_name: Ident, + }, // TODO: Support ROW FORMAT Directory { overwrite: bool, @@ -2637,6 +2651,13 @@ impl fmt::Display for Statement { Ok(()) } + Statement::Install { + extension_name: name, + } => write!(f, "INSTALL {name}"), + + Statement::Load { + extension_name: name, + } => write!(f, "LOAD {name}"), Statement::Call(function) => write!(f, "CALL {function}"), diff --git a/src/keywords.rs b/src/keywords.rs index f14b92b7..771e8670 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -351,6 +351,7 @@ define_keywords!( INPUTFORMAT, INSENSITIVE, INSERT, + INSTALL, INT, INT2, INT4, @@ -390,6 +391,7 @@ define_keywords!( LIMIT, LISTAGG, LN, + LOAD, LOCAL, LOCALTIME, LOCALTIMESTAMP, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ff7939c9..210d157d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -516,6 +516,15 @@ impl<'a> Parser<'a> { Keyword::MERGE => Ok(self.parse_merge()?), // `PRAGMA` is sqlite specific https://www.sqlite.org/pragma.html Keyword::PRAGMA => Ok(self.parse_pragma()?), + // `INSTALL` is duckdb specific https://duckdb.org/docs/extensions/overview + Keyword::INSTALL if dialect_of!(self is DuckDbDialect | GenericDialect) => { + Ok(self.parse_install()?) + } + // `LOAD` is duckdb specific https://duckdb.org/docs/extensions/overview + Keyword::LOAD if dialect_of!(self is DuckDbDialect | GenericDialect) => { + Ok(self.parse_load()?) + } + _ => self.expected("an SQL statement", next_token), }, Token::LParen => { @@ -8791,6 +8800,19 @@ impl<'a> Parser<'a> { } } + /// `INSTALL [extension_name]` + pub fn parse_install(&mut self) -> Result { + let extension_name = self.parse_identifier(false)?; + + Ok(Statement::Install { extension_name }) + } + + /// `LOAD [extension_name]` + pub fn parse_load(&mut self) -> Result { + let extension_name = self.parse_identifier(false)?; + Ok(Statement::Load { extension_name }) + } + /// ```sql /// CREATE [ { TEMPORARY | TEMP } ] SEQUENCE [ IF NOT EXISTS ] /// ``` diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index db11d1e7..97bdd8e1 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -216,3 +216,31 @@ fn test_select_union_by_name() { assert_eq!(ast.body, expected); } } + +#[test] +fn test_duckdb_install() { + let stmt = duckdb().verified_stmt("INSTALL tpch"); + assert_eq!( + stmt, + Statement::Install { + extension_name: Ident { + value: "tpch".to_string(), + quote_style: None + } + } + ); +} + +#[test] +fn test_duckdb_load_extension() { + let stmt = duckdb().verified_stmt("LOAD my_extension"); + assert_eq!( + Statement::Load { + extension_name: Ident { + value: "my_extension".to_string(), + quote_style: None + } + }, + stmt + ); +}