Skip to content

Commit

Permalink
Merge branch 'wwbakker-add-underlying-primitive-support'
Browse files Browse the repository at this point in the history
  • Loading branch information
julianpeeters committed Feb 2, 2024
2 parents e099ae6 + b003934 commit ecece18
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 16 deletions.
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ Scalding, Spark, Avro, etc.).
|UNION|Option<br>Either<br>Shapeless Coproduct|Option<br>Either<br>Shapeless Coproduct| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|RECORD|case class<br>case class + schema|case class extending `SpecificRecordBase`| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|PROTOCOL|_No Type_<br>Scala ADT|RPC trait<br>Scala ADT| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|Date|java.time.LocalDate<br>java.sql.Date|java.time.LocalDate<br>java.sql.Date| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|TimeMillis|java.time.LocalTime|java.time.LocalTime| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|TimeMicros|java.time.LocalTime|java.time.LocalTime| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|TimestampMillis|java.time.Instant<br>java.sql.Timestamp|java.time.Instant<br>java.sql.Timestamp| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|TimestampMicros|java.time.Instant<br>java.sql.Timestamp|java.time.Instant<br>java.sql.Timestamp| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|LocalTimestampMillis|java.time.LocalDateTime|java.time.LocalDateTime| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|LocalTimestampMicros|java.time.LocalDateTime|java.time.LocalDateTime| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|Date|java.time.LocalDate<br>java.sql.Date<br>Int|java.time.LocalDate<br>java.sql.Date<br>Int| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|TimeMillis|java.time.LocalTime<br>Int|java.time.LocalTime<br>Int| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|TimeMicros|java.time.LocalTime<br>Long|java.time.LocalTime<br>Long| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|TimestampMillis|java.time.Instant<br>java.sql.Timestamp<br>Long|java.time.Instant<br>java.sql.Timestamp<br>Long| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|TimestampMicros|java.time.Instant<br>java.sql.Timestamp<br>Long|java.time.Instant<br>java.sql.Timestamp<br>Long| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|LocalTimestampMillis|java.time.LocalDateTime<br>Long|java.time.LocalDateTime<br>Long| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|LocalTimestampMicros|java.time.LocalDateTime<br>Long|java.time.LocalDateTime<br>Long| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|UUID|java.util.UUID|java.util.UUID| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
|Decimal|BigDecimal|BigDecimal| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|

Expand All @@ -82,9 +82,9 @@ _NOTE: Currently logical types are only supported for `Standard` and `SpecificRe

* `date`: Annotates Avro `int` schemas to generate `java.time.LocalDate` or `java.sql.Date` (See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)). Examples: [avdl](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avdl#L9), [avsc](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avsc#L22-L27).
* `decimal`: Annotates Avro `bytes` and `fixed` schemas to generate `BigDecimal`. Examples: [avdl](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avdl#L6), [avsc](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avsc#L6-L14).
* `timestamp-millis`: Annotates Avro `long` schemas to genarate `java.time.Instant` or `java.sql.Timestamp` (See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)). Examples: [avdl](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avdl#L8), [avsc](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avsc#L15-L21).
* `timestamp-millis`: Annotates Avro `long` schemas to genarate `java.time.Instant` or `java.sql.Timestamp` or `long` (See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)). Examples: [avdl](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avdl#L8), [avsc](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avsc#L15-L21).
* `uuid`: Annotates Avro `string` schemas and idls to generate `java.util.UUID` (See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)). Example: [avsc](https://github.com/julianpeeters/sbt-avrohugger/blob/master/src/sbt-test/avrohugger/GenericSerializationTests/src/main/avro/logical.avsc#L29-L35).
* `time-millis`: Annotates Avro `int` schemas to genarate `java.time.LocalTime` or `java.sql.Time`
* `time-millis`: Annotates Avro `int` schemas to genarate `java.time.LocalTime` or `java.sql.Time` or `int`

##### Protocol Support:

Expand Down Expand Up @@ -119,7 +119,7 @@ _Note:_ Currently [Treehugger](http://eed3si9n.com/treehugger/comments.html#Scal

##### Get the dependency with:

"com.julianpeeters" %% "avrohugger-core" % "2.8.2"
"com.julianpeeters" %% "avrohugger-core" % "2.8.3"


##### Description:
Expand Down Expand Up @@ -211,7 +211,7 @@ namespace rewritten. Multiple conflicting wildcards are not permitted.

##### Get the dependency with:

"com.julianpeeters" %% "avrohugger-filesorter" % "2.8.2"
"com.julianpeeters" %% "avrohugger-filesorter" % "2.8.3"


##### Description:
Expand All @@ -231,17 +231,17 @@ To ensure dependent schemas are compiled in the proper order (thus avoiding `org
#### `avrohugger-tools`


