by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
shrynx
@shrynx
i am having difficulties using doobie when i try to map enumeratum enum to a postgres enum

say i have a scala enum

sealed trait Fruits extends EnumEntry with Uppercase
object Fruits extends Enum[Fruits] {
  val values: immutable.IndexedSeq[Fruits] = findValues
  case object Apple extends Fruits
  case object Banana extends Fruits
  case object Orange extends Fruits
}

and same in postgres

CREATE TYPE fruits AS ENUM ('APPLE', 'BANANA', 'ORANGE');

i want to be able to write queries like

sql"SELECT * FROM fruit_prices where fruit = $Fruits.Apple"
shrynx
@shrynx
was hoping this to work but no luck
  implicit val fruitsMapper: Meta[Fruits] =
    pgEnumString[Fruits]("fruits", Fruits.withName, _.entryName)
Rob Norris
@tpolecat
you could need to say ${Fruits.Apple} with braces, and figure out how to manage the case difference
what doesn't work?
shrynx
@shrynx

that doesn't work either

i think i understand the issue

def foo = {
  val fruit = Fruits.Apple
  sql"SELECT * FROM fruit_prices where fruit = $fruit"
}

here

  val fruit: Fruit.Apple.type = Fruits.Apple

and not

  val fruit: Fruits = Fruits.Apple

thus the mapper isn't working 😞

Rob Norris
@tpolecat
How doesn't it work?
What is the error message?
shrynx
@shrynx
Cannot construct a parameter vector of the following type:

  com.foo.models.Fruits.Apple.type :: shapeless.HNil

Because one or more types therein (disregarding HNil) does not have a Put
instance in scope. Try them one by one in the REPL or in your code:

  scala> Put[Foo]

and find the one that has no instance, then construct one as needed. Refer to
Chapter 12 of the book of doobie for more information.
]
Rob Norris
@tpolecat
Ah, you need to widen it to Fruits.
val fruit: Fruit = ...
or ${fruit: Fruits}
So it will work if you have fruit: Fruit but if you have something more precise you will need to widen it. This is a common thing in Scala.
shrynx
@shrynx

yes that does work. Thank you !
i was able to figure out but i am still struggling with something like this

  val addPrices: Update[FruitPrice] = {
    val fruit: Fruits = Fruits.Apple
    val query = s"""
                INSERT INTO fruit_prices (price, vendor, fruit)
                VALUES (?, ?,  $fruit)
              """
    Update[FruitPrice](query)
  }

and this gives error

INSERT INTO fruit_prices (price, vendor, fruit)
                VALUES ('20.5', 'Some Vendor',   Apple)  was aborted: ERROR: column "Apple" does not exist

because of how the mapping uses entryName and the enum has UpperCase trait, the resultant query should be
with quote and capitalised

INSERT INTO fruit_prices (price, vendor, fruit)
                VALUES ('20.5', 'Some Vendor', 'APPLE')

when i hard code the string 'APPLE', it does work

shrynx
@shrynx
ahh never mind this is just string interpolation
thanks @tpolecat
Jente Hidskes
@Hjdskes

Hello folks! I'm running into an issue I don't understand, maybe you can show me what I am missing.

I have a case class with a number of fields, let's say case class Foo(a: A, b: B, c: C), where each field has a Read instance. Yet doobie can not infer a Read[Foo] instance. I have verified in a scratchpad that indeed Read[A], Read[B] and Read[C] instances exist. The peculiar thing is that if I comment out a: A in Foo, a Read[Foo] instance can be inferred. However, as I said, a Read[A] instance does exist! What am I missing?

Rob Norris
@tpolecat
@ trait A; trait B; trait C 
defined trait A
defined trait B
defined trait C

@ implicit def ra: Read[A] = ??? 
defined function ra

@ implicit def rb: Read[B] = ??? 
defined function rb

@ implicit def rc: Read[C] = ??? 
defined function rc

@ case class Foo(a: A, b: B, c: C) 
defined class Foo

@ def rf = Read[Foo] 
defined function rf
So that should work. Can you reproduce the issue minimally?
Paulius Imbrasas
@CremboC
is there any way to avoid the parameterization? So I want to just literally interpolate a variable into a sql statement?
Rob Norris
@tpolecat
sql"blah ${Fragment.const("foo")} fnord"
But obviously be careful, never do that with user-provided data.
Paulius Imbrasas
@CremboC
of course, thank you!
not really related, but are separate INSERTS in a single transaction are done in parallel in Postgres?
Rob Norris
@tpolecat
No, sessions are fundamentally sequential.
Paulius Imbrasas
@CremboC
thanks :thumbsup:
Sushovan
@sushovannits
Hi very new to entire scala. I am trying to figure out how do I do a select query and get a class object which has an extra field which is not present in db. Any help please
I have been going through the column vector mapping but not sure.
nigredo-tori
@nigredo-tori

@sushovannits, the easiest way would be to do something like

final case class MyClass(
  column1: Column1,
  ...
  columnN: ColumnN,
  extraField: ExtraField
)
val extraFieldValue: ExtraField = ???

val query = fr"SELECT column1, column2, ..., columnN"
  .query[(Column1, Column2, ..., ColumnN)]
  .map { case (c1, c2, ... cN) =>
    MyClass(c1, c2, ..., cN, extraFieldValue)
  }

That is, you read all the columns that are in the database, and manually convert the rows to your type. If you have to do this more than once, you might want a custom Read instance, but that's optional.

Sushovan
@sushovannits

