Skip to content

Commit

Permalink
Generate ForeignKey inside Fields for easier joins (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
oyvindberg authored May 24, 2024
1 parent 179f527 commit e1f4923
Show file tree
Hide file tree
Showing 157 changed files with 1,681 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ package person

import testdb.hardcoded.myschema.Number
import testdb.hardcoded.myschema.Sector
import testdb.hardcoded.myschema.football_club.FootballClubFields
import testdb.hardcoded.myschema.football_club.FootballClubId
import testdb.hardcoded.myschema.football_club.FootballClubRow
import testdb.hardcoded.myschema.marital_status.MaritalStatusFields
import testdb.hardcoded.myschema.marital_status.MaritalStatusId
import testdb.hardcoded.myschema.marital_status.MaritalStatusRow
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -32,6 +37,12 @@ trait PersonFields {
def workEmail: OptField[/* max 254 chars */ String, PersonRow]
def sector: Field[Sector, PersonRow]
def favoriteNumber: Field[Number, PersonRow]
def fkFootballClub: ForeignKey[FootballClubFields, FootballClubRow] =
ForeignKey[FootballClubFields, FootballClubRow]("myschema.person_favourite_football_club_id_fkey", Nil)
.withColumnPair(favouriteFootballClubId, _.id)
def fkMaritalStatus: ForeignKey[MaritalStatusFields, MaritalStatusRow] =
ForeignKey[MaritalStatusFields, MaritalStatusRow]("myschema.person_marital_status_id_fkey", Nil)
.withColumnPair(maritalStatusId, _.id)
}

object PersonFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ package person

import testdb.hardcoded.myschema.Number
import testdb.hardcoded.myschema.Sector
import testdb.hardcoded.myschema.football_club.FootballClubFields
import testdb.hardcoded.myschema.football_club.FootballClubId
import testdb.hardcoded.myschema.football_club.FootballClubRow
import testdb.hardcoded.myschema.marital_status.MaritalStatusFields
import testdb.hardcoded.myschema.marital_status.MaritalStatusId
import testdb.hardcoded.myschema.marital_status.MaritalStatusRow
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -32,6 +37,12 @@ trait PersonFields {
def workEmail: OptField[/* max 254 chars */ String, PersonRow]
def sector: Field[Sector, PersonRow]
def favoriteNumber: Field[Number, PersonRow]
def fkFootballClub: ForeignKey[FootballClubFields, FootballClubRow] =
ForeignKey[FootballClubFields, FootballClubRow]("myschema.person_favourite_football_club_id_fkey", Nil)
.withColumnPair(favouriteFootballClubId, _.id)
def fkMaritalStatus: ForeignKey[MaritalStatusFields, MaritalStatusRow] =
ForeignKey[MaritalStatusFields, MaritalStatusRow]("myschema.person_marital_status_id_fkey", Nil)
.withColumnPair(maritalStatusId, _.id)
}

object PersonFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ package person

import testdb.hardcoded.myschema.Number
import testdb.hardcoded.myschema.Sector
import testdb.hardcoded.myschema.football_club.FootballClubFields
import testdb.hardcoded.myschema.football_club.FootballClubId
import testdb.hardcoded.myschema.football_club.FootballClubRow
import testdb.hardcoded.myschema.marital_status.MaritalStatusFields
import testdb.hardcoded.myschema.marital_status.MaritalStatusId
import testdb.hardcoded.myschema.marital_status.MaritalStatusRow
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -32,6 +37,12 @@ trait PersonFields {
def workEmail: OptField[/* max 254 chars */ String, PersonRow]
def sector: Field[Sector, PersonRow]
def favoriteNumber: Field[Number, PersonRow]
def fkFootballClub: ForeignKey[FootballClubFields, FootballClubRow] =
ForeignKey[FootballClubFields, FootballClubRow]("myschema.person_favourite_football_club_id_fkey", Nil)
.withColumnPair(favouriteFootballClubId, _.id)
def fkMaritalStatus: ForeignKey[MaritalStatusFields, MaritalStatusRow] =
ForeignKey[MaritalStatusFields, MaritalStatusRow]("myschema.person_marital_status_id_fkey", Nil)
.withColumnPair(maritalStatusId, _.id)
}

object PersonFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ package person

import testdb.hardcoded.myschema.Number
import testdb.hardcoded.myschema.Sector
import testdb.hardcoded.myschema.football_club.FootballClubFields
import testdb.hardcoded.myschema.football_club.FootballClubId
import testdb.hardcoded.myschema.football_club.FootballClubRow
import testdb.hardcoded.myschema.marital_status.MaritalStatusFields
import testdb.hardcoded.myschema.marital_status.MaritalStatusId
import testdb.hardcoded.myschema.marital_status.MaritalStatusRow
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -32,6 +37,12 @@ trait PersonFields {
def workEmail: OptField[/* max 254 chars */ String, PersonRow]
def sector: Field[Sector, PersonRow]
def favoriteNumber: Field[Number, PersonRow]
def fkFootballClub: ForeignKey[FootballClubFields, FootballClubRow] =
ForeignKey[FootballClubFields, FootballClubRow]("myschema.person_favourite_football_club_id_fkey", Nil)
.withColumnPair(favouriteFootballClubId, _.id)
def fkMaritalStatus: ForeignKey[MaritalStatusFields, MaritalStatusRow] =
ForeignKey[MaritalStatusFields, MaritalStatusRow]("myschema.person_marital_status_id_fkey", Nil)
.withColumnPair(maritalStatusId, _.id)
}

object PersonFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ package person

import testdb.hardcoded.myschema.Number
import testdb.hardcoded.myschema.Sector
import testdb.hardcoded.myschema.football_club.FootballClubFields
import testdb.hardcoded.myschema.football_club.FootballClubId
import testdb.hardcoded.myschema.football_club.FootballClubRow
import testdb.hardcoded.myschema.marital_status.MaritalStatusFields
import testdb.hardcoded.myschema.marital_status.MaritalStatusId
import testdb.hardcoded.myschema.marital_status.MaritalStatusRow
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -32,6 +37,12 @@ trait PersonFields {
def workEmail: OptField[/* max 254 chars */ String, PersonRow]
def sector: Field[Sector, PersonRow]
def favoriteNumber: Field[Number, PersonRow]
def fkFootballClub: ForeignKey[FootballClubFields, FootballClubRow] =
ForeignKey[FootballClubFields, FootballClubRow]("myschema.person_favourite_football_club_id_fkey", Nil)
.withColumnPair(favouriteFootballClubId, _.id)
def fkMaritalStatus: ForeignKey[MaritalStatusFields, MaritalStatusRow] =
ForeignKey[MaritalStatusFields, MaritalStatusRow]("myschema.person_marital_status_id_fkey", Nil)
.withColumnPair(maritalStatusId, _.id)
}

object PersonFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ package person

import testdb.hardcoded.myschema.Number
import testdb.hardcoded.myschema.Sector
import testdb.hardcoded.myschema.football_club.FootballClubFields
import testdb.hardcoded.myschema.football_club.FootballClubId
import testdb.hardcoded.myschema.football_club.FootballClubRow
import testdb.hardcoded.myschema.marital_status.MaritalStatusFields
import testdb.hardcoded.myschema.marital_status.MaritalStatusId
import testdb.hardcoded.myschema.marital_status.MaritalStatusRow
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -32,6 +37,12 @@ trait PersonFields {
def workEmail: OptField[/* max 254 chars */ String, PersonRow]
def sector: Field[Sector, PersonRow]
def favoriteNumber: Field[Number, PersonRow]
def fkFootballClub: ForeignKey[FootballClubFields, FootballClubRow] =
ForeignKey[FootballClubFields, FootballClubRow]("myschema.person_favourite_football_club_id_fkey", Nil)
.withColumnPair(favouriteFootballClubId, _.id)
def fkMaritalStatus: ForeignKey[MaritalStatusFields, MaritalStatusRow] =
ForeignKey[MaritalStatusFields, MaritalStatusRow]("myschema.person_marital_status_id_fkey", Nil)
.withColumnPair(maritalStatusId, _.id)
}

object PersonFields {
Expand Down
16 changes: 16 additions & 0 deletions typo-dsl-anorm/src/scala/typo/dsl/SelectBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,27 @@ trait SelectBuilder[Fields, Row] {
/** Return sql for debugging. [[None]] if backed by a mock repository */
def sql: Option[Fragment]

final def joinFk[Fields2, Row2](f: Fields => ForeignKey[Fields2, Row2])(other: SelectBuilder[Fields2, Row2]): SelectBuilder[Joined[Fields, Fields2], (Row, Row2)] =
joinOn[Fields2, Option, Row2](other) { case (thisFields, thatFields) =>
val fk: ForeignKey[Fields2, Row2] = f(thisFields)

fk.columnPairs
.map { case columnPair: ForeignKey.ColumnPair[t, Fields2] =>
implicit val ord: Ordering[t] = columnPair.ordering
val left: SqlExpr[t, Option] = columnPair.thisField
val right: SqlExpr[t, Option] = columnPair.thatField(thatFields)
left === right
}
.reduce(_.and(_))
}

/** start constructing a join */
final def join[F2, Row2](other: SelectBuilder[F2, Row2]): PartialJoin[F2, Row2] =
new PartialJoin[F2, Row2](other)

final class PartialJoin[Fields2, Row2](other: SelectBuilder[Fields2, Row2]) {
def onFk(f: Fields => ForeignKey[Fields2, Row2]): SelectBuilder[Joined[Fields, Fields2], (Row, Row2)] =
joinFk(f)(other)

/** inner join with the given predicate
* {{{
Expand Down
16 changes: 16 additions & 0 deletions typo-dsl-doobie/src/scala/typo/dsl/SelectBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,27 @@ trait SelectBuilder[Fields, Row] {
/** Return sql for debugging. [[None]] if backed by a mock repository */
def sql: Option[Fragment]

final def joinFk[Fields2, Row2](f: Fields => ForeignKey[Fields2, Row2])(other: SelectBuilder[Fields2, Row2]): SelectBuilder[Joined[Fields, Fields2], (Row, Row2)] =
joinOn[Fields2, Option, Row2](other) { case (thisFields, thatFields) =>
val fk: ForeignKey[Fields2, Row2] = f(thisFields)

fk.columnPairs
.map { case columnPair: ForeignKey.ColumnPair[t, Fields2] =>
implicit val ord: Ordering[t] = columnPair.ordering
val left: SqlExpr[t, Option] = columnPair.thisField
val right: SqlExpr[t, Option] = columnPair.thatField(thatFields)
left === right
}
.reduce(_.and(_))
}

/** start constructing a join */
final def join[F2, Row2](other: SelectBuilder[F2, Row2]): PartialJoin[F2, Row2] =
new PartialJoin[F2, Row2](other)

final class PartialJoin[Fields2, Row2](other: SelectBuilder[Fields2, Row2]) {
def onFk(f: Fields => ForeignKey[Fields2, Row2]): SelectBuilder[Joined[Fields, Fields2], (Row, Row2)] =
joinFk(f)(other)

/** inner join with the given predicate
* {{{
Expand Down
2 changes: 1 addition & 1 deletion typo-dsl-doobie/src/scala/typo/dsl/SqlExpr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ object SqlExpr {

sealed trait FieldLikeNotIdNoHkt[NT, R] extends FieldLikeNoHkt[NT, R]

sealed trait FieldLike[T, N[_], R] extends SqlExpr[T, N] with FieldLikeNoHkt[N[T], R] with Product {
sealed trait FieldLike[T, N[_], R] extends SqlExpr[T, N] with FieldLikeNoHkt[N[T], R] {
val path: List[Path]
val name: String
val sqlReadCast: Option[String]
Expand Down
24 changes: 24 additions & 0 deletions typo-dsl-shared/typo/dsl/ForeignKey.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package typo.dsl

case class ForeignKey[Fields2, Row2](
name: String,
columnPairs: List[ForeignKey.ColumnPair[?, Fields2]]
) {

def withColumnPair[T, ThisN[_]: Nullability, ThatN[_]: Nullability](
field: SqlExpr.FieldLike[T, ThisN, ?],
thatField: Fields2 => SqlExpr.FieldLike[T, ThatN, Row2]
)(implicit O: Ordering[T]): ForeignKey[Fields2, Row2] =
new ForeignKey[Fields2, Row2](
name,
columnPairs :+ ForeignKey.ColumnPair[T, Fields2](field.?, f => thatField(f).?, O)
)
}

object ForeignKey {
case class ColumnPair[T, Fields2](
thisField: SqlExpr[T, Option],
thatField: Fields2 => SqlExpr[T, Option],
ordering: Ordering[T]
)
}
16 changes: 16 additions & 0 deletions typo-dsl-zio-jdbc/src/scala/typo/dsl/SelectBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,27 @@ trait SelectBuilder[Fields, Row] {
/** Return sql for debugging. [[None]] if backed by a mock repository */
def sql: Option[SqlFragment]

final def joinFk[Fields2, Row2](f: Fields => ForeignKey[Fields2, Row2])(other: SelectBuilder[Fields2, Row2]): SelectBuilder[Joined[Fields, Fields2], (Row, Row2)] =
joinOn[Fields2, Option, Row2](other) { case (thisFields, thatFields) =>
val fk: ForeignKey[Fields2, Row2] = f(thisFields)

fk.columnPairs
.map { case columnPair: ForeignKey.ColumnPair[t, Fields2] =>
implicit val ord: Ordering[t] = columnPair.ordering
val left: SqlExpr[t, Option] = columnPair.thisField
val right: SqlExpr[t, Option] = columnPair.thatField(thatFields)
left === right
}
.reduce(_.and(_))
}

/** start constructing a join */
final def join[F2, Row2](other: SelectBuilder[F2, Row2]): PartialJoin[F2, Row2] =
new PartialJoin[F2, Row2](other)

final class PartialJoin[Fields2, Row2](other: SelectBuilder[Fields2, Row2]) {
def onFk(f: Fields => ForeignKey[Fields2, Row2]): SelectBuilder[Joined[Fields, Fields2], (Row, Row2)] =
joinFk(f)(other)

/** inner join with the given predicate
* {{{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import adventureworks.customtypes.TypoLocalDateTime
import adventureworks.customtypes.TypoShort
import adventureworks.customtypes.TypoUUID
import adventureworks.person.businessentity.BusinessentityId
import adventureworks.person.person.PersonFields
import adventureworks.person.person.PersonRow
import adventureworks.public.Flag
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -36,6 +39,9 @@ trait EmployeeFields {
def rowguid: Field[TypoUUID, EmployeeRow]
def modifieddate: Field[TypoLocalDateTime, EmployeeRow]
def organizationnode: OptField[String, EmployeeRow]
def fkPersonPerson: ForeignKey[PersonFields, PersonRow] =
ForeignKey[PersonFields, PersonRow]("humanresources.FK_Employee_Person_BusinessEntityID", Nil)
.withColumnPair(businessentityid, _.businessentityid)
}

object EmployeeFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ package employeedepartmenthistory

import adventureworks.customtypes.TypoLocalDate
import adventureworks.customtypes.TypoLocalDateTime
import adventureworks.humanresources.department.DepartmentFields
import adventureworks.humanresources.department.DepartmentId
import adventureworks.humanresources.department.DepartmentRow
import adventureworks.humanresources.employee.EmployeeFields
import adventureworks.humanresources.employee.EmployeeRow
import adventureworks.humanresources.shift.ShiftFields
import adventureworks.humanresources.shift.ShiftId
import adventureworks.humanresources.shift.ShiftRow
import adventureworks.person.businessentity.BusinessentityId
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -26,6 +33,15 @@ trait EmployeedepartmenthistoryFields {
def startdate: IdField[TypoLocalDate, EmployeedepartmenthistoryRow]
def enddate: OptField[TypoLocalDate, EmployeedepartmenthistoryRow]
def modifieddate: Field[TypoLocalDateTime, EmployeedepartmenthistoryRow]
def fkDepartment: ForeignKey[DepartmentFields, DepartmentRow] =
ForeignKey[DepartmentFields, DepartmentRow]("humanresources.FK_EmployeeDepartmentHistory_Department_DepartmentID", Nil)
.withColumnPair(departmentid, _.departmentid)
def fkEmployee: ForeignKey[EmployeeFields, EmployeeRow] =
ForeignKey[EmployeeFields, EmployeeRow]("humanresources.FK_EmployeeDepartmentHistory_Employee_BusinessEntityID", Nil)
.withColumnPair(businessentityid, _.businessentityid)
def fkShift: ForeignKey[ShiftFields, ShiftRow] =
ForeignKey[ShiftFields, ShiftRow]("humanresources.FK_EmployeeDepartmentHistory_Shift_ShiftID", Nil)
.withColumnPair(shiftid, _.shiftid)
}

object EmployeedepartmenthistoryFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ package employeepayhistory

import adventureworks.customtypes.TypoLocalDateTime
import adventureworks.customtypes.TypoShort
import adventureworks.humanresources.employee.EmployeeFields
import adventureworks.humanresources.employee.EmployeeRow
import adventureworks.person.businessentity.BusinessentityId
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -22,6 +25,9 @@ trait EmployeepayhistoryFields {
def rate: Field[BigDecimal, EmployeepayhistoryRow]
def payfrequency: Field[TypoShort, EmployeepayhistoryRow]
def modifieddate: Field[TypoLocalDateTime, EmployeepayhistoryRow]
def fkEmployee: ForeignKey[EmployeeFields, EmployeeRow] =
ForeignKey[EmployeeFields, EmployeeRow]("humanresources.FK_EmployeePayHistory_Employee_BusinessEntityID", Nil)
.withColumnPair(businessentityid, _.businessentityid)
}

object EmployeepayhistoryFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ package jobcandidate

import adventureworks.customtypes.TypoLocalDateTime
import adventureworks.customtypes.TypoXml
import adventureworks.humanresources.employee.EmployeeFields
import adventureworks.humanresources.employee.EmployeeRow
import adventureworks.person.businessentity.BusinessentityId
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -22,6 +25,9 @@ trait JobcandidateFields {
def businessentityid: OptField[BusinessentityId, JobcandidateRow]
def resume: OptField[TypoXml, JobcandidateRow]
def modifieddate: Field[TypoLocalDateTime, JobcandidateRow]
def fkEmployee: ForeignKey[EmployeeFields, EmployeeRow] =
ForeignKey[EmployeeFields, EmployeeRow]("humanresources.FK_JobCandidate_Employee_BusinessEntityID", Nil)
.withColumnPair(businessentityid, _.businessentityid)
}

object JobcandidateFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ package address
import adventureworks.customtypes.TypoBytea
import adventureworks.customtypes.TypoLocalDateTime
import adventureworks.customtypes.TypoUUID
import adventureworks.person.stateprovince.StateprovinceFields
import adventureworks.person.stateprovince.StateprovinceId
import adventureworks.person.stateprovince.StateprovinceRow
import typo.dsl.ForeignKey
import typo.dsl.Path
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.FieldLikeNoHkt
Expand All @@ -28,6 +31,9 @@ trait AddressFields {
def spatiallocation: OptField[TypoBytea, AddressRow]
def rowguid: Field[TypoUUID, AddressRow]
def modifieddate: Field[TypoLocalDateTime, AddressRow]
def fkStateprovince: ForeignKey[StateprovinceFields, StateprovinceRow] =
ForeignKey[StateprovinceFields, StateprovinceRow]("person.FK_Address_StateProvince_StateProvinceID", Nil)
.withColumnPair(stateprovinceid, _.stateprovinceid)
}

object AddressFields {
Expand Down
Loading

0 comments on commit e1f4923

Please sign in to comment.