First Contact with SQLite
brandur.org
brandur.org
I find these 'different therefore wrong' takes to be immature.
Yes, SQLite is idiosyncratic in comparison to other relational database engines. There are reasons behind those idiosyncrasies: SQLite is designed for other use cases than those other engines, and therefore has other design decisions.
Ultimately, all computer programs are solutions to problems, and the approach to solving a problem depends on the nature of the problem. A list of grievances and a Boolean judgment is useless without stating the problem that the author is trying to solve.
The reason it remains unaltered by default is because one of the goals (and accomplishments) of SQLite is that the on-disk-data-file is completely backwards compatible, and cross-platform. This is a very important feature in some situations, and not lightly tossed aside because some old default or system is not in vogue anymore.
The latter is the luxury of end-user software unburdened by decades of legacy compatibility obligations.
CREATE TABLE mytable (
id INTEGER PRIMARY KEY,
created DATETIME,
mything JSON
) STRICT;Like violin, I still play my $50 special I bought in 2005, because it sounds fine and I'm terrible so it's Good Enough.
On the other hand, I am building my second computer keyboard that's going to cost me $300+ because, well, I make a living with these things and I use it 60+ hours a week. I have wrist problems, so a better keyboard literally translates into more hours billed.
My favourite feature of my sqlite-utils CLI tool is the "transform" command which implements this pattern for you (also available as a Python library method): https://sqlite-utils.datasette.io/en/stable/cli.html#transfo...
That's why it provides a --sql option - if it doesn't entirely handle your particular case you can instead get it to generate SQL for you without executing it, so you can make modifications you need before running it.
This seems very blunty anti-sqlite, and things postgreSQL is the best, so I'd be interested to see a guide for (two things I've used sqlite for in the last week):
* Using postgreSQL to store data in an iPhone app
* Making a small python script which uses PostgreSQL, and then seeing how much work it is to send that to someone else, so they can use your work and extend it (send the database, and also instructions for installing postreSQL, and getting everything set up. Make sure it works on linux, mac and windows).
Looking into his "about" section he worked mostly with web APIs (Stripe, Heroku) and is a self-proclaimed fan of Postgres. Maybe when he acquires some experience working with embedded applications without boatloads of resources available nor a stable network stack he might appreciate SQLite for what it is since the comparison to Postgres is non-sensical when approaching from that viewpoint.
For anyone coming from a heavyweight SQL background, it is indeed pretty easy to be caught off guard with SQLite. That doesn't reduce its value of course.
If want to have more of an apples-to-apples comparison you could swap out Postgres for DuckDB (which aims to follow Postgres in SQL dialect), and all of the mentioned points should still hold.
Which is why I mention "history and philosophy" on the same line of my comment.
The `ALTER TABLE` documentation on SQLite [0] has a very clear reasoning on why it's implemented the way it is, and gives steps on how to reproduce more common usage of `ALTER TABLE` in other SQL engines.
To directly quote from their docs:
> Why ALTER TABLE is such a problem for SQLite
> Most SQL database engines store the schema already parsed into various system tables. On those database engines, ALTER TABLE merely has to make modifications to the corresponding system tables.
> SQLite is different in that it stores the schema in the sqlite_schema table as the original text of the CREATE statements that define the schema. Hence ALTER TABLE needs to revise the text of the CREATE statement. Doing so can be tricky for certain "creative" schema designs.
> The SQLite approach of storing the schema as text has advantages for an embedded relational database. For one, it means that the schema takes up less space in the database file. This is important since a common SQLite usage pattern is to have many small, separate database files instead of putting everything in one big global database file, which is the usual approach for client/server database engines. Since the schema is duplicated in each separate database file, it is important to keep the schema representation compact.
> Storing the schema as text rather than as parsed tables also give flexibility to the implementation. Since the internal parse of the schema is regenerated each time the database is opened, the internal representation of the schema can change from one release to the next. This is important, as sometimes new features require enhancements to the internal schema representation. Changing the internal schema representation would be much more difficult if the schema representation was exposed in the database file. So, in other words, storing the schema as text helps maintain backwards compatibility, and helps ensure that older database files can be read and written by newer versions of SQLite.
> Storing the schema as text also makes the SQLite database file format easier to define, document, and understand. This helps make SQLite database files a recommended storage format for long-term archiving of data.
> The downside of storing schema a text is that it can make the schema tricky to modify. And for that reason, the ALTER TABLE support in SQLite has traditionally lagged behind other SQL database engines that store their schemas as parsed system tables that are easier to modify.
Current personal project is a game with crafting. I'm tired of games with limited inventory and no search / filters so I've been enjoying SQLite to manage that. The fact the db is a simple file is awesome to manage backups and you don't have to start a server to tinker with it: DB Browser, open file and you're done.
Also kudos to the team behind the Godot SQLite wrapper.
I'm not saying it does not deserve the attention, it is a fantastic piece of software but if I had the option to use PostgreSQL for something I'd never ever get close to choosing SQLite over it. It shines when you don't need or want something more feature packed.
I had the option for a recent project. It's a niche forum-like application with around 2,000 users. Went with a monolithic design and vertical scaling (if needed), so SQLite was perfect. Every dynamic HTML page renders in under 1ms. Litestream for live-replication to a couple S3 buckets.
Running PostgreSQL for something like this would be a pain in the ass, and add a minimum 10ms of latency to every request. There would genuinely be more maintenance required for the PostgreSQL server/daemon than the application itself. It just makes no sense.
I think this is the key point the original post was missing. When you’re working on a project with a DB, you should factor in the cost/overhead of the DB as well. Postgres is a wonderful DB. But it is also big and requires work to keep it running. For many projects, the extra overhead is well worth it.
(And if you’re using a “cloud” DB, the overhead is still there, but you’re explicitly paying for the privilege of making it someone else’s problem. )
But for smaller projects, or ones with less DB requirements, something small like SQLite is much more appropriate. And it nearly removes the DB overhead from the maintenance equation.
I'm using both Postgres and SQLite for active projects. Postgres is great for a multi-user blog I run where the DB is hosted, backed up, etc. The same site would run fine and slightly faster with local SQLite (which it used to) but having Postgres lets me use Render.com's built in management features, which are nice.
SQLite works great for anything app-like. I have a script that OCRs certain video game screenshots and saves the OCR data in a searchable database, for example. Postgres would be complete overkill for this and would add nothing but hassle. I don't want to bother with keeping a separate database going for that in my Postgres server. I just want a folder of files, and SQLite works perfectly for that. I could use Postgres but it would offer zero useful benefits and might be a bit slower (even with a local server) due to my sloppy code.
They are just different tools for different tasks, with some overlap.
1) strict always enforced.
2) full datatypes (ints, floats, datetime, jsonb)
3) all "ALTER TABLE" functionality, even if it has to rewrite the tableWhy make a new vesion that breaks compatibility with the old version?
Why make a new version just so it behaves like all the other database engines out there? Isn't having difference the point of having choices?
Seeing several comments like yours is puzzling to me because I find myself unable to understand what are the devs preaching for it gaining from SQLite's extremely conservative backwards compatibility policy.
For example, PostgreSQL has `pg_upgrade`. You run that after you upgrade its major version and it's a bulletproof and easy transition 99% of the time.
If you have a "closed" system (such that "you" control the database, and all access to it) then upgrades are "easy" to do. You just upgrade the server and all clients.
If your system is more diverse then it's harder because in that case server and clients gave to be upgraded together. If I'm using multiple different programs (Accounting, Payroll, Access control etc) possibly from multiple vendors, then coordinating everything to happen at the same time can be impossible. If the server is not backwards compatible with old clients then you can get stuck.
In the case of SQLite for example lots of systems rely on the stability of the file format. Breaking that would not be welcome.
When file format is no longer backwards compatible, it makes sense to make a new major (semantic) version, so you don't have a situation where version 3.50 can read a file but version 3.43 cannot.
He heard a hype (sqlite), decided to try it to see what is it about, found out it's not for him really, wrote a bit blurb about it on his blog.
So it's more a short set of notes (like a TIL) than a full-fleshed blog post. Brandur's long-form writing has a different tone: https://brandur.org/articles
I don't understand the comparison here at all.
All technologies, and especially databases, are best applied with thoughtful consideration of the context. I didn't get the sense the author was suggesting their SQLite use case would actually be better served by Postgres. Just the same as a use case best served by Postgres is not suitable for SQLite.
> I didn't get the sense the author was suggesting their SQLite use case would actually be better served by Postgres.
I got exactly that sense, from "I tried SQLite, it lacked these things. Postgres really is the best database".
I wouldn't bring a Ferrari Enzo to do a horse's job, nor a horse to do a Ferrari Enzo's job.
The same is even more true for Postgres and SQLite. Postgres is a poor fit for an on-device database for a mobile app or embedded device, and SQLite is a poor fit to power a high traffic horizontally scaled web service.
I really don't see how you can read this as anything other than "SQLite is worse than Postgres". He basically says "sometimes I doubt if Postgres is the best database, but then I use SQLite, and it really is".
SQLite is a tradeoff: It's very small and to make it that small, some traditional database aspects need to be discarded. That small size makes it useful when size is a factor.
Noting that every interactive site run under the Hwaci[^1] umbrella does, e.g. sqlite's own forum and source control system.
[1]: The company behind sqlite.
1. Strictness by default and with no escape hatches;
2. Proper data types like all dates / times / datetimes / JSON etc.
3. In-process / embedded database. Almost all projects I worked on over the course of a 22 years of career did not need a separate node / VM / pod for a database.
PostgreSQL / MySQL and SQLite are not some ideal polar opposites. There is potential for a lot of cross-pollination of features and the ability to achieve the perfect DB.
There. Now you understand the comparison.
Although conceptually I agree that SQLite's limited type system is frustrating, if your usecase allows, an ORM might help with not having to think about it or touch it directly.
- So make breaking changes as needed. (it was first released in 2000 and has fantastic backwards compatibility, but hardware & OS have radically changes over the last 24 years - as well as use cases).
- put more focus on client/server use cases
- make things more 'strict' (types, checks, etc)
Note: I say this with tremendous love for SQLite. There's just so many attempts for companies & projects to morph SQLite what it's not designed for, that you might as well embarrass these use cases and make an official fork to support them.
This is like arguing how great bicycles would be if they had four wheels, enclosed cabins, and gasoline engines.
They are not, they introduce management of another node / VM / pod which I'll boldly say that likely at least 80% of all projects everywhere do not need.
I'd kick a puppy if that means we can get in-process / embedded PostgreSQL.
> why dilute the optimality of SQLite
What does that even mean? Such a strange wording, as if it's a competition or a fight.
> This is like arguing how great bicycles would be if they had four wheels, enclosed cabins, and gasoline engines.
No, that's akin to arguing that a bicycle would benefit from crash protection if it came with just 2-3kg extra weight.
Can you explain your thinking re a solution being both in-process/embedded and focusing on client/server use cases? On the surface, these seem contradictory to me.
> What does that even mean? Such a strange wording, as if it's a competition or a fight.
I don't know about a "competition or a fight" but forking a project to target contradictory use cases definitely involves trade-offs.
What seems contradictory, not sure I understand?
In my consulting and contracting practice I have only ever had 3 projects that actually needed a big dedicated database. Everything else would have done just fine with an in-OS-process model like SQLite. But SQLite is too lax with data typing and I am not keen on 40% of the invoice for my customers to be "+300% extra data validation code because SQLite devs used and loved TCL". Sorry for the snark, but TCL influencing SQLite is a historical reality, if my memory hasn't betrayed me that is.
I'd love it if we had the same DB engine have an embedded and client-server variants. You start off with the embedded and if the project grows then you simply modify its config and don't have to change one line of code in your project (though obviously your platform team has to then provision it but that's a given).
Today this is sadly a fantasy and does not exist. I want it to exist.
And why PostgreSQL? Well, I like data strictness, and PG has a lot of desirable features like DDL transactions and enum types.
In-process embedding implies making the DB engine part of the program itself, within a single runtime instance, in the same way as you'd import any other library. Client/server architecture is a situation in which one program is communicating with another one, running somewhere else, over some sort of messaging channel. On the surface, these seem to be mutually exclusive usage models.
> I'd love it if we had the same DB engine have an embedded and client-server variants.
It sounds like you want something that uses similar syntax and defaults as Postgre, but is used in an embedded fashion, a la SQLite. This is a reasonable idea, but it seems like you want to focus on embedded use cases, not client/server, and this sounds like something that would be best implemented as a third solution entirely, not a fork of either SQLite or Postgres.
Not sure how I was unclear (sorry if I was), my take was basically "I want PostgreSQL[-like] engine that can work in embedded and client-server mode depending on project" really. I want more choice than we have right now, that is my wish.
> and this sounds like something that would be best implemented as a third solution entirely, not a fork of either SQLite or Postgres
I don't see why. Technically there are hurdles, sure, but there always are anyway -- I believe in the case of both SQLite and PostgreSQL it's either lack of resources or lack of motivation to go outside their niche. Whatever the case I am not judging them, it's their project and I am just a rando who wants to work less on their storage / validation layer.
But yeah, I don't see why must we get a 3rd player necessarily. You might still turn out to be correct, mind you, I am just saying that it's not necessarily the case that this DB engine (that will have both embedded and client-server modes) must be a separate project.
I love SQLite but I always end up having to write a lot of validation and at one point you do ask yourself whether your energy should not go somewhere else.
Time will tell, I suppose.
You can use CREATE TABLE STRICT[1] to get a typed table.
One concrete example of that is sqlite's own source control system, the Fossil SCM. Within Fossil, sqlite does _lots_ of the heavy lifting, replacing tens of thousands of lines of C code[^1]. Richard Hipp (of sqlite fame) recently mused that sqlite takes on at least the following distinct database roles in that project:
- Document database (how SCM records are natively stored[^2]).
- Graph database (queries which extract the lineages of projects' artifacts from directed acyclic graphs[^3]).
- Key-value store for config data of arbitrary types (all in the same table).
The first two can be done with any SQL db, but the latter requires sqlite's particular flexibility. Never once (literally never once) in the development of fossil has that flexibility caused us (==its many contributors) any grief.
[^1]: as a fossil contributor since 2008, i can say with complete confidence that that is no exaggeration.
[^2]: <https://fossil-scm.org/home/doc/trunk/www/fossil-is-not-rela...>
[^3]: <https://core.tcl-lang.org/tcl/timeline?c=2024-06-30> is a good example
1. It started as a way for the author to access databases from TCL, in which everything is a string. Sounds kind of mad now, but that was the kind of thing you did back in the 90's.
2. SQLite is fanatically backwards compatible. That means that once you get a system that works, it will continue to work through all newer versions of SQLite. But that also means that you can't suddenly decide to enforce foreign keys or column types by default, because it will break loads of systems that worked just fine before.
The code is Public Domain, though. Anyone should feel free to scratch their itch.
Perhaps the debate might be over how much trouble is too much. AFAICT automated "find and replace" can cure just about every "incompatibility".
And if you decide you want to "win" here and thus stubbornly reply "yes" then I'd say "you are the vanishing minority".
Approaching a piece of tech -- that's yet unknown to you -- with expectations is natural. Sometimes with tragic results but still completely natural for us the humans.
My approach is to firstly skim the index and look at anything interesting, secondly just use the docs as a reference while I learn the technology, and thirdly come back and read them cover-to-cover (skimming over the boring/irrelevant bits).
There are some neat things you can do with it like using HTTP range queries to query it directly from object store or Litestream, but my point stands.
Its straightforward, fast and easy for a casual DB programmer. Everything is wrapped in SQLAlchemy anyway, so all the complicated logic is in my Python code.
Which have been around ALTER_TABLE and CONSTRAINTS
Here are some notes about my experience, offered to anybody who might be able to use them. https://www.plumislandmedia.net/reference/sqlite3-in-php-som...
I recently built a product with the backend using SQLite as the data store and ran into all these issues and many more. It is frustrating. I use SQLAlchemy and Alembic. It seemed everywhere I turned, the docs said “it works this way in all databases, except SQLite where X isn’t supported or you have to do Y differently.”
I think with litestream and D1 and other web SQLite tech emerging, you see the sentiment: “if you don’t have Google-scale, you can easily serve using disk-backed SQLite, plus enjoy skipping RTT network latency to DB.” Then, when someone does that and has a bad time, the comments instead go: “SQLite is only for embedded data stores.”
Personally, if I had to do it again, I would stick to the most boring tech for the target stack: Postgres and Django’s ORM.
Just use sql. Very straightforward and easy to use program understand maintain.
In the end SQLAlchemy and Alembic generate and execute SQL. The weird behaviours are due to sqlite's idiosynchasies
I also think that the closer you are to your DB, more you appreciate the features you actually need are. A developer should know how their data is ultimately stored. And if an ORM hides this from you, that’s a problem.
Note: and ORM doesn’t need to hide this and can be a rational way to manage DB storage. But it’s then up to the developer to manage that abstraction. I’ve done this before where I used an ORM but I still knew exactly what SQL was going to be generated. But for many, especially new devs, the ORM is a black box.
Sqlite is not just for embedded data stores. However, it makes trade-offs to achieve ease of use and performance on certain workloads. If it tried to be postgres then it would be an inferior competitor; instead, it is an alternative that suits some use cases better and others worse. If you're trying to build a web app that can be deployed and backed up as two files, an executable and a db, then you'll probably want sqlite. If you're trying not to shoot yourself in the foot with "oh god why can't I do a full outer join and why are all the dates weird" then use postgres.
I imagine programmers would also find cockroachdb frustrating compared to postgres, if they didn't benefit from anything it offered and used it anyway.
https://docs.djangoproject.com/en/dev/ref/databases/#sqlite-...