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
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>
84 lines
2.1 KiB
Go
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()
|
|
}
|