diff --git a/README.md b/README.md index bdba66e..2b7b688 100644 --- a/README.md +++ b/README.md @@ -716,13 +716,13 @@ Key settings in more detail: - `db_url` — the SQLite database to open; omitted, it is `file:/state.sqlite3?_pragma=journal_mode(WAL)`, which keeps the database in WAL mode. `state_dir` is percent-encoded in it, so a `%`, `?` or - `#` in the name is written as `%25`, `%3F` or `%23`. For a `state_dir` - starting with `//` it starts `file:////`, so SQLite does not read the first - directory name as a host. pixa opens one connection to it, so its own reads - and writes run one at a time. pixa adds `_pragma=busy_timeout(5000)` to any - `db_url`, so a write that finds another program writing to the file waits up - to five seconds for it instead of failing. WAL mode comes only from the URL: - keep `_pragma=journal_mode(WAL)` in one you set + `#` in the name is written as `%25`, `%3F` or `%23`. For a `state_dir` of `/` + or one starting with `//` it starts `file:////`, so SQLite does not read the + first name in the path as a host. pixa opens one connection to it, so its own + reads and writes run one at a time. pixa adds `_pragma=busy_timeout(5000)` to + any `db_url`, so a write that finds another program writing to the file waits + up to five seconds for it instead of failing. WAL mode comes only from the + URL: keep `_pragma=journal_mode(WAL)` in one you set - `cache_max_bytes` — disk cache size limit in bytes; `0` disables the disk cache entirely; omitted defaults to 75% of the sum of the free space on the filesystem containing `/cache/` and the bytes of source and diff --git a/TODO.md b/TODO.md index c193ade..05a03ef 100644 --- a/TODO.md +++ b/TODO.md @@ -30,6 +30,14 @@ P2: security: per-origin rate limiting # Completed Steps +- 2026-10-16 a `state_dir` whose name holds `%`, `?` or `#` keeps its database + at `state.sqlite3` in that directory (closes #290): the `db_url` derived from + it put the name into a `file:` URL as it was, so SQLite read `%41` as `A`, a + `?` or `#` ended the path early, and the journal mode parameter was lost. The + derived `db_url` is now built with Go's `net/url`, which writes those + characters as `%25`, `%3F` and `%23`. For a `state_dir` of `/` or one starting + with `//` it starts `file:////`, so SQLite no longer reads the first name in + the path as a host. - 2026-10-16 the source URL a request fetches is built with `net/url` from the source host, path and query, not joined as text (closes #288). The source path, decoded, is fetched percent-encoded again, so `100%25.png` fetches @@ -43,14 +51,6 @@ P2: security: per-origin rate limiting another way, such as with `%2F` in the path, whatever else the path holds, or with a user name and password. It also answers 400 for a source path holding a `..` segment, as `/v1/image/` refuses one. -- 2026-10-16 a `state_dir` whose name holds `%`, `?` or `#` keeps its database - at `state.sqlite3` in that directory (closes #290): the `db_url` derived from - it put the name into a `file:` URL as it was, so SQLite read `%41` as `A`, a - `?` or `#` ended the path early, and the journal mode parameter was lost. The - derived `db_url` is now built with Go's `net/url`, which writes those - characters as `%25`, `%3F` and `%23`. For a `state_dir` starting with `//` it - starts `file:////`, so SQLite no longer reads the first directory name as a - host. - 2026-10-15 a thin source requested with `fit=cover` no longer makes libvips build a huge image (closes #282): libvips' thumbnail scaled the whole source to cover the requested size and copied that into memory before it cut out the diff --git a/internal/config/config.go b/internal/config/config.go index bc657ff..40eb799 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -359,14 +359,18 @@ func newFromSmartConfig(sc *smartconfig.Config) (*Config, error) { // the directory name, which SQLite decodes back, so the URL names exactly // this file. OmitHost writes file:/var/lib/pixa/... instead of // file:///var/lib/pixa/..., and keeps a relative stateDir from being read -// as a host name. A stateDir starting with // keeps the empty host, so the -// URL starts file://// and SQLite does not read the first directory name -// as a host. +// as a host name. The path written into the URL is stateDir followed by +// /state.sqlite3; when it starts with //, as it does for a stateDir of / +// or one starting with //, the empty host is kept, so the URL starts +// file://// and SQLite does not read the first name in the path as a +// host. func defaultDBURL(stateDir string) string { + dbPath := stateDir + "/state.sqlite3" + dbURL := url.URL{ Scheme: "file", - OmitHost: !strings.HasPrefix(stateDir, "//"), - Path: stateDir + "/state.sqlite3", + OmitHost: !strings.HasPrefix(dbPath, "//"), + Path: dbPath, RawQuery: "_pragma=journal_mode(WAL)", }