Skip to content

blob encode uuids if stored as binary - #173

Draft
sbaildon wants to merge 1 commit into
elixir-sqlite:mainfrom
sbaildon:main
Draft

blob encode uuids if stored as binary#173
sbaildon wants to merge 1 commit into
elixir-sqlite:mainfrom
sbaildon:main

Conversation

@sbaildon

@sbaildonsbaildon commented Jul 3, 2025

Copy link
Copy Markdown
Contributor

Marking as a draft because I actually don't know if this is the correct solution

I'm using https://github.com/sloanelybutsurely/typeid-elixir which implements an Ecto.ParameterizedType that can be either a :string or :uuid type. As strings they look like "user_01jz8mgbxkevwagbhr8cdqhpqh"

I want to use ecto_sqlite3 with uuid_type: :binary for efficiency reasons

Storing the TypeID as :uuid will drop the prefix, leaving only the suffix "01jz8mgbxkevwagbhr8cdqhpqh", then convert that suffix from base32 to binary uuidv7 — but Ecto blows up in STRICT tables with BLOB columns because typeid_elixir is unaware exqlite needs to receive {:blob, value}, not only value (binary uuidv7).

** (Exqlite.Error) cannot store TEXT value in BLOB column users.id

I've added blob encoding to the dumper pipeline, but again, maybe this is not the correct solution.

@warmwaffles

Copy link
Copy Markdown
Member

Would you be able to provide a stripped down example of an app where this is occurring so I can fiddle with it?

@sbaildon

Copy link
Copy Markdown
ContributorAuthor

Of course

# main.exsMix.install([{:typeid_elixir,"~> 1.1"},{:ecto,"~> 3.13"},{:ecto_sql,"~> 3.13"},{:ecto_sqlite3,"~> 0.21.0"}])Enum.each([uuid_type: :binary,journal_mode: :wal,],fn{k,v}->Application.put_env(:ecto_sqlite3,k,v)end)Application.put_env(:myapp,Repo,migration_primary_key: [name: :id,type: :uuid,null: false],database: ":memory:",pool_size: 1)Application.put_env(:typeid_elixir,:default_type,:uuid)defmoduleMigration0douseEcto.Migrationdefchangedocreatetable("entities",options: "STRICT")doadd(:field,:string)endendenddefmoduleRepodouseEcto.Repo,otp_app: :myapp,adapter: Ecto.Adapters.SQLite3enddefmoduleEntitydouseEcto.Schema@primary_key{:id,TypeID,prefix: "entity",autogenerate: true}schema"entities"dofield(:field,:string)endenddefmoduleMaindoimportEcto.Query,warn: falsedefmaindoRepo.__adapter__().storage_down(Repo.config()):ok=Repo.__adapter__().storage_up(Repo.config()){:ok,_}=Repo.start_link([])Ecto.Migrator.run(Repo,[{0,Migration0}],:up,all: true,log_migrations_sql: :info)Repo.insert!(%Entity{field: "Post 1"})Repo.all(from(einEntity))|>dbg()endendMain.main()
$ elixir main.exs
17:30:31.238 [info] == Running 0 Migration0.change/0 forward
17:30:31.240 [info] create table entities
17:30:31.241 [info] QUERY OK db=0.1ms
CREATE TABLE "entities" ("id" BLOB NOT NULL PRIMARY KEY, "field" TEXT) STRICT []
17:30:31.241 [info] == Migrated 0 in 0.0s
17:30:31.260 [debug] QUERY ERROR source="entities" db=0.0ms idle=6.4ms
INSERT INTO "entities" ("field","id") VALUES (?1,?2) ["Post 1", #TypeID<"entity_01jz96kc2nfzaanentmz3tsybr">]** (Exqlite.Error) cannot store TEXT value in BLOB column entities.id
INSERT INTO "entities" ("field","id") VALUES (?1,?2)
(ecto_sql 3.13.2) lib/ecto/adapters/sql.ex:1098: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto 3.13.2) lib/ecto/repo/schema.ex:1000: Ecto.Repo.Schema.apply/4
(ecto 3.13.2) lib/ecto/repo/schema.ex:500: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
(ecto 3.13.2) lib/ecto/repo/schema.ex:381: Ecto.Repo.Schema.insert!/4
main.exs:59: Main.main/0
main.exs:68: (file)

@sbaildon

sbaildon commented Jul 3, 2025

Copy link
Copy Markdown
ContributorAuthor

If this line from typeid_elixir were to return {:ok, {:blob, TypeID.uuid_bytes(tid)}} everything works okay in the context of ecto_sqlite3. The binary needs to be wrapped in a {:blob, binary} tuple somewhere, but I'm not sure where

@warmwaffles

Copy link
Copy Markdown
Member

Thank you @sbaildon I'll take a look at this soon

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@sbaildon@warmwaffles