Download the avrohugger-tools jar for Scala [2.12](https://search.maven.org/remotecontent?filepath=com/julianpeeters/avrohugger-tools_2.12/2.8.2/avrohugger-tools_2.12-2.8.2-assembly.jar), or Scala [2.13](https://search.maven.org/remotecontent?filepath=com/julianpeeters/avrohugger-tools_2.13/2.8.2/avrohugger-tools_2.13-2.8.2-assembly.jar) (>30MB!) and use it like the avro-tools jar `Usage: [-string] (schema|protocol|datafile) input... outputdir`:
Download the avrohugger-tools jar for Scala [2.12](https://search.maven.org/remotecontent?filepath=com/julianpeeters/avrohugger-tools_2.12/2.8.3/avrohugger-tools_2.12-2.8.3-assembly.jar), or Scala [2.13](https://search.maven.org/remotecontent?filepath=com/julianpeeters/avrohugger-tools_2.13/2.8.3/avrohugger-tools_2.13-2.8.3-assembly.jar) (>30MB!) and use it like the avro-tools jar `Usage: [-string] (schema|protocol|datafile) input... outputdir`:


* `generate` generates Scala case class definitions:

`java -jar /path/to/avrohugger-tools_2.12-2.8.2-assembly.jar generate schema user.avsc . `
`java -jar /path/to/avrohugger-tools_2.12-2.8.3-assembly.jar generate schema user.avsc . `


* `generate-specific` generates definitions that extend Avro's `SpecificRecordBase`:

`java -jar /path/to/avrohugger-tools_2.12-2.8.2-assembly.jar generate-specific schema user.avsc . `
`java -jar /path/to/avrohugger-tools_2.12-2.8.3-assembly.jar generate-specific schema user.avsc . `


## Warnings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,29 +154,36 @@ object JavaConverter {
case timestamp: LogicalTypes.TimestampMillis => typeMatcher.avroScalaTypes.timestampMillis match {
case JavaSqlTimestamp => BLOCK(tree.DOT("getTime").APPLY())
case JavaTimeInstant => BLOCK(tree.DOT("toEpochMilli"))
case UnderlyingPrimitive => tree
}
case _: LogicalTypes.TimestampMicros => (typeMatcher.avroScalaTypes.timestampMicros match {
case JavaTimeZonedDateTime => BLOCK(tree.DOT("toEpochSecond").INFIX("*", LIT(1000000L)).INFIX("+", tree.DOT("getNano").INFIX("/", LIT(1000L))))
case UnderlyingPrimitive => tree
}) withComment "avro timestamp-micros long stores the number of microseconds from the unix epoch, 1 January 1970 00:00:00.000000 UTC"
case _: LogicalTypes.LocalTimestampMillis => (typeMatcher.avroScalaTypes.localTimestampMillis match {
case JavaTimeLocalDateTime => BLOCK(tree.DOT("toEpochSecond").APPLY(RootClass.newClass("java.time.ZoneOffset").DOT("UTC")).INFIX("*", LIT(1000L)).INFIX("+", tree.DOT("getNano").INFIX("/", LIT(1000000L))))
case UnderlyingPrimitive => tree
}) withComment "avro local-timestamp-millis long stores the number of millis, from 1 January 1970 00:00:00.000000"
case _: LogicalTypes.LocalTimestampMicros => (typeMatcher.avroScalaTypes.localTimestampMicros match {
case JavaTimeLocalDateTime => BLOCK(tree.DOT("toEpochSecond").APPLY(RootClass.newClass("java.time.ZoneOffset").DOT("UTC")).INFIX("*", LIT(1000000L)).INFIX("+", tree.DOT("getNano").INFIX("/", LIT(1000L))))
case UnderlyingPrimitive => tree
}) withComment "avro local-timestamp-micros long stores the number of microseconds, from 1 January 1970 00:00:00.000000"
case _: LogicalTypes.TimeMicros => (typeMatcher.avroScalaTypes.timeMicros match {
case JavaTimeLocalTime => BLOCK(tree.DOT("toNanoOfDay").INFIX("/", LIT(1000L)))
case UnderlyingPrimitive => tree
}) withComment "avro time-micros long stores the number of microseconds after midnight, 00:00:00.000000"
case _ => tree
}
case Schema.Type.INT => schema.getLogicalType match {
case date: LogicalTypes.Date => typeMatcher.avroScalaTypes.date match {
case JavaSqlDate => tree.DOT("getTime").APPLY().DOT("/").APPLY(LIT(86400000))
case JavaTimeLocalDate => tree.DOT("toEpochDay").DOT("toInt")
case UnderlyingPrimitive => tree
}
case timeMillis: LogicalTypes.TimeMillis => typeMatcher.avroScalaTypes.timeMillis match {
case JavaSqlTime => tree.DOT("getTime").APPLY()
case JavaTimeLocalTime => tree.DOT("get").APPLY(REF("java.time.temporal.ChronoField").DOT("MILLI_OF_DAY"))
case UnderlyingPrimitive => tree
}
case _ => tree
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ object ScalaConverter {
val LocalTimeClass = RootClass.newClass("java.time.LocalTime")
val resultExpr = BLOCK(LocalTimeClass.DOT("ofNanoOfDay").APPLY(REF("l").INFIX("*", LIT(1000L))))
tree MATCH caseLWithTypeLong ==> resultExpr
case UnderlyingPrimitive => tree
}) withComment "avro time-micros long stores the number of microseconds after midnight, 00:00:00.000000"
} else if (logicalType.getName == "timestamp-millis") {
typeMatcher.avroScalaTypes.timestampMillis match {
Expand All @@ -221,6 +222,7 @@ object ScalaConverter {
val longConversion = CASE(ID("l") withType (LongClass)) ==> resultExpr
tree MATCH longConversion
}
case UnderlyingPrimitive => tree
}
} else if (logicalType.getName == "timestamp-micros") {
(typeMatcher.avroScalaTypes.timestampMicros match {
Expand All @@ -235,6 +237,7 @@ object ScalaConverter {
ZoneOffset DOT "UTC"
), ZoneId DOT "of" APPLY LIT("UTC")))
tree MATCH CASE(ID("l") withType (LongClass)) ==> resultExpr
case UnderlyingPrimitive => tree
}) withComment "avro timestamp-micros long stores the number of microseconds from the unix epoch, 1 January 1970 00:00:00.000000 UTC"
} else if (logicalType.getName == "local-timestamp-millis") {
(typeMatcher.avroScalaTypes.localTimestampMillis match {
Expand All @@ -247,6 +250,7 @@ object ScalaConverter {
ZoneOffset DOT "UTC"
))
tree MATCH CASE(ID("l") withType (LongClass)) ==> resultExpr
case UnderlyingPrimitive => tree
}) withComment "avro local-timestamp-millis long stores the number of millis, from 1 January 1970 00:00:00.000000"
} else if (logicalType.getName == "local-timestamp-micros") {
(typeMatcher.avroScalaTypes.localTimestampMicros match {
Expand All @@ -259,6 +263,7 @@ object ScalaConverter {
ZoneOffset DOT "UTC"
))
tree MATCH CASE(ID("l") withType (LongClass)) ==> resultExpr
case UnderlyingPrimitive => tree
}) withComment "avro local-timestamp-micros long stores the number of microseconds, from 1 January 1970 00:00:00.000000"
}
else tree
Expand All @@ -285,6 +290,7 @@ object ScalaConverter {
val integerConversion = CASE(ID("i") withType (IntegerClass)) ==> resultExpr
tree MATCH integerConversion
}
case UnderlyingPrimitive => tree
}
}
else if (logicalType.getName == "time-millis") {
Expand All @@ -303,6 +309,7 @@ object ScalaConverter {
val integerConversion = CASE(ID("i") withType (IntegerClass)) ==> resultExpr
tree MATCH integerConversion
}
case UnderlyingPrimitive => tree
}
}
else tree
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ object CustomDefaultParamMatcher {
def checkCustomDateType(dateType: AvroScalaDateType) = dateType match {
case JavaSqlDate => NEW(REF("java.sql.Date"), LIT(0L))
case JavaTimeLocalDate => REF("java.time.LocalDate.now")
case UnderlyingPrimitive => LIT(0)
}

def checkCustomTimestampMillisType(timestampMillisType: AvroScalaTimestampMillisType) =
timestampMillisType match {
case JavaSqlTimestamp => NEW(REF("java.sql.Timestamp"), LIT(0L))
case JavaTimeInstant => REF("java.time.Instant.now")
case UnderlyingPrimitive => LIT(0L)
}

def checkCustomDecimalType(decimalType: AvroScalaDecimalType, schema: Schema, default: => Tree, decimalValue: => Option[String] = None) = {
Expand All @@ -57,25 +59,30 @@ object CustomDefaultParamMatcher {
timeMillisType match {
case JavaSqlTime => NEW(REF("java.sql.Time"), LIT(0L))
case JavaTimeLocalTime => REF("java.time.LocalTime.now")
case UnderlyingPrimitive => LIT(0L)
}

def checkCustomTimeMicrosType(timeMillisType: AvroScalaTimeType): Tree =
timeMillisType match {
case JavaTimeLocalTime => REF("java.time.LocalTime.MIDNIGHT")
case UnderlyingPrimitive => LIT(0L)
}

def checkCustomTimestampMicrosType(timeMillisType: AvroScalaTimestampType): Tree =
timeMillisType match {
case JavaTimeZonedDateTime => REF("java.time.ZonedDateTime.of").APPLY(REF("java.time.LocalDateTime") DOT "MIN", REF("java.time.ZoneId") DOT "of" APPLY LIT("UTC"))
case UnderlyingPrimitive => LIT(0L)
}

def checkCustomLocalTimestampMillisType(timeMillisType: AvroScalaLocalTimestampType): Tree =
timeMillisType match {
case JavaTimeLocalDateTime => REF("java.time.LocalDateTime") DOT "MIN"
case UnderlyingPrimitive => LIT(0L)
}

def checkCustomLocalTimestampMicrosType(timeMillisType: AvroScalaLocalTimestampType): Tree =
timeMillisType match {
case JavaTimeLocalDateTime => REF("java.time.LocalDateTime") DOT "MIN"
case UnderlyingPrimitive => LIT(0L)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@ object CustomDefaultValueMatcher {
def checkCustomDateType(value: Long, dateType: AvroScalaDateType) = dateType match {
case JavaSqlDate => NEW("java.sql.Date", LIT(value))
case JavaTimeLocalDate => REF("java.time.LocalDate.ofEpochDay") APPLY LIT(value)
case UnderlyingPrimitive => LIT(value)
}

def checkCustomTimestampMillisType(value: Long, timestampMillisType: AvroScalaTimestampMillisType) =
timestampMillisType match {
case JavaSqlTimestamp => NEW("java.sql.Timestamp", LIT(value))
case JavaTimeInstant => REF("java.time.Instant.ofEpochMilli") APPLY LIT(value)
case UnderlyingPrimitive => LIT(value)
}

def checkCustomTimeMillisType(value: Long, timeMillisType: AvroScalaTimeMillisType) =
timeMillisType match {
case JavaSqlTime => NEW("java.sql.Time", LIT(value))
case JavaTimeLocalTime => REF("java.time.LocalTime.ofNanoOfDay").APPLY(LIT(value).INFIX("*", LIT(1000000L)))
case UnderlyingPrimitive => LIT(value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,19 @@ object CustomTypeMatcher {
def checkCustomDateType(dateType: AvroScalaDateType) = dateType match {
case JavaTimeLocalDate => RootClass.newClass(nme.createNameType("java.time.LocalDate"))
case JavaSqlDate => RootClass.newClass(nme.createNameType("java.sql.Date"))
case UnderlyingPrimitive => IntClass
}

def checkCustomTimestampMillisType(timestampType: AvroScalaTimestampMillisType) = timestampType match {
case JavaSqlTimestamp => RootClass.newClass(nme.createNameType("java.sql.Timestamp"))
case JavaTimeInstant => RootClass.newClass(nme.createNameType("java.time.Instant"))
case UnderlyingPrimitive => LongClass
}

def checkCustomTimeMillisType(timeType: AvroScalaTimeMillisType) = timeType match {
case JavaSqlTime => RootClass.newClass(nme.createNameType("java.sql.Time"))
case JavaTimeLocalTime => RootClass.newClass(nme.createNameType("java.time.LocalTime"))
case UnderlyingPrimitive => LongClass
}

def checkCustomDecimalType(decimalType: AvroScalaDecimalType, schema: Schema): Type =
Expand All @@ -65,17 +68,21 @@ object CustomTypeMatcher {

def checkCustomTimeMicrosType(timeType: AvroScalaTimeType) = timeType match {
case JavaTimeLocalTime => RootClass.newClass(nme.createNameType("java.time.LocalTime"))
case UnderlyingPrimitive => LongClass
}

def checkCustomTimestampMicrosType(timeType: AvroScalaTimestampType) = timeType match {
case JavaTimeZonedDateTime => RootClass.newClass(nme.createNameType("java.time.ZonedDateTime"))
case UnderlyingPrimitive => LongClass
}

def checkCustomLocalTimestampMicrosType(timeType: AvroScalaLocalTimestampType) = timeType match {
case JavaTimeLocalDateTime => RootClass.newClass(nme.createNameType("java.time.LocalDateTime"))
case UnderlyingPrimitive => LongClass
}

def checkCustomLocalTimestampMillisType(timeType: AvroScalaLocalTimestampType) = timeType match {
case JavaTimeLocalDateTime => RootClass.newClass(nme.createNameType("java.time.LocalDateTime"))
case UnderlyingPrimitive => LongClass
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ case object JavaTimeZonedDateTime extends AvroScalaTimestampType

sealed trait AvroScalaLocalTimestampType extends Serializable
case object JavaTimeLocalDateTime extends AvroScalaLocalTimestampType

case object UnderlyingPrimitive extends AvroScalaTimeType with AvroScalaTimeMillisType with AvroScalaTimestampMillisType with AvroScalaTimestampType with AvroScalaLocalTimestampType with AvroScalaDateType
sealed abstract class LogicalType(name: String)
case class Decimal(precision: Int, scale: Int) extends LogicalType("decimal")
case object Date extends LogicalType("date")
Expand Down
Loading

0 comments on commit ecece18

Please sign in to comment.