@@ -194,6 +194,70 @@ impl fmt::Display for Array {
194194 }
195195}
196196
197+ /// Represents an INTERVAL expression, roughly in the following format:
198+ /// `INTERVAL '<value>' [ <leading_field> [ (<leading_precision>) ] ]
199+ /// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]`,
200+ /// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`.
201+ ///
202+ /// The parser does not validate the `<value>`, nor does it ensure
203+ /// that the `<leading_field>` units >= the units in `<last_field>`,
204+ /// so the user will have to reject intervals like `HOUR TO YEAR`.
205+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
206+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
207+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
208+ pub struct Interval {
209+ pub value : Box < Expr > ,
210+ pub leading_field : Option < DateTimeField > ,
211+ pub leading_precision : Option < u64 > ,
212+ pub last_field : Option < DateTimeField > ,
213+ /// The seconds precision can be specified in SQL source as
214+ /// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field`
215+ /// will be `Second` and the `last_field` will be `None`),
216+ /// or as `__ TO SECOND(x)`.
217+ pub fractional_seconds_precision : Option < u64 > ,
218+ }
219+
220+ impl fmt:: Display for Interval {
221+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
222+ let value = self . value . as_ref ( ) ;
223+ match (
224+ self . leading_field ,
225+ self . leading_precision ,
226+ self . fractional_seconds_precision ,
227+ ) {
228+ (
229+ Some ( DateTimeField :: Second ) ,
230+ Some ( leading_precision) ,
231+ Some ( fractional_seconds_precision) ,
232+ ) => {
233+ // When the leading field is SECOND, the parser guarantees that
234+ // the last field is None.
235+ assert ! ( self . last_field. is_none( ) ) ;
236+ write ! (
237+ f,
238+ "INTERVAL {value} SECOND ({leading_precision}, {fractional_seconds_precision})"
239+ )
240+ }
241+ _ => {
242+ write ! ( f, "INTERVAL {value}" ) ?;
243+ if let Some ( leading_field) = self . leading_field {
244+ write ! ( f, " {leading_field}" ) ?;
245+ }
246+ if let Some ( leading_precision) = self . leading_precision {
247+ write ! ( f, " ({leading_precision})" ) ?;
248+ }
249+ if let Some ( last_field) = self . last_field {
250+ write ! ( f, " TO {last_field}" ) ?;
251+ }
252+ if let Some ( fractional_seconds_precision) = self . fractional_seconds_precision {
253+ write ! ( f, " ({fractional_seconds_precision})" ) ?;
254+ }
255+ Ok ( ( ) )
256+ }
257+ }
258+ }
259+ }
260+
197261/// JsonOperator
198262#[ derive( Debug , Copy , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
199263#[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
@@ -491,25 +555,8 @@ pub enum Expr {
491555 ArrayIndex { obj : Box < Expr > , indexes : Vec < Expr > } ,
492556 /// An array expression e.g. `ARRAY[1, 2]`
493557 Array ( Array ) ,
494- /// INTERVAL literals, roughly in the following format:
495- /// `INTERVAL '<value>' [ <leading_field> [ (<leading_precision>) ] ]
496- /// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]`,
497- /// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`.
498- ///
499- /// The parser does not validate the `<value>`, nor does it ensure
500- /// that the `<leading_field>` units >= the units in `<last_field>`,
501- /// so the user will have to reject intervals like `HOUR TO YEAR`.
502- Interval {
503- value : Box < Expr > ,
504- leading_field : Option < DateTimeField > ,
505- leading_precision : Option < u64 > ,
506- last_field : Option < DateTimeField > ,
507- /// The seconds precision can be specified in SQL source as
508- /// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field`
509- /// will be `Second` and the `last_field` will be `None`),
510- /// or as `__ TO SECOND(x)`.
511- fractional_seconds_precision : Option < u64 > ,
512- } ,
558+ /// An interval expression e.g. `INTERVAL '1' YEAR`
559+ Interval ( Interval ) ,
513560 /// `MySQL` specific text search function [(1)].
514561 ///
515562 /// Syntax:
@@ -861,42 +908,8 @@ impl fmt::Display for Expr {
861908 } => {
862909 write ! ( f, "{timestamp} AT TIME ZONE '{time_zone}'" )
863910 }
864- Expr :: Interval {
865- value,
866- leading_field : Some ( DateTimeField :: Second ) ,
867- leading_precision : Some ( leading_precision) ,
868- last_field,
869- fractional_seconds_precision : Some ( fractional_seconds_precision) ,
870- } => {
871- // When the leading field is SECOND, the parser guarantees that
872- // the last field is None.
873- assert ! ( last_field. is_none( ) ) ;
874- write ! (
875- f,
876- "INTERVAL {value} SECOND ({leading_precision}, {fractional_seconds_precision})"
877- )
878- }
879- Expr :: Interval {
880- value,
881- leading_field,
882- leading_precision,
883- last_field,
884- fractional_seconds_precision,
885- } => {
886- write ! ( f, "INTERVAL {value}" ) ?;
887- if let Some ( leading_field) = leading_field {
888- write ! ( f, " {leading_field}" ) ?;
889- }
890- if let Some ( leading_precision) = leading_precision {
891- write ! ( f, " ({leading_precision})" ) ?;
892- }
893- if let Some ( last_field) = last_field {
894- write ! ( f, " TO {last_field}" ) ?;
895- }
896- if let Some ( fractional_seconds_precision) = fractional_seconds_precision {
897- write ! ( f, " ({fractional_seconds_precision})" ) ?;
898- }
899- Ok ( ( ) )
911+ Expr :: Interval ( interval) => {
912+ write ! ( f, "{interval}" )
900913 }
901914 Expr :: MatchAgainst {
902915 columns,
@@ -4410,4 +4423,30 @@ mod tests {
44104423 ] ) ;
44114424 assert_eq ! ( "CUBE (a, (b, c), d)" , format!( "{cube}" ) ) ;
44124425 }
4426+
4427+ #[ test]
4428+ fn test_interval_display ( ) {
4429+ let interval = Expr :: Interval ( Interval {
4430+ value : Box :: new ( Expr :: Value ( Value :: SingleQuotedString ( String :: from (
4431+ "123:45.67" ,
4432+ ) ) ) ) ,
4433+ leading_field : Some ( DateTimeField :: Minute ) ,
4434+ leading_precision : Some ( 10 ) ,
4435+ last_field : Some ( DateTimeField :: Second ) ,
4436+ fractional_seconds_precision : Some ( 9 ) ,
4437+ } ) ;
4438+ assert_eq ! (
4439+ "INTERVAL '123:45.67' MINUTE (10) TO SECOND (9)" ,
4440+ format!( "{interval}" ) ,
4441+ ) ;
4442+
4443+ let interval = Expr :: Interval ( Interval {
4444+ value : Box :: new ( Expr :: Value ( Value :: SingleQuotedString ( String :: from ( "5" ) ) ) ) ,
4445+ leading_field : Some ( DateTimeField :: Second ) ,
4446+ leading_precision : Some ( 1 ) ,
4447+ last_field : None ,
4448+ fractional_seconds_precision : Some ( 3 ) ,
4449+ } ) ;
4450+ assert_eq ! ( "INTERVAL '5' SECOND (1, 3)" , format!( "{interval}" ) ) ;
4451+ }
44134452}
0 commit comments