Skip to content

v0.3.0-beta9

Latest
Compare
Choose a tag to compare
@takapi327 takapi327 released this 07 Dec 12:18
· 231 commits to master since this release

ldbc v0.3.0-beta9 is released.
This release adds enhancements to existing features.

Note

ldbc is pre-1.0 software and is still undergoing active development. New versions are not binary compatible with prior versions, although in most cases user code will be source compatible.
The major version will be the stable version.

What's Changed

Caution

This version is not compatible with the previous version, v0.3.0-beta8.

Adding Column Annotation

Table generation using Deriving used the property name as it is as the column name.

case class City(
  id:          Int,
  name:        String,
  countryCode: String,
  district:    String,
  population:  Int
) derives Table

The formatting of the column names could be adjusted by implicitly passing Naming.

given Naming = Naming.PASCAL

However, this alone could not address all cases. For example, the following column names cannot be used in the previous implementation.

CREATE TABLE city (
  ID Bigint
  CountryID Bigint
)

Naming supports only certain formats (CAMEL, PASCAL, and SNAKE cases) and does not support column names that are all uppercase or only uppercase in certain parts of the column name.

Such cases can be resolved by using column annotations.

case class City(
  @Column("ID") id: Int,
  name:             String,
  countryCode:      String,
  district:         String,
  population:       Int
) derives Table

The overall formatting is done by Naming, and changes to only specific columns can be made using this annotation, which allows for flexible customization of column names.

Support for INSERT SELECT/JOIN

There are two ways to pass values in an INSERT statement: by VALUES or by using a SELECT statement or JOIN.

This modification supports both syntaxes.

TableQuery[City]
   .insertInto(city => city. id *: city. name)
   .select(
     TableQuery[Country]
       .select(country => country. id *: city. name)
   )

The same can be defined for using the records resulting from a JOIN.

TableQuery[City]
   .insertInto(city => city. id *: city. name)
   .select(
     TableQuery[Country]
       .join(TableQuery[City])
       .on((country, city) => country. id === city. countryId)
       .select((country, city) => country. id *: city. name)
       .where((country, city) => city. population > 1000000)
   )

Breaking Change

Change column refinement method

So far, column refinement has simply grouped the columns used as Tuple.

cityTable.select(city => (city.id, city.name))

However, there was a problem with this. Column is a type with a single type parameter. Until Scala2, there was a limit to the number of Tuples that could be passed. Scala 2 had a limit on the number of Tuples, so it was necessary to create a boilerplate or something that could handle all the number of Tuples.
In this case, dynamic Tuples are treated as Tuples or Tuple.Map, so if you wanted to access a Column type, you had to cast the type using asInstanceOf because its type can only be treated as a Tuple.
Casting the type would of course lose its type safety and complicate the code.

We decided to adopt twiddles, one of the same TypeLevel projects, to solve this problem.

Columns can be more easily composited by using twiddles.

cityTable.select(city => city.id *: city.name)

Composition itself is implementor-friendly because it can be done using *:, and this eliminates the need for unsafe typecasting since the internal code can just use Column[T] instead of Tuple.

Twiddles also make it easy to convert the composite result to another type.

case class City(id: Long, name: String)

def id: Column[Int] = column[Int]("ID")
def name: Column[String] = column[String]("Name")

def city: Column[City] = (id *: name).to[City]

Change from Table to TableQuery

Until now, the same Table type has been used to construct queries using the Table type and the Table type with Table information from the model.

case class City(id: Long, name: String) derives Table
val cityTable = Table[City]

Because the same type was used to represent two things, it was easy to implement incorrectly.

cityTable.select(city => city.insert(???))

Development tools such as IDEs could cause no small amount of confusion to implementers because they complement all available APIs.

case class City(id: Long, name: String) derives Table
val cityTable = TableQuery[City]

To solve this problem, we created a new type called TableQuery and separated the implementation of query construction.

Changes to Insert

The Insert statement is similarly modified so that columns are composited and inserted columns are specified.

cityTable
- .insertInto(city => (city.id, city.name))
+ .insertInto(city => city.id *: city.name)
  .values((1L, "Tokyo"))

Changes to Update

Previously, the Update Statement had to be set up one by one for each column to be updated. This implementation is convenient when you want to update a few columns individually, but it is very cumbersome to write set for each additional column you want to update.

cityTable
  .update("id", 1L)
  .set("name", "Tokyo")
  .set("population", 1, false)

With this update, columns can now be combined, allowing multiple columns to be specified together for update processing.

cityTable
  .update(city => city.id *: city.name)((1L, "Tokyo"))
  .set(_.population, 1, false)

With this update, columns can now be combined, allowing multiple columns to be specified together for update processing. It is still possible to update only specific columns using set. In addition, since set can be used to set conditions for updating, it is possible to create a query that updates additional columns only when the conditions are positive.

Changes to Join

Previously, table joins were constructed by setting the join condition as the second argument.

cityTable.join(countryTable)((c, co) => c.countryCode === co.code)

From this change, the conditions for joining tables must be set via the on API. This change is the result of an internal implementation change.

cityTable.join(countryTable).on((c, co) => c.countryCode === co.code)

💪 Enhancement

  • Enhancement/2024 09 raises errors in fields that cannot be decoded by @takapi327 in #306
  • Enhancement/2024 10 added support tuple class mapping by @takapi327 in #307
  • Refactor/2024 10 renewal schema by @takapi327 in #329

🪲 Bug Fixes

🔧 Refactoring

📖 Documentation

⛓️ Dependency update

Full Changelog: v0.3.0-beta8...v0.3.0-beta9