Skip to content

Conversation

@brandur
Copy link
Contributor

@brandur brandur commented Apr 17, 2025

Here, introduce a full version of schema injection as initially started
in #798, and exposing a new Schema option to make it configurable even
outside the test suite.

This involved fixing a lot of bugs from #798, and the only way to make
it possible to root them all out was to make full use of schemas across
the entire test suite. The original "test DB manager" system has been
replaced with a new riverschematest.TestSchema helper that generates a
schema for use with a test case or prefers an existing idle one that was
already generated for the same test run.

TestSchema runs migrations for generated schemas which also means we
don't need to use testdbman anymore, with tests capable of
bootstrapping themselves at run time. We reuse schemas to avoid this
extra work when possible, but migrating a new schema is also
surprisingly fast, taking up to 50ms, but getting more down to a stable
~10ms once things are warmed up. Here's a series of sample timings:

$ go test ./rivermigrate -run TestMigrator/MigrateUpDefault -test.v -count 50 | grep 'migrations ran in'
    river_migrate_test.go:492: migrations ran in 45.571209ms
    river_migrate_test.go:492: migrations ran in 24.642458ms
    river_migrate_test.go:492: migrations ran in 16.749708ms
    river_migrate_test.go:492: migrations ran in 22.970375ms
    river_migrate_test.go:492: migrations ran in 16.201375ms
    river_migrate_test.go:492: migrations ran in 15.727625ms
    river_migrate_test.go:492: migrations ran in 13.291333ms
    river_migrate_test.go:492: migrations ran in 13.680708ms
    river_migrate_test.go:492: migrations ran in 14.867416ms
    river_migrate_test.go:492: migrations ran in 15.631916ms
    river_migrate_test.go:492: migrations ran in 13.873791ms
    river_migrate_test.go:492: migrations ran in 14.8645ms
    river_migrate_test.go:492: migrations ran in 14.92575ms
    river_migrate_test.go:492: migrations ran in 12.541834ms
    river_migrate_test.go:492: migrations ran in 14.753875ms
    river_migrate_test.go:492: migrations ran in 12.694334ms
    river_migrate_test.go:492: migrations ran in 13.955917ms
    river_migrate_test.go:492: migrations ran in 12.126458ms
    river_migrate_test.go:492: migrations ran in 14.095958ms
    river_migrate_test.go:492: migrations ran in 13.273375ms
    river_migrate_test.go:492: migrations ran in 13.988917ms
    river_migrate_test.go:492: migrations ran in 13.141459ms
    river_migrate_test.go:492: migrations ran in 12.394417ms
    river_migrate_test.go:492: migrations ran in 11.539208ms
    river_migrate_test.go:492: migrations ran in 11.577834ms
    river_migrate_test.go:492: migrations ran in 10.883375ms
    river_migrate_test.go:492: migrations ran in 10.547417ms
    river_migrate_test.go:492: migrations ran in 12.330375ms
    river_migrate_test.go:492: migrations ran in 11.54575ms
    river_migrate_test.go:492: migrations ran in 11.437458ms
    river_migrate_test.go:492: migrations ran in 10.957ms
    river_migrate_test.go:492: migrations ran in 10.589083ms
    river_migrate_test.go:492: migrations ran in 9.758583ms

Removal of the "test DB manager" system also means that we can ungate
test from -p 1 because they're all able to run in parallel now. The
limiting factor I ran in is that we need to keep max pool connections
within each package's tests to a relatively modest number (I found 15
seemed to maximum success) so parallel packages don't exceed the default
Postgres configuration of 100 connections.