@nigredo-tori thank you so much for replying. Yes I tried this but I do have to do for multiple times (for insert/update/select). I was trying to implement the Read instance like this:

implicit val pointRead: Read[Point] =
  Read[(Int, Int)].map { case (x, y) => new Point(x, y, x+y) }
// pointRead: Read[Point] = doobie.util.Read@e67456a

But I am a bit confused how to use the Read instance.
If Point is like

case class Point(x: Int, y: Int, extraNumberNotInDb : Int)

and I a do a query as:

sql"select x, y from point_table limit 1".Query[Point]..."

It still gives an error of The column index is out of range: 3, number of columns: 2.

Sushovan
@sushovannits
My bad :cry: the Read instance was not visible. Thanks for your help.
Jente Hidskes
@Hjdskes

@tpolecat Your scatchpad works just fine, as expected. I'm starting to believe that my issue has to do with my field a: A being a newtype over a refined NonEmptyFiniteString with an automatically generated Meta instance using newtype's Coercible, i.e., something like this:

import doobie.util.Read
import doobie.util.meta.Meta
import eu.timepit.refined.types.string.NonEmptyFiniteString
import io.estatico.newtype.Coercible

implicit def coercibleMeta[R, N](implicit ev: Coercible[Meta[R], Meta[N]], r: Meta[R]): Meta[N] = ev(r)

@newtype case class A(value: NonEmptyFiniteString[100])

trait B
trait C

implicit def ra: Read[A] = ???
implicit def rb: Read[B] = ???
implicit def rc: Read[C] = ???

case class Foo(a: A, b: B, c: C)

def rf = Read[Foo]

Although this also works. And as I said, in the actual code Read[A] does resolve, it's only Read[Foo] that does not when it has the field a: A...

sinanspd
@sinanspd
that's weird
can you try defining a coercibleRead and coerciblePut ?
Jente Hidskes
@Hjdskes
@sinanspd Sure :) Did you maybe mean coercibleRead and coercibleWrite, or CoercibleGet and coerciblePut?
sinanspd
@sinanspd
Actually I originally meant Read and Put. I don't know how common it is to have that combination but it works with newType because the requirements for your "read" and "write" are different. However I am just realizing you have a refined type inside a newtype (my bad, 3:30 am here ...), so I am not sure what the requirements will be.
is doobie-refined still a thing ? if so, does using that help? (also I missed the original conversation so, apologies if these were already covered)
Jente Hidskes
@Hjdskes
No worries @sinanspd, I appreciate you thinking with me! doobie-refined is still a thing (at least, AFAIK) and we're using that. Could you elaborate on "because the requirements for your "read" and "write" are different.", I'm not sure I understand what you're trying to say here.
sinanspd
@sinanspd

I'm not sure I understand what you're trying to say here.

Since when you are persisting this, I am assuming you will be unpacking the object. i.e. "insert (....) values (foo.a, foo.b) If that is the case, you only need to tell doobie how to persist that one column that is newtype (as opposed to the whole Write for the entire Foo), but when you are reading it, you are reading the entire Foo so you need a wider instruction such as Read

I personally like this, so I can have more focused instructions for updates but everyone has different preferences
Jente Hidskes
@Hjdskes
Aah yes, that makes sense
sinanspd
@sinanspd
So I get away with
 implicit def coerciblePut[N: Coercible[R, *], R: Put]: Put[N] = Put[R].contramap[N](_.repr.asInstanceOf[R])
 implicit def coercibleRead[N: Coercible[R, *], R: Read]: Read[N] = Read[R].map(_.asInstanceOf[N])
sinanspd
@sinanspd
However, to be frank, I usually use the refined types at the api level and they are usually loosened by the time I persist them, so I don't know how this is react to a refined type. I think you will still need to have a Put & Read in scope telling doobie what to do. Unfortunately, defining those generically for a refined type will look slightly uglier, possibly something along the lines Meta[T].xmap(refType.refine[P](_) match { ...}) (maybe ??) so I am hoping doobie-refined could provide those instances for you.
that being said, I tend to give bad advice at 4 in the morning, so I will shut up. Hopefully there was something at least somewhat useful here... good luck!
Jente Hidskes
@Hjdskes

Alright, something has resolved this. Perhaps a clean cache or something like that. However, now I'm getting

Error:(21, 4) could not find implicit value for parameter ev: doobie.util.Get[Option[java.time.LocalDate]]
Get[Option[LocalDate]]

I have import doobie.implicits.javatime._ imported, however, and this has worked fine before. This case is getting more and more mysterious...

Jente Hidskes
@Hjdskes
import java.time.LocalDate

import doobie.util.{Get, Read}
import doobie.implicits.javatime._

Read[LocalDate]
Get[LocalDate]
Read[Option[LocalDate]]
Get[Option[LocalDate]]
This breaks on the last line
Sushovan
@sushovannits

I need some more help to get my project going. I am following this:
tpolecat/doobie#718
I am trying to use something like:

sql"INSERT INTO demo (id, text) VALUES (1, 'hallo') RETURNING *".query[Demo].unique

But I keep getting the error "No results were returned by the query."
Please help.

Sushovan
@sushovannits

Basically I want to do:

sql"INSERT INTO demo (id, text) VALUES (1, 'hallo') RETURNING *, inc(id)".query[Demo].unique

as Demo has an extra property which is computed from id

Rob Norris
@tpolecat
Does it return rows when you run it in psql?
Sushovan
@sushovannits
Yes it does return rows when I run it in psql.