-
Notifications
You must be signed in to change notification settings - Fork 180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PgArraySupport: Type Parameter Missing in Generated Code #547
Comments
@enzeart try package com.example.db_profile
import com.github.tminglei.slickpg._
trait CustomPostgresProfile
extends ExPostgresProfile
with PgArraySupport
with PgDate2Support
with PgRangeSupport
with PgHStoreSupport
with PgSearchSupport
with PgNetSupport
with PgLTreeSupport {
// Add back `capabilities.insertOrUpdate` to enable native `upsert` support; for postgres 9.5+
override protected def computeCapabilities: Set[slick.basic.Capability] =
super.computeCapabilities + slick.jdbc.JdbcCapabilities.insertOrUpdate
override val api: API = new MyAPI {}
trait MyAPI
extends super.API
with ArrayImplicits
with DateTimeImplicits
with NetImplicits
with LTreeImplicits
with RangeImplicits
with HStoreImplicits
with SearchImplicits
with SearchAssistants
}
object CustomPostgresProfile extends CustomPostgresProfile p.s. change |
@tminglei this didn't work for me Here's my updated custom profile definition from a test project: package du.mungus.play_grpc_test_5.db_profile
import com.github.tminglei.slickpg._
import slick.jdbc.{GetResult, PositionedResult}
import java.time.{Duration, LocalDate, LocalDateTime, LocalTime, OffsetDateTime, OffsetTime}
import java.util.UUID
import scala.reflect.classTag
trait CustomPostgresProfile
extends ExPostgresProfile
with PgArraySupport
with PgDate2Support
with PgRangeSupport
with PgHStoreSupport
with PgSearchSupport
with PgNetSupport
with PgLTreeSupport {
// Add back `capabilities.insertOrUpdate` to enable native `upsert` support; for postgres 9.5+
override protected def computeCapabilities: Set[slick.basic.Capability] =
super.computeCapabilities + slick.jdbc.JdbcCapabilities.insertOrUpdate
override val api: API = new CustomAPI {}
trait CustomAPI
extends super.API
with ArrayImplicits
with DateTimeImplicits
with NetImplicits
with LTreeImplicits
with RangeImplicits
with HStoreImplicits
with SearchImplicits
with SearchAssistants {
implicit object GetUuid extends GetResult[UUID] {
override def apply(p: PositionedResult): UUID = UUID.fromString(p.nextString())
}
}
}
object CustomPostgresProfile extends CustomPostgresProfile {
bindPgDateTypesToScala(
classTag[LocalDate],
classTag[LocalTime],
classTag[LocalDateTime],
classTag[OffsetTime],
classTag[OffsetDateTime],
classTag[Duration]
)
} Here's the table definition: CREATE TABLE IF NOT EXISTS test (
data TEXT[] NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
); Here's the code generated for the table: package du.mungus.play_grpc_test_5.db
// AUTO-GENERATED Slick data model for table Test
trait TestTable {
self:Tables =>
import profile.api._
import slick.model.ForeignKeyAction
// NOTE: GetResult mappers for plain SQL are only generated for tables where Slick knows how to map the types of all columns.
import slick.jdbc.{GetResult => GR}
/** Entity class storing rows of table Test
* @param data Database column data SqlType(_text)
* @param createdAt Database column created_at SqlType(timestamptz) */
case class TestRow(data: scala.collection.immutable.Seq, createdAt: java.time.OffsetDateTime)
/** GetResult implicit for fetching TestRow objects using plain SQL queries */
implicit def GetResultTestRow(implicit e0: GR[scala.collection.immutable.Seq], e1: GR[java.time.OffsetDateTime]): GR[TestRow] = GR{
prs => import prs._
TestRow.tupled((<<[scala.collection.immutable.Seq], <<[java.time.OffsetDateTime]))
}
/** Table description of table test. Objects of this class serve as prototypes for rows in queries. */
class Test(_tableTag: Tag) extends profile.api.Table[TestRow](_tableTag, "test") {
def * = (data, createdAt) <> (TestRow.tupled, TestRow.unapply)
/** Maps whole row to an option. Useful for outer joins. */
def ? = ((Rep.Some(data), Rep.Some(createdAt))).shaped.<>({r=>import r._; _1.map(_=> TestRow.tupled((_1.get, _2.get)))}, (_:Any) => throw new Exception("Inserting into ? projection not supported."))
/** Database column data SqlType(_text) */
val data: Rep[scala.collection.immutable.Seq] = column[scala.collection.immutable.Seq]("data")
/** Database column created_at SqlType(timestamptz) */
val createdAt: Rep[java.time.OffsetDateTime] = column[java.time.OffsetDateTime]("created_at")
}
/** Collection-like TableQuery object for table Test */
lazy val Test = new TableQuery(tag => new Test(tag))
} The type parameters are still missing from the generated Seq fields for me. |
Same issue with me 552 ^^ |
I got things working by doing the following steps. I created a custom SourceCodeGenerator class. You have to take special care to override the packageCode and packageContainerCode methods to ensure your custom profile is used for the profile fields of the generated Tables trait. One of these methods is used for single-file code generation and the other is used for multi-file code generation. This brings all of the PgArraySupport implicits into scope for the generated code. The mappings from the database array types to the scala collections types are also configured here. import slick.codegen.SourceCodeGenerator
import slick.model.Model
import slick.sql.SqlProfile.ColumnOption
class CustomSourceCodeGenerator(model: Model) extends SourceCodeGenerator(model) {
override def Table: slick.model.Table => TableDef = new Table(_) { table =>
override def Column: slick.model.Column => ColumnDef = new Column(_) { column =>
override def rawType: String = {
model.options
.find(_.isInstanceOf[ColumnOption.SqlType])
.flatMap { tpe =>
tpe.asInstanceOf[ColumnOption.SqlType].typeName match {
case "_text" | "text[]" | "_varchar" | "varchar[]" => Option("List[String]")
case "_int8" | "int8[]" => Option("List[Long]")
case "_int4" | "int4[]" => Option("List[Int]")
case "_int2" | "int2[]" => Option("List[Short]")
case _ => None
}
}
.getOrElse {
model.tpe match {
case _ => super.rawType
}
}
}
}
}
override def packageCode(profile: String, pkg: String, container: String, parentType: Option[String]): String = {
s"""
|package $pkg
|// AUTO-GENERATED Slick data model
|/** Stand-alone Slick data model for immediate use */
|object $container extends {
| val profile = $profile
|} with $container
|
|/** Slick data model trait for extension, choice of backend or usage in the cake pattern. (Make sure to initialize this late.) */
|trait $container${parentType.map(t => s" extends $t").getOrElse("")} {
| val profile: $profile
| import profile.api._
| ${indent(code)}
|}
|""".stripMargin
}
protected def handleQuotedNamed(tableName: String): String = {
if (tableName.endsWith("`")) s"${tableName.init}Table`" else s"${tableName}Table"
}
override def packageContainerCode(profile: String, pkg: String, container: String): String = {
val mixinCode =
codePerTable.keys.map(tableName => s"${handleQuotedNamed(tableName)}").mkString("extends ", " with ", "")
s"""
|package $pkg
|// AUTO-GENERATED Slick data model
|/** Stand-alone Slick data model for immediate use */
|object $container extends {
| val profile = $profile
|} with $container
|
|/** Slick data model trait for extension, choice of backend or usage in the cake pattern. (Make sure to initialize this late.)
| Each generated XXXXTable trait is mixed in this trait hence allowing access to all the TableQuery lazy vals.
| */
|trait $container${parentType.map(t => s" extends $t").getOrElse("")} $mixinCode {
| val profile: $profile
| import profile.api._
| ${indent(codeForContainer)}
|
|}
|""".stripMargin
}
} I changed the type of the overridden api field from API to CustomAPI.type so its implicits don't get hidden by the more generic interface. import com.github.tminglei.slickpg._
import slick.jdbc.{GetResult, PositionedResult}
import java.time.{Duration, LocalDate, LocalDateTime, LocalTime, OffsetDateTime, OffsetTime}
import java.util.UUID
import scala.reflect.classTag
trait CustomPostgresProfile
extends ExPostgresProfile
with PgArraySupport
with PgDate2Support
with PgRangeSupport
with PgHStoreSupport
with PgSearchSupport
with PgNetSupport
with PgLTreeSupport {
// Add back `capabilities.insertOrUpdate` to enable native `upsert` support; for postgres 9.5+
override protected def computeCapabilities: Set[slick.basic.Capability] =
super.computeCapabilities + slick.jdbc.JdbcCapabilities.insertOrUpdate
override val api: CustomAPI.type = CustomAPI
object CustomAPI
extends super.API
with ArrayImplicits
with DateTimeImplicits
with NetImplicits
with LTreeImplicits
with RangeImplicits
with HStoreImplicits
with SearchImplicits
with SearchAssistants {
implicit object GetUuid extends GetResult[UUID] {
override def apply(p: PositionedResult): UUID = UUID.fromString(p.nextString())
}
}
}
object CustomPostgresProfile extends CustomPostgresProfile {
bindPgDateTypesToScala(
classTag[LocalDate],
classTag[LocalTime],
classTag[LocalDateTime],
classTag[OffsetTime],
classTag[OffsetDateTime],
classTag[Duration]
)
} I then use the default SourceCodeGenerator entrypoint to drive the codegen process, but I pass CustomSourceCodeGenerator as the value for the codeGeneratorClass argument. @tminglei is this a sound approach? |
@enzeart sounds good. 👍 You did what I would do when met similar scenes. |
Hello,
When using PgArraySupport in my custom profile, array types in the database result in code being generated that has scala.collection.immutable.Seq types with missing type parameters. This causes the following error during compilation: trait Seq takes type parameters. Am I doing something wrong, or is this a bug? My profile is shown below:
The text was updated successfully, but these errors were encountered: