@@ -58,45 +58,48 @@ peg::parser! {
5858 / aggregate_function( )
5959 / simple_aggregate_function( )
6060 / nothing( )
61- rule nullable( ) -> DT = "Nullable(" t: data_type( ) ")" { DT :: Nullable ( Box :: new( t) ) }
62- rule uint8( ) -> DT = "UInt8" { DT :: UInt8 }
63- rule uint16( ) -> DT = "UInt16" { DT :: UInt16 }
64- rule uint32( ) -> DT = "UInt32" { DT :: UInt32 }
65- rule uint64( ) -> DT = "UInt64" { DT :: UInt64 }
66- rule uint128( ) -> DT = "UInt128" { DT :: UInt128 }
67- rule uint256( ) -> DT = "UInt256" { DT :: UInt256 }
68- rule int8( ) -> DT = "Int8" { DT :: Int8 }
69- rule int16( ) -> DT = "Int16" { DT :: Int16 }
70- rule int32( ) -> DT = "Int32" { DT :: Int32 }
71- rule int64( ) -> DT = "Int64" { DT :: Int64 }
72- rule int128( ) -> DT = "Int128" { DT :: Int128 }
73- rule int256( ) -> DT = "Int256" { DT :: Int256 }
74- rule float32( ) -> DT = "Float32" { DT :: Float32 }
75- rule float64( ) -> DT = "Float64" { DT :: Float64 }
76- rule decimal( ) -> DT = "Decimal(" precision: integer_value( ) comma_separator( ) scale: integer_value( ) ")" { DT :: Decimal { precision, scale } }
77- rule decimal32( ) -> DT = "Decimal32(" scale: integer_value( ) ")" { DT :: Decimal32 { scale } }
78- rule decimal64( ) -> DT = "Decimal64(" scale: integer_value( ) ")" { DT :: Decimal64 { scale } }
79- rule decimal128( ) -> DT = "Decimal128(" scale: integer_value( ) ")" { DT :: Decimal128 { scale } }
80- rule decimal256( ) -> DT = "Decimal256(" scale: integer_value( ) ")" { DT :: Decimal256 { scale } }
81- rule bool ( ) -> DT = "Bool" { DT :: Bool }
82- rule string( ) -> DT = "String" { DT :: String }
83- rule fixed_string( ) -> DT = "FixedString(" n: integer_value( ) ")" { DT :: FixedString ( n) }
84- rule date( ) -> DT = "Date" { DT :: Date }
85- rule date32( ) -> DT = "Date32" { DT :: Date32 }
86- rule date_time( ) -> DT = "DateTime" tz: ( "(" tz: single_quoted_string_value( ) ? ")" { tz } ) ? { DT :: DateTime { timezone: tz. flatten( ) . map( |s| s. to_owned( ) ) } }
87- rule date_time64( ) -> DT = "DateTime64(" precision: integer_value( ) tz: ( comma_separator( ) tz: single_quoted_string_value( ) ? { tz } ) ? ")" { DT :: DateTime64 { precision, timezone: tz. flatten( ) . map( |s| s. to_owned( ) ) } }
88- rule uuid( ) -> DT = "UUID" { DT :: Uuid }
89- rule ipv4( ) -> DT = "IPv4" { DT :: IPv4 }
90- rule ipv6( ) -> DT = "IPv6" { DT :: IPv6 }
91- rule low_cardinality( ) -> DT = "LowCardinality(" t: data_type( ) ")" { DT :: LowCardinality ( Box :: new( t) ) }
92- rule nested( ) -> DT = "Nested(" e: ( ( n: identifier( ) __ t: data_type( ) { ( n, t) } ) * * comma_separator( ) ) ")" { DT :: Nested ( e) }
93- rule array( ) -> DT = "Array(" t: data_type( ) ")" { DT :: Array ( Box :: new( t) ) }
94- rule map( ) -> DT = "Map(" k: data_type( ) comma_separator( ) v: data_type( ) ")" { DT :: Map { key: Box :: new( k) , value: Box :: new( v) } }
95- rule tuple( ) -> DT = "Tuple(" e: ( ( n: ( n: identifier( ) __ { n } ) ? t: data_type( ) { ( n, t) } ) * * comma_separator( ) ) ")" { DT :: Tuple ( e) }
96- rule r#enum( ) -> DT = "Enum" ( "8" / "16" ) ? "(" e: ( ( n: single_quoted_string_value( ) i: ( _ "=" _ i: integer_value( ) { i } ) ? { ( n, i) } ) * * comma_separator( ) ) ")" { DT :: Enum ( e) }
97- rule aggregate_function( ) -> DT = "AggregateFunction(" f: aggregate_function_definition( ) comma_separator( ) a: ( data_type( ) * * comma_separator( ) ) ")" { DT :: AggregateFunction { function: f, arguments: a } }
98- rule simple_aggregate_function( ) -> DT = "SimpleAggregateFunction(" f: aggregate_function_definition( ) comma_separator( ) a: ( data_type( ) * * comma_separator( ) ) ")" { DT :: SimpleAggregateFunction { function: f, arguments: a } }
99- rule nothing( ) -> DT = "Nothing" { DT :: Nothing }
61+ rule nullable( ) -> DT = i( "Nullable(" ) t: data_type( ) ")" { DT :: Nullable ( Box :: new( t) ) }
62+ rule uint8( ) -> DT = i( "UInt8" ) { DT :: UInt8 }
63+ rule uint16( ) -> DT = i( "UInt16" ) { DT :: UInt16 }
64+ rule uint32( ) -> DT = i( "UInt32" ) { DT :: UInt32 }
65+ rule uint64( ) -> DT = i( "UInt64" ) { DT :: UInt64 }
66+ rule uint128( ) -> DT = i( "UInt128" ) { DT :: UInt128 }
67+ rule uint256( ) -> DT = i( "UInt256" ) { DT :: UInt256 }
68+ rule int8( ) -> DT = i( "Int8" ) { DT :: Int8 }
69+ rule int16( ) -> DT = i( "Int16" ) { DT :: Int16 }
70+ rule int32( ) -> DT = i( "Int32" ) { DT :: Int32 }
71+ rule int64( ) -> DT = i( "Int64" ) { DT :: Int64 }
72+ rule int128( ) -> DT = i( "Int128" ) { DT :: Int128 }
73+ rule int256( ) -> DT = i( "Int256" ) { DT :: Int256 }
74+ rule float32( ) -> DT = i( "Float32" ) { DT :: Float32 }
75+ rule float64( ) -> DT = i( "Float64" ) { DT :: Float64 }
76+ rule decimal( ) -> DT = i( "Decimal(" ) precision: integer_value( ) comma_separator( ) scale: integer_value( ) ")" { DT :: Decimal { precision, scale } }
77+ / i( "Decimal(" ) precision: integer_value( ) ")" { DT :: Decimal { precision, scale: 0 } }
78+ / i( "Decimal" ) { DT :: Decimal { precision: 10 , scale: 0 } }
79+ rule decimal32( ) -> DT = i( "Decimal32(" ) scale: integer_value( ) ")" { DT :: Decimal32 { scale } }
80+ rule decimal64( ) -> DT = i( "Decimal64(" ) scale: integer_value( ) ")" { DT :: Decimal64 { scale } }
81+ rule decimal128( ) -> DT = i( "Decimal128(" ) scale: integer_value( ) ")" { DT :: Decimal128 { scale } }
82+ rule decimal256( ) -> DT = i( "Decimal256(" ) scale: integer_value( ) ")" { DT :: Decimal256 { scale } }
83+ rule bool ( ) -> DT = i( "Bool" ) { DT :: Bool }
84+ rule string( ) -> DT = i( "String" ) { DT :: String }
85+ rule fixed_string( ) -> DT = i( "FixedString(" ) n: integer_value( ) ")" { DT :: FixedString ( n) }
86+ rule date( ) -> DT = i( "Date" ) { DT :: Date }
87+ rule date32( ) -> DT = i( "Date32" ) { DT :: Date32 }
88+ rule date_time( ) -> DT = i( "DateTime" ) tz: ( "(" tz: single_quoted_string_value( ) ? ")" { tz } ) ? { DT :: DateTime { timezone: tz. flatten( ) . map( |s| s. to_owned( ) ) } }
89+ rule date_time64( ) -> DT = i( "DateTime64(" ) precision: integer_value( ) tz: ( comma_separator( ) tz: single_quoted_string_value( ) ? { tz } ) ? ")" { DT :: DateTime64 { precision, timezone: tz. flatten( ) . map( |s| s. to_owned( ) ) } }
90+ / i( "DateTime64" ) { DT :: DateTime64 { precision: 3 , timezone: None } }
91+ rule uuid( ) -> DT = i( "UUID" ) { DT :: Uuid }
92+ rule ipv4( ) -> DT = i( "IPv4" ) { DT :: IPv4 }
93+ rule ipv6( ) -> DT = i( "IPv6" ) { DT :: IPv6 }
94+ rule low_cardinality( ) -> DT = i( "LowCardinality(" ) t: data_type( ) ")" { DT :: LowCardinality ( Box :: new( t) ) }
95+ rule nested( ) -> DT = i( "Nested(" ) e: ( ( n: identifier( ) __ t: data_type( ) { ( n, t) } ) * * comma_separator( ) ) ")" { DT :: Nested ( e) }
96+ rule array( ) -> DT = i( "Array(" ) t: data_type( ) ")" { DT :: Array ( Box :: new( t) ) }
97+ rule map( ) -> DT = i( "Map(" ) k: data_type( ) comma_separator( ) v: data_type( ) ")" { DT :: Map { key: Box :: new( k) , value: Box :: new( v) } }
98+ rule tuple( ) -> DT = i( "Tuple(" ) e: ( ( n: ( n: identifier( ) __ { n } ) ? t: data_type( ) { ( n, t) } ) * * comma_separator( ) ) ")" { DT :: Tuple ( e) }
99+ rule r#enum( ) -> DT = i( "Enum" ) ( "8" / "16" ) ? "(" e: ( ( n: single_quoted_string_value( ) i: ( _ "=" _ i: integer_value( ) { i } ) ? { ( n, i) } ) * * comma_separator( ) ) ")" { DT :: Enum ( e) }
100+ rule aggregate_function( ) -> DT = i( "AggregateFunction(" ) f: aggregate_function_definition( ) comma_separator( ) a: ( data_type( ) * * comma_separator( ) ) ")" { DT :: AggregateFunction { function: f, arguments: a } }
101+ rule simple_aggregate_function( ) -> DT = i( "SimpleAggregateFunction(" ) f: aggregate_function_definition( ) comma_separator( ) a: ( data_type( ) * * comma_separator( ) ) ")" { DT :: SimpleAggregateFunction { function: f, arguments: a } }
102+ rule nothing( ) -> DT = i( "Nothing" ) { DT :: Nothing }
100103
101104 rule aggregate_function_definition( ) -> AggregateFunctionDefinition = n: identifier( ) p: ( "(" p: ( aggregate_function_parameter( ) * * comma_separator( ) ) ")" { p } ) ? { AggregateFunctionDefinition { name: n, parameters: p } }
102105 rule aggregate_function_parameter( ) -> AggregateFunctionParameter = s: single_quoted_string_value( ) { AggregateFunctionParameter :: SingleQuotedString ( s) }
@@ -120,6 +123,8 @@ peg::parser! {
120123 rule _ = [ ' ' | '\t' | '\r' | '\n' ] *
121124 /// A comma surrounded by optional whitespace
122125 rule comma_separator( ) = _ "," _
126+ /// A case insensitive string
127+ rule i( literal: & ' static str ) = input: $( [ _] * <{ literal. len( ) } >) { ? if input. eq_ignore_ascii_case( literal) { Ok ( ( ) ) } else { Err ( literal) } }
123128 }
124129}
125130
@@ -195,7 +200,58 @@ fn can_parse_clickhouse_data_type() {
195200
196201 for ( s, t) in data_types {
197202 let parsed = clickhouse_parser:: data_type ( s) ;
198- assert_eq ! ( parsed, Ok ( t) , "Able to parse correctly" ) ;
203+ assert_eq ! ( parsed, Ok ( t) , "Able to parse {s} correctly" ) ;
204+ }
205+ }
206+
207+ #[ test]
208+ fn support_shorthands ( ) {
209+ let test_cases = vec ! [
210+ (
211+ "Decimal" ,
212+ DT :: Decimal {
213+ precision: 10 ,
214+ scale: 0 ,
215+ } ,
216+ ) ,
217+ (
218+ "Decimal(3)" ,
219+ DT :: Decimal {
220+ precision: 3 ,
221+ scale: 0 ,
222+ } ,
223+ ) ,
224+ (
225+ "DateTime64" ,
226+ DT :: DateTime64 {
227+ precision: 3 ,
228+ timezone: None ,
229+ } ,
230+ ) ,
231+ ] ;
232+
233+ for ( s, t) in test_cases {
234+ let parsed = clickhouse_parser:: data_type ( s) ;
235+ assert_eq ! ( parsed, Ok ( t) , "Able to parse {s} correctly" ) ;
236+ }
237+ }
238+
239+ #[ test]
240+ fn is_case_insensitive ( ) {
241+ let test_cases = vec ! [
242+ ( "dateTime" , "DateTime" ) ,
243+ ( "daTetIme64" , "DateTime64(3)" ) ,
244+ ( "bool" , "Bool" ) ,
245+ ( "STRING" , "String" ) ,
246+ ] ;
247+
248+ for ( input, cased) in test_cases {
249+ let parsed = clickhouse_parser:: data_type ( input) . expect ( "Able to parse type" ) ;
250+ assert_eq ! (
251+ & parsed. to_string( ) ,
252+ cased,
253+ "Should parse incorrectly cased type {input} into {cased}"
254+ ) ;
199255 }
200256}
201257
0 commit comments