Files
Warp/server/internal/storage/pocketbase/helpers.go
T
tajniak81andClaude Opus 5.5 a47e15cc1c
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Admin panel (push) Canceled after 0s
CI / Web client (push) Canceled after 0s
CI / Desktop client (ubuntu-24.04) (push) Canceled after 0s
CI / Desktop client (windows-latest) (push) Canceled after 0s
PocketBase as a standalone database server
The pocketbase driver no longer embeds PocketBase: Warp keeps its data as
PocketBase collections on a server of its own and reaches it only through
the REST API, the way the postgres driver reaches PostgreSQL.
database.connection_string is the server's URL, and Warp signs in as the
superuser in WARP_PB_SUPERUSER_EMAIL / WARP_PB_SUPERUSER_PASSWORD (or a
WARP_PB_SUPERUSER_FILE of the two).

Every SQL table is a superuser-only collection of the same name and
columns, created by warp migrate. Reads go through PocketBase's SQL
endpoint with the SQL drivers' queries, via a read-only database/sql
driver that inlines arguments, moves long ones into warp_params and pages
past 1000 rows. Writes are record operations in batch requests under one
write lock in the Warp process. Media is a protected file field in
warp_media, local or in the S3 bucket Warp writes into PocketBase's
settings. Search matches word prefixes with LIKE. The storage tests run
against an in-process PocketBase and pass as on SQLite.

Docker: the PocketBase stacks get a pocketbase service running
tajniak81/pocketbase-docker-root (WARP_PB_IMAGE), with a pb-data volume
and its admin UI on loopback; the AIO images run that PocketBase binary
as a process beside the homeserver, making up a superuser when none is
set. The .env examples, READMEs, docs and warp.yaml.example follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 16:31:08 +01:00

84 lines
2.1 KiB
Go

package pocketbase
import (
"encoding/json"
"strings"
)
// The counter tables and the names in them, as the SQL drivers have them.
const (
roomSequences = "roomserver_sequences"
syncSequences = "sync_sequences"
federationSequences = "federation_sequences"
keySequences = "keyserver_sequences"
pushSequences = "push_sequences"
adminSequences = "admin_sequences"
seqStreamOrdering = "stream_ordering"
seqStateSnapshot = "state_snapshot"
seqBackfillOrdering = "backfill_ordering"
seqReceipt = "receipt"
seqToDevice = "to_device"
seqAccountData = "account_data"
seqPresence = "presence"
seqQueue = "queue"
seqDeviceList = "device_list"
seqNotification = "notification"
seqAudit = "audit"
)
// inList is the IN-list form every store uses: one JSON array argument,
// expanded by json_each, instead of a placeholder per element. The SQL
// endpoint takes no parameters and at most 5000 characters, and a single
// argument is what the driver can move out of a query that would be too
// long; a few hundred separate ones would each have to be moved.
const inList = `(SELECT value FROM json_each(?))`
// jsonList renders strings as the JSON array inList takes.
func jsonList(values []string) string {
if values == nil {
values = []string{}
}
raw, _ := json.Marshal(values)
return string(raw)
}
// jsonInts renders integers as the JSON array inList takes.
func jsonInts(values []int64) string {
if values == nil {
values = []int64{}
}
raw, _ := json.Marshal(values)
return string(raw)
}
// pageBounds clamps a caller's limit and offset.
func pageBounds(limit, offset, defaultLimit, maxLimit int) (int, int) {
if limit <= 0 {
limit = defaultLimit
}
if limit > maxLimit {
limit = maxLimit
}
if offset < 0 {
offset = 0
}
return limit, offset
}
// escapeLike makes a literal string safe to embed in a LIKE pattern that
// declares '\' as its escape character.
func escapeLike(s string) string {
var b strings.Builder
b.Grow(len(s))
for _, r := range s {
if r == '\\' || r == '%' || r == '_' {
b.WriteRune('\\')
}
b.WriteRune(r)
}
return b.String()
}