@@ -1117,6 +1117,10 @@ impl<'a> Parser<'a> {
1117
1117
self . prev_token ( ) ;
1118
1118
Ok ( Expr :: Value ( self . parse_value ( ) ?) )
1119
1119
}
1120
+ Token :: LBrace if dialect_of ! ( self is DuckDbDialect | GenericDialect ) => {
1121
+ self . prev_token ( ) ;
1122
+ self . parse_duckdb_struct_literal ( )
1123
+ }
1120
1124
_ => self . expected ( "an expression:" , next_token) ,
1121
1125
} ?;
1122
1126
@@ -2127,6 +2131,45 @@ impl<'a> Parser<'a> {
2127
2131
) )
2128
2132
}
2129
2133
2134
+ /// DuckDB specific: Parse a duckdb dictionary [1]
2135
+ ///
2136
+ /// Syntax:
2137
+ ///
2138
+ /// ```sql
2139
+ /// {'field_name': expr1[, ... ]}
2140
+ /// ```
2141
+ ///
2142
+ /// [1]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
2143
+ fn parse_duckdb_struct_literal ( & mut self ) -> Result < Expr , ParserError > {
2144
+ self . expect_token ( & Token :: LBrace ) ?;
2145
+
2146
+ let fields = self . parse_comma_separated ( Self :: parse_duckdb_dictionary_field) ?;
2147
+
2148
+ self . expect_token ( & Token :: RBrace ) ?;
2149
+
2150
+ Ok ( Expr :: Dictionary ( fields) )
2151
+ }
2152
+
2153
+ /// Parse a field for a duckdb dictionary [1]
2154
+ /// Syntax
2155
+ /// ```sql
2156
+ /// 'name': expr
2157
+ /// ```
2158
+ ///
2159
+ /// [1]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
2160
+ fn parse_duckdb_dictionary_field ( & mut self ) -> Result < DictionaryField , ParserError > {
2161
+ let key = self . parse_identifier ( false ) ?;
2162
+
2163
+ self . expect_token ( & Token :: Colon ) ?;
2164
+
2165
+ let expr = self . parse_expr ( ) ?;
2166
+
2167
+ Ok ( DictionaryField {
2168
+ key,
2169
+ value : Box :: new ( expr) ,
2170
+ } )
2171
+ }
2172
+
2130
2173
/// For nested types that use the angle bracket syntax, this matches either
2131
2174
/// `>`, `>>` or nothing depending on which variant is expected (specified by the previously
2132
2175
/// matched `trailing_bracket` argument). It returns whether there is a trailing
@@ -3031,6 +3074,7 @@ impl<'a> Parser<'a> {
3031
3074
let temporary = self
3032
3075
. parse_one_of_keywords ( & [ Keyword :: TEMP , Keyword :: TEMPORARY ] )
3033
3076
. is_some ( ) ;
3077
+ let persistent = dialect_of ! ( self is DuckDbDialect ) && self . parse_one_of_keywords ( & [ Keyword :: PERSISTENT ] ) . is_some ( ) ;
3034
3078
if self . parse_keyword ( Keyword :: TABLE ) {
3035
3079
self . parse_create_table ( or_replace, temporary, global, transient)
3036
3080
} else if self . parse_keyword ( Keyword :: MATERIALIZED ) || self . parse_keyword ( Keyword :: VIEW ) {
@@ -3042,6 +3086,8 @@ impl<'a> Parser<'a> {
3042
3086
self . parse_create_function ( or_replace, temporary)
3043
3087
} else if self . parse_keyword ( Keyword :: MACRO ) {
3044
3088
self . parse_create_macro ( or_replace, temporary)
3089
+ } else if self . parse_keyword ( Keyword :: SECRET ) {
3090
+ self . parse_create_secret ( or_replace, temporary, persistent)
3045
3091
} else if or_replace {
3046
3092
self . expected (
3047
3093
"[EXTERNAL] TABLE or [MATERIALIZED] VIEW or FUNCTION after CREATE OR REPLACE" ,
@@ -3072,6 +3118,62 @@ impl<'a> Parser<'a> {
3072
3118
}
3073
3119
}
3074
3120
3121
+ /// See [DuckDB Docs](https://duckdb.org/docs/sql/statements/create_secret.html) for more details.
3122
+ pub fn parse_create_secret (
3123
+ & mut self ,
3124
+ or_replace : bool ,
3125
+ temporary : bool ,
3126
+ persistent : bool ,
3127
+ ) -> Result < Statement , ParserError > {
3128
+ let if_not_exists = self . parse_keywords ( & [ Keyword :: IF , Keyword :: NOT , Keyword :: EXISTS ] ) ;
3129
+
3130
+ let mut storage_specifier = None ;
3131
+ let mut name = None ;
3132
+ if self . peek_token ( ) != Token :: LParen {
3133
+ if self . parse_keyword ( Keyword :: IN ) {
3134
+ storage_specifier = self . parse_identifier ( false ) . ok ( )
3135
+ } else {
3136
+ name = self . parse_identifier ( false ) . ok ( ) ;
3137
+ }
3138
+
3139
+ // Storage specifier may follow the name
3140
+ if storage_specifier. is_none ( ) && self . peek_token ( ) != Token :: LParen && self . parse_keyword ( Keyword :: IN ) {
3141
+ storage_specifier = self . parse_identifier ( false ) . ok ( ) ;
3142
+ }
3143
+ }
3144
+
3145
+ self . expect_token ( & Token :: LParen ) ?;
3146
+ self . expect_keyword ( Keyword :: TYPE ) ?;
3147
+ let secret_type = self . parse_identifier ( false ) ?;
3148
+
3149
+ let mut options = Vec :: new ( ) ;
3150
+ if self . consume_token ( & Token :: Comma ) {
3151
+ options. append ( & mut self . parse_comma_separated ( |p| {
3152
+ let key = p. parse_identifier ( false ) ?;
3153
+ let value = p. parse_identifier ( false ) ?;
3154
+ Ok ( SecretOption { key, value } )
3155
+ } ) ?) ;
3156
+ }
3157
+ self . expect_token ( & Token :: RParen ) ?;
3158
+
3159
+ let temp = match ( temporary, persistent) {
3160
+ ( true , false ) => Some ( true ) ,
3161
+ ( false , true ) => Some ( false ) ,
3162
+ ( false , false ) => None ,
3163
+ _ => self . expected ( "TEMPORARY or PERSISTENT" , self . peek_token ( ) ) ?,
3164
+ } ;
3165
+
3166
+ Ok ( Statement :: CreateSecret {
3167
+ or_replace,
3168
+ temporary : temp,
3169
+ if_not_exists,
3170
+ name,
3171
+ storage_specifier,
3172
+ secret_type,
3173
+ options,
3174
+ } )
3175
+ }
3176
+
3075
3177
/// Parse a CACHE TABLE statement
3076
3178
pub fn parse_cache_table ( & mut self ) -> Result < Statement , ParserError > {
3077
3179
let ( mut table_flag, mut options, mut has_as, mut query) = ( None , vec ! [ ] , false , None ) ;
@@ -3805,8 +3907,9 @@ impl<'a> Parser<'a> {
3805
3907
3806
3908
pub fn parse_drop ( & mut self ) -> Result < Statement , ParserError > {
3807
3909
// MySQL dialect supports `TEMPORARY`
3808
- let temporary = dialect_of ! ( self is MySqlDialect | GenericDialect )
3910
+ let temporary = dialect_of ! ( self is MySqlDialect | GenericDialect | DuckDbDialect )
3809
3911
&& self . parse_keyword ( Keyword :: TEMPORARY ) ;
3912
+ let persistent = dialect_of ! ( self is DuckDbDialect ) && self . parse_one_of_keywords ( & [ Keyword :: PERSISTENT ] ) . is_some ( ) ;
3810
3913
3811
3914
let object_type = if self . parse_keyword ( Keyword :: TABLE ) {
3812
3915
ObjectType :: Table
@@ -3824,6 +3927,8 @@ impl<'a> Parser<'a> {
3824
3927
ObjectType :: Stage
3825
3928
} else if self . parse_keyword ( Keyword :: FUNCTION ) {
3826
3929
return self . parse_drop_function ( ) ;
3930
+ } else if self . parse_keyword ( Keyword :: SECRET ) {
3931
+ return self . parse_drop_secret ( temporary, persistent) ;
3827
3932
} else {
3828
3933
return self . expected (
3829
3934
"TABLE, VIEW, INDEX, ROLE, SCHEMA, FUNCTION, STAGE or SEQUENCE after DROP" ,
@@ -3896,6 +4001,25 @@ impl<'a> Parser<'a> {
3896
4001
Ok ( DropFunctionDesc { name, args } )
3897
4002
}
3898
4003
4004
+ /// See [DuckDB Docs](https://duckdb.org/docs/sql/statements/create_secret.html) for more details.
4005
+ fn parse_drop_secret ( & mut self , temporary : bool , persistent : bool ) -> Result < Statement , ParserError > {
4006
+ let if_exists = self . parse_keywords ( & [ Keyword :: IF , Keyword :: EXISTS ] ) ;
4007
+ let name = self . parse_identifier ( false ) ?;
4008
+ let storage_specifier = if self . parse_keyword ( Keyword :: FROM ) {
4009
+ self . parse_identifier ( false ) . ok ( )
4010
+ } else {
4011
+ None
4012
+ } ;
4013
+ let temp = match ( temporary, persistent) {
4014
+ ( true , false ) => Some ( true ) ,
4015
+ ( false , true ) => Some ( false ) ,
4016
+ ( false , false ) => None ,
4017
+ _ => self . expected ( "TEMPORARY or PERSISTENT" , self . peek_token ( ) ) ?,
4018
+ } ;
4019
+
4020
+ Ok ( Statement :: DropSecret { if_exists, temporary : temp, name, storage_specifier} )
4021
+ }
4022
+
3899
4023
/// Parse a `DECLARE` statement.
3900
4024
///
3901
4025
/// ```sql
0 commit comments