SQLite should have (Rust-style) editions

mort.coffee

348 points by gnyeki a day ago


sethev - a day ago

Interesting idea - I like seeing a list of pet-peeves followed by a proposal for a straightforward way to have a set of 'alternative defaults' that remains backwards compatible. If you don't want to opt in, don't run the new PRAGMA edition = 2026.

Too often it's just a list of issues and a wish that everyone else will change.

In (mild) defense of SQLITE_BUSY - busy_timeout just tells sqlite to sleep and retry up to the timeout when it receives SQLITE_BUSY. It seems like a sensible default for a library to leave that up the calling code - which may have something else it could do while it waits. However, that logic often gets missed!

kccqzy - a day ago

SQLite is slightly different from Rust in that it is a data container. It’s somewhat more common for people to move SQLite database files from one machine to another and then inspect using the command line tool. And it is often the case that the embedded SQLite version in your app is a newer version than whatever version /usr/bin/sqlite3 happens to be. Adding editions to your SQLite file will probably break this use case of using an older version to read a database written by a newer version because it does not know what has changed in a new edition.

Not a big deal though. Probably just need better ops to bundle the command-line utility that’s the same version as what’s used in your app.

dolmen - 12 hours ago

Counterpoint: each new edition will add bloat to SQLite as future edition will need to keep each past edition pragma set for backward compatibility.

As SQLite is often used embedded, bloat matters.

So I suggest that "PRAGMA edition" to be only be a shortcut to a list of PRAGMA commands, that would be expanded at the library level: PRAGMA edition would never appear in the DB file. As such, the build of the library would just support a limited set of editions, with a removal policy in default builds. Maybe editions could even be defined at runtime (a system table?) as a way to load them dynamically if old editions are needed beyond builtin support (think about the state of SQLite in 2046).

andai - a day ago

The "use strict" thing is interesting. I often hear people say, well we can't fix absurd behavior in JS because backwards compatibility! Well, we already did, and we can do it again!

jmull - 10 hours ago

I actually think this wouldn’t be that helpful.

The developer still needs to ensure they apply their set of pragmas, whether that’s a single edition pragma or a set of pragmas. And they still need to understand and carefully choose the pragmas/options they use (an edition really makes this a little harder by abstracting/hiding something that needs to be directly understood and visible).

And these proposed new default pragmas are more incremental improvements rather than complete solutions, more convenience than critical. E.g., while the default affinity is goofy, strict tables don’t come close to a comprehensive validation mechanism — so if you need strong validation you’re likely going to need to implement that at a higher level anyway. (Also, there’s a decent separation-of-concerns argument that you should handle it separately.)

Likewise the busy timeout. The pragma is convenient but you still need to handle busy timeouts. (The author’s problem, “I didn’t realize busy timeouts could happen so the app didn’t handle them correctly”, is not solved by the pragma.)

mort96 - a day ago

Oh hi, author here. Fun to see this make it to HN.

alexwennerberg - 2 hours ago

I don't have a problem with setting a few relevant flags for any new SQLite database. It's a good encouragement to familiarize yourself with the settings.

andai - a day ago

In the first example, there's a a second thing that surprised me: you delete an entity and it's unique ID gets reused? Is that a good idea?

I guess if foreign keys are handled properly then that's not a problem by definition? But it sounds wrong somehow.

azeirah - 17 hours ago

Oh I really like this! The one counterargument that comes to mind is when I think about the likes of C++, where there are many editions and they can be confusing to keep track of.

I'm not sure if you'd want to set one edition in stone every year. Perhaps every 3 years? Or 5 years? Especially for a long-term project like SQLite, that sounds perfectly acceptable!

bambax - 16 hours ago

> I don't think I need to explain why it's a bad idea for a database to be so careless about data validation.

Well, loose typing can be extremely useful, and having a type of "ANY" would not replace it.

I have built recently an accounting reconciliation system to find discrepancies in data coming from a large variety of sources: some from proper database engines (MySQL MariaDB), but most from proprietary systems that export to CSV. It's amazing how corrupt data can become: dates that are invalid, numbers that aren't numbers, strings strings strings everywhere.

Being able to store the data into tables that have types, but can accept anything, is simply great.

tzot - 11 hours ago

> I once had to clean up a project where some code had accidentally been writing the strings '1' and '0' to a column which was intended to store booleans (1 and 0). That was not a fun debugging story.

So this was a write to a column that did not have INTEGER affinity. If it was intended to be used as a boolean, then it should have INTEGER affinity. I know because I've tried hard to enter integer- and float-like strings as strings in INTEGER affinity columns, and I haven't managed to; I could only insert them as BLOBs, or prefix the string with say '\' and check/remove at the application level. (That was for an ontology-like database, where table EAttribute.eatvalue could have any type.)

throwaway2037 - 7 hours ago

    > Bad default #3: SQLITE_BUSY errors with concurrent writers
This is a weird complaint to me. When I use SQLite, I always make sure there is a single writer thread. Any thread can submit a write request in a thread-safe queue. If you follow this pattern, you never need to worry about SQLITE_BUSY errors.
red_admiral - 12 hours ago

I think the OP wants duckdb.

The first two points are deliberate SQLite design decisions so they're unlikely to change.

hommelix - 7 hours ago

The oldest `editions` idea I know of, is with Perl. For 20 years it was already common to write `use 5.18` to activate multiple options.

https://perldoc.perl.org/functions/use

ian-g - 8 hours ago

It sounds like you're also advocating for some form of permanent pragma that gets stored in the SQLite file and that the CLI will read and apply on startup?

That way somebody can snag a copy of your data and be subject to the same constraints by default.

Retr0id - a day ago

An alternative is to use wrapper libraries that set sane defaults, e.g. https://rogerbinns.github.io/apsw/bestpractice.html

But I suppose it would be nice to have a standard way to refer to those defaults, in a cross-runtime way.

Rendello - a day ago

It might be worth bringing this up on the forum [1]. The developers are quite active there, and it's possible they've never considered this option, or they have considered it and have reasons to not go for it. The original design followed Postel's Law (see my comment from the other say [2]), it would (theoretically) be nice if that mess could be avoided by specifying an edition.

Today I noticed I could do `pragma foreign_key = ON`, and despite the pragma being wrong (it should be foreign_keys, plural), it reported nothing. In fact, it reports nothing with the correct pragma either. So check your pragmas!

1. https://sqlite.org/forum/forum

2. https://news.ycombinator.com/item?id=48900625

souvlakee - a day ago

Sadly, the ORM layer lags here: Drizzle has no way to declare STRICT tables. The request has been open since March 2023 (issue #202, now discussion #2435) and didn't make the v1.0 beta either. The only workaround is hand-appending STRICT to generated migration SQL, which doesn't work at all if you use `drizzle-kit push`.

https://github.com/drizzle-team/drizzle-orm/discussions/2435

ncruces - 15 hours ago

This changes one default that "everyone agrees about" and which you can change with a compile time option: SQLITE_DEFAULT_FOREIGN_KEYS

Then it argues for STRICT tables, recognizing that there are drawbacks without introducing a new feature (custom type aliases, CREATE TYPE alias = base).

If also doesn't even considering what it means for existing data to make tables strict, which is precisely why “there is no pragma to globally make all tables strict”.

Then it argues for setting a busy timeout, and picks 5s. Why? Why 5s and not 1 or 60s? SQLite doesn't decide, which makes perfect sense. Your OS or programming language also doesn't offer you locks with a default timeout: it's either indefinite, or an instant "try lock".

Finally: WAL mode is a different file format, unsupported on many platforms, in more danger of silent corruption. Why should it be the default?

vanyaland - 18 hours ago

On iOS most apps link Apple's prebuilt libsqlite3, so compile-time defaults are out of reach.

- 21 hours ago
[deleted]
Thaxll - a day ago

SQLite gets so much praise here but when you start using it, you realize quickly how bad it is, the type system is by default very limited and dangerous.

It's like comparing old php with a strongly typed language.

There is not even a date type...

ak39 - 14 hours ago

Off-topic: TIL wal2 mode in the works. Wow. That's a no-brainer feature, IMHO.

GianFabien - a day ago

Nah, all those defaults are features. Of course, there are contexts where those defaults are unsuitable which means: Use a Different RDBMS!

henryoman - 18 hours ago

been working on a new implementation entirely of a local sqlite like database. It's from scratch in rust and data is made up of TSV's so the data is human (and agent) readable. sql querying is more expensive for an llm

chillfox - a day ago

you can also set those defaults with compile time flags, that's what I have been doing.

tmpfile - 21 hours ago

Why not a .conf file like everything in /etc or postgresql.conf?

IceDane - 13 hours ago

It's really weird to me how the SQLite author is clearly a very smart guy and talented developer and then his argument against type safety effectively just boils down to

> But I do not recall a single instance where the bugs might have been caught by a rigid type system.

Which is a shame. Of course the author writes more than this, but this is IMO largely the gist of the argument. At this point it's beginning to feel like this is mostly a sort of stubborn sunken cost fallacy, where they've been arguing this for so long they can't take the "hit" of agreeing to change the defaults.

latexr - 13 hours ago

Assuming the author is here (they link to the HN submission from the post), you have a typo: “laudbile” instead of “laudable”.

andrewchambers - a day ago

It is sqlite3. Emphasis on the 3 - it already has 'editions'.

PunchyHamster - 15 hours ago

I think the solution is for author to use PostgreSQL

Those choices were made for specific reasons that make sense in embedded environment and when backward compatibility is no.1 concern.

But I wouldn't mind feature-sets. Editions are too wide of a concept and tell you nothing at glance what a given code is doing, "enable 2026 set of features" tells me nothing on what is actually enabled.

spwa4 - 16 hours ago

You know, 10 years ago one might have remarked ... changing these defaults means C programmers would have to correctly implement error handling and retries.

The reaction they're likely to have to that can probably be best described in megatons, like any other nuclear explosion ...

WhereIsTheTruth - 13 hours ago

Who are these tourists?

D will get caught into that edition trap too

All it does is fragment a ecosystem, bloat a source tree and makes maintenance a painful task only to please people who think maintainers should cater to their poor tech hygiene

eduction - 21 hours ago

HN… for the love of god… please please stop trying to make SQLite be something it isn’t. Leave this poor project alone.

It’s a great tool if you want to give a local app its own database. If you need concurrent writes and full ACID guarantees of an industrial strength database, use an industrial strength database.

Yes, other databases will require you to read more manual pages and configure a service. Higher up front cost. Not “lightweight.” But given enough operating time there is a certain unarguable lightness to using the right tool for the job.

SnehRJoshi - 18 hours ago

[flagged]

KomorKomor - 11 hours ago

[flagged]

lasjk7 - 18 hours ago

[flagged]

linncharm - a day ago

[flagged]

mgc_blackbox - 19 hours ago

[flagged]

vugar82 - 15 hours ago

[flagged]

redsocksfan45 - a day ago

[dead]

tiaisliar - 8 hours ago

[flagged]

SaturnIC - 8 hours ago

Rust is a cancer