Something that can be kind of annoying is that in case a schema isn't
used properly somewhere in a test case (i.e. TestSchema is run, but
then not used), inserts/operations will go the default schema, which
will leave debris there, and that will interfere with test cases using
TestTx (with test DB manager, all debris would go to a different
database so you wouldn't notice). To remediate this, I've added a
cleanup hook to TestSchema that looks for leftovers that may have been
added to the default schema. This isn't perfect because those leftovers
may have come from another test case running in parallel or which ran
previously, but it helps to zero in on the original source of the issue.

@brandur brandur force-pushed the brandur-schema-testing branch from 5bec84c to 919a01f Compare April 17, 2025 05:00
@brandur brandur marked this pull request as draft April 17, 2025 05:02
@brandur brandur force-pushed the brandur-schema-testing branch 4 times, most recently from 98c2b96 to 9101db8 Compare April 17, 2025 05:23
@brandur
Copy link
Contributor Author

brandur commented Apr 17, 2025

@bgentry Still needs a little more work, but test runs go from ~2m45s to ~1m, which is pretty nice.

I'm not even completely sure why the improvement is that good, but I think we were waiting a lot longer than we thought waiting for test databases. Although only N = num CPUs tests were running in parallel at any given time, I think what happens is that Go boots a whole bunch of test cases, but may pause them while it engages others, some of which may have been holding a test database and leaving the available pool smaller for other test cases that might need one. Ungating the number of schemas allowed at any given time I found that the main package will generate up to 125 of them before tests start finishing fully and it starts reusing them. Not 100% sure about this theory, but whatever it is exactly, the speed increase dramatic.

I'm still blocked on one major thing which is that sqlc's copyfrom is totally screwed and won't even work with the sqlctemplate system. I got the tests running by editing the generated Go manually to enable a schema to be injected, but still trying to figure out what to do about it.

diff --git a/riverdriver/riverpgxv5/internal/dbsqlc/copyfrom.go b/riverdriver/riverpgxv5/internal/dbsqlc/copyfrom.go
index ee29edf..5d18b02 100644
--- a/riverdriver/riverpgxv5/internal/dbsqlc/copyfrom.go
+++ b/riverdriver/riverpgxv5/internal/dbsqlc/copyfrom.go
@@ -48,6 +48,11 @@ func (r iteratorForJobInsertFastManyCopyFrom) Err() error {
 	return nil
 }
 
-func (q *Queries) JobInsertFastManyCopyFrom(ctx context.Context, db DBTX, arg []*JobInsertFastManyCopyFromParams) (int64, error) {
-	return db.CopyFrom(ctx, []string{"river_job"}, []string{"args", "created_at", "kind", "max_attempts", "metadata", "priority", "queue", "scheduled_at", "state", "tags", "unique_key", "unique_states"}, &iteratorForJobInsertFastManyCopyFrom{rows: arg})
+func (q *Queries) JobInsertFastManyCopyFrom(ctx context.Context, db DBTX, schema string, arg []*JobInsertFastManyCopyFromParams) (int64, error) {
+	tableName := []string{"river_job"}
+	if schema != "" {
+		tableName = append([]string{schema}, tableName...)
+	}
+
+	return db.CopyFrom(ctx, tableName, []string{"args", "created_at", "kind", "max_attempts", "metadata", "priority", "queue", "scheduled_at", "state", "tags", "unique_key", "unique_states"}, &iteratorForJobInsertFastManyCopyFrom{rows: arg})
 }

@brandur brandur force-pushed the brandur-schema-testing branch 11 times, most recently from efcd2cd to 3db7a7e Compare April 18, 2025 00:45
@brandur brandur marked this pull request as ready for review April 18, 2025 01:21
@brandur brandur requested a review from bgentry April 18, 2025 01:21
@brandur
Copy link
Contributor Author

brandur commented Apr 18, 2025

@bgentry Alright, I managed to resolve the copyfrom problem by leveraging pgx's generated interface once again and putting some code in templateReplaceWrapper that'll add a schema if necessary. It's all cleaner than I expected.

I'm still writing tests for riverschematest and did find a couple minor problems with the design there, but I think this is pretty much good to start looking at when you have a chance.

I also rebuilt the TestSchema API to use a driver instead of pgxpool, which will make sure that River Pro's migration lines are available to it so we can get a quick implementation over there.

@brandur brandur force-pushed the brandur-schema-testing branch 3 times, most recently from 0b8592e to 842cc2c Compare April 18, 2025 07:23
@brandur
Copy link
Contributor Author

brandur commented Apr 18, 2025

Alright, new tests in place for riverschematest. Everything here should be 100% functional.

@brandur brandur force-pushed the brandur-schema-testing branch 5 times, most recently from c9a4459 to 4411a48 Compare April 20, 2025 23:18
brandur added a commit that referenced this pull request May 10, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 10, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 10, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 10, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 10, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 10, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 10, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 11, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 14, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 14, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 14, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 14, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 14, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 14, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 14, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
brandur added a commit that referenced this pull request May 14, 2025
Here, add an implementation for SQLite. The new driver passes the entire
driver test suite (including some newly added tests), and checks out
against a new end-to-end suite of client tests targeting it. I think at
this point we can consider it to be largely functional, although with
minimal real-world vetting, but at least alpha quality that we can start
putting in front of users.

The main reason this turned out to be quite an effort is that SQLite,
although nominally supporting a lot of the same syntax Postgres does,
just has a lot of unexpected limitations and quirks that all need to be
tracked down separately and a workaround added. For example:

* SQLite doesn't really have data types. Everything is either an
  integer, real, text, or blob, and anything more sophisticated just has
  to be shoehorned into one of these types. For example, there's no
  boolean. Despite the fanfare recently, there isn't even a jsonb. Jsonb
  gets pushed into a blob.

* The most annoying missing data type is a timestamp. Date/times are
  stored as either Unix integers or strings, and SQLite provides a
  number of obnoxious, error-prone, built-ins to work with them.

* No arrays.

* No modification operations in CTEs. (Although counting my lucky stars
  that CTEs are supported at least.)

* No listen/notify.

Aside from that though, we had to get some testing infrastructure in
place, and our testing has traditionally been quite affixed to Postgres,
which would've made a lot of changes necessary. However, the schema
additions in #848 do help to make this quite a bit easier with the
driver-based `TestSchema` and `TestTx` functions, and because they raise
schema automatically, it doesn't complicate test setup instructions in
any way by adding extra steps.

A new `TestDriverRiverSQLite` test has been added to run the full
barrage of driver tests for SQLite. I've also added a new family of
`TestClientWithDriver` tests that are similar to `TestDriver*` in that
they run for each of our supported drivers, but in this case they do a
series of end-to-end tests to more fully exercise the entire River
client. I added a respectable set of base client driver tests that walks
each driver through all basic functionality (including for features that
are higher risk of failing from one database compared to the next like
`JobList`), but as with most tests, they could stand to be extended
further, which I'm sure we'll get to in the future.

An example test for SQLite has been added, demonstrating the use of
River with a SQLite driver and in-memory SQLite database.

A few caveats:

* I've found that the combination of SQLite's reduced capabilities +
  sqlc's bugginess [1] make bit batch insert/update operations basically
  impossible (I tried every workaround I could possibly thin of), so
  these are done in loops of individual operations instead. I *think*
  this is okay for now though. For one, sqlc will hopefully get these
  limitations fixed eventually, and for two, SQLite databases will often
  be running locally, meaning the round trip cost per operation is much
  lower than what we'd see in a hosted Postgres somewhere.

* It's not totally clear that having a SQLite implementation will be
  "worth it enough" in the long run given that it will add some
  difficulty to non-trivial future database-related operations. My hope
  is that as it's in a prerelease state, we can gauge how bad it is to
  keep up to date. If it feels like way more effort than it's worth, we
  can still axe it before it ever becomes a functional driver.

[1] sqlc-dev/sqlc#3802 (comment)
Sign up for free to 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.

3 participants