Inexplicable "no such table" error from Exqlite

So, I have an SQLite3 DB I am trying to connect to a Phoenix app. It’s the backup file of a popular book cataloguing app and this is the list of its tables:

sqlite> .tables
ZACTIVITY                
ZGLOBAL                                              
Z_METADATA              
ZAUTHOR                 
ZIMAGE                                                 
Z_MODELCACHE            
ZBOOK                     
ZTAG                                                      
Z_PRIMARYKEY            
ZCATEGORY            
Z_2ADDITIONALAUTHORBOOKS
ZCONTACT              
Z_3TAGS    

I am trying to connect to ZBOOK, so I have prepared a MW schema:

defmodule MyApp.Schema do
  defmacro __using__(_) do
    quote do
      use Ecto.Schema
      @primary_key {:Z_PK, :integer, autogenerate: false}
      @foreign_key_type :integer
    end
  end
end

defmodule MyApp.Book do
  use MyApp.Schema

  schema "ZBOOK" do
    field :ZTITLE, :string
  end
end

All of ZBOOK, Z_PK and ZTITLE exist and are typed correctly. Now, when I try spin up the Phoenix server using iex, no errors are thrown, which means that SQLite3 DB’s path is correct. But then, when I try to run (in sequence):

alias MyApp.{Repo, Book}
import Ecto.Query

query = from(Book)
Repo.all(query)

I get the error:

** (Exqlite.Error) no such table: ZBOOK
SELECT Z0."Z_PK", Z0."ZTITLE" FROM "ZBOOK" AS Z0
    (ecto_sql 3.11.1) lib/ecto/adapters/sql.ex:1054: Ecto.Adapters.SQL.raise_sql_call_error/1
    (ecto_sql 3.11.1) lib/ecto/adapters/sql.ex:952: Ecto.Adapters.SQL.execute/6
    (ecto 3.11.2) lib/ecto/repo/queryable.ex:232: Ecto.Repo.Queryable.execute/4
    (ecto 3.11.2) lib/ecto/repo/queryable.ex:19: Ecto.Repo.Queryable.all/3
    iex:6: (file)

To be sure, I ran the query SELECT Z0."Z_PK", Z0."ZTITLE" FROM "ZBOOK" AS Z0 into the SQLite3 shell and it worked without any problem. What am I doing wrong?

This is actually not true, so without more info this likely has to do with your problem. If a sqlite db doesn’t exist at the path you give it (at least in dev, maybe in prod too??) it just creates it. It will also run migrations if you have them. If you .read or cat your schema into one db and happened to have it named wrong in config/dev.exs, then this would be the result. This is a guess, though!

My only other (very doubful) thought was that the all caps had something to do with it, but I did a quick test out of curiosity and it worked fine.

1 Like

That’s not the behaviour I am seeing. This is in my dev.exs:

config :myapp, MyApp.Repo,
  database: "../data/books.db"

if I point the path to “nothing”, it throws me the following error:

[error] Exqlite.Connection (#PID<0.507.0>) failed to connect: ** (Exqlite.Error) erofs

I solved the problem: It was a faulty path expansion.
Everything works as expected now, thank you for the help.

1 Like

Yeah and when it doesn’t have permissions to create that file it crashes, taking down erlang runtime with it, without any logs. Really fun experience :joy: .

1 Like

Yes, I feel out of all “official” adapters, SQLite3 seems to one with the least amount of love, but this seems to be somewhat of a trend in the BEAM community (cf. this feed, for example).

Don’t get me wrong: If I could, I would also do everything I need SQLite3 for with ETS / Mnesia, but the problem comes from 3rd party apps that already use SQLite3 as a DB, for exports or as a backup storage. Eventually, you’ll have to interact with SQLite3, or you use tools like pgloader and try to get the DB into PG before accessing it via Ecto.

I wouldn’t worry too much, it is just a question of time until it will get better.

I never used mnesia, however I would always advocate for sqlite3, simply because you have much better tooling around it and you don’t depend on any specific technology. It is also great compared to a self-hosted postgres (or similar databases) the fact that you don’t have a separate runtime or configuration hurdles, it just works (until it doesn’t :joy: ).

2 Likes

Yes, definitely. SQLite3 is frankly insane, all the benefits of file-based storage (CSV, JSON, etc.) with built-in SQL. The Erlang dependency is brutal, because as soon as you leave the BEAM bubble (in which I am very comfortable, don’t get me wrong), a lot of devs have no idea what you are talking about when you mention Mnesia.

Definitely, even prior to moving to the BEAM, I very often demoed apps with SQLite3 in order to avoid the overhead of running a PG instance. I still do it sometimes, especially if I want to throw together something quick. But of course, if you want to leverage the QoL features of the DB you’re using, you can’t, which is the reason why today I have a PG dev DB running as a docker compose service in almost every project with a backend.

1 Like

Not sure what you call official, but exqlite and sqlite_ecto3 are certainly not developed by the ecto team. It’s also tricky, because a lot of the assumptions made by ecto_sql (what we usually interact with) don’t really map cleanly to sqlite, because there is no server, no connections different constraints around blocking between transactions and so on. You can look at Replace DirtyNIF execution model with a different mechanism · Issue #192 · elixir-sqlite/exqlite · GitHub if you’re interested in more of the details.

Also the thread you linked was around ecto_sqlite2, which ran into similar problems, but had an even harder time because it was powered by an erlang library, which wasn’t even built with ecto in mind. Exqlite does quite a bit better in that regard.

3 Likes

Yeah, that was some unhappy wording on my end, I rather meant that it’s somehow the default adapter driver like postgrex is for PG, e.g., when running mix phx.new with the --database sqlite3 flag it defaults to exqlite.

Yes, I agree, SQLite3 is very different from PG and MySQL in the way it works. I was not criticizing Ecto in any way and maybe it’s easier to plug into SQLite3 directly w/o the Ecto layer, but I don’t know because I haven’t tried it yet.

Good point, I missed the fact that the discussion was about a different driver. I apologize.

Why yes it is “easy” although I highly suggest you don’t do that. There are so many goodies that come with Ecto and it’s changesets. You can indeed just query an sqlite database without ecto. You can utilize Exqlite.Basic if you really want this, but be warned it may be going away.

Was this a bug on the consumer side or was the library expanding the path wrong? If the library was expanding it incorrectly, this may need an issue opened up.

1 Like