will / crystal-pg

a postgres driver for crystal
BSD 3-Clause "New" or "Revised" License
462 stars 77 forks source link

Encode Crystal enum args as integers #286

Closed jgaskins closed 1 month ago

jgaskins commented 1 month ago

Decoding enum values is already supported in DB, but encoding still requires explicitly specifying enum.value in query args. This PR uses the value property implicitly.

An argument could be made that it would be easier to understand the query if we use the string representations of the Crystal enums instead, but I'm proposing the numeric values for three reasons:

  1. An int4 values takes up less space than a text
  2. Storing the text representation makes @[Flags] enums painful to parse, but the integer representation makes them trivial
  3. The enum is an Int32

Example:

require "pg"

struct User
  include DB::Serializable

  getter id : UUID
  getter name : String
  getter role : Role
  getter created_at : Time

  @[Flags]
  enum Role
    Admin      = 0x01
    Moderator  = 0x02
    Subscriber = 0x04
    Member     = 0x08
    Guest      = 0x10
  end
end

pg = DB.open("postgres:///")

sql = "SELECT $1::uuid id, $2 name, $3::int4 role, now() created_at"
args = {
  UUID.v7,                        # $1 id
  "jgaskins",                     # $2 name
  User::Role[:admin, :moderator], # $3 role
}
pp pg.query_one sql, *args, as: User
# User(
#  @created_at=2024-07-21 12:10:33.400016000 -05:00 America/Chicago,
#  @id=UUID(0190d646-0cb7-7d60-82f0-18074e861a46),
#  @name="jgaskins",
#  @role=User::Role[Admin, Moderator])
will commented 1 month ago

Thanks!