Skip to main content

StateStore dialects (SQLite vs Postgres)

This is a reference page for the small set of intentional schema differences between SQLite and Postgres.

Quick orientation

Dialect matrix

ConcernSQLitePostgresRule of thumb
IDsTEXTUUIDKeep logical shapes aligned; hide differences in statestore helpers.
BooleansINTEGER (0/1)BOOLEANBind through helpers, not inline db.kind branches.
Wall-clock timestampsTEXT + datetime('now')TIMESTAMPTZ + now()Normalize on read.
Millisecond epochs / countersINTEGERBIGINTUse for leases, expiry, monotonic counters, and retry timing.
JSON-heavy payloadsusually TEXTusually TEXTPrefer portability unless a proven Postgres-only query need exists.

Allowed divergence

The divergence list should stay short. Add a new dialect difference only when one of these is true:

Allowed reasonExample
Native type materially improves correctnessUUID, BOOLEAN, TIMESTAMPTZ on Postgres
SQLite portability would become meaningfully worse otherwiseJSON payloads kept as TEXT
The divergence can be centralized in the statestore layerhelper-based parameter binding and read normalization

Rules for implementation

  • Prefer parameterized SQL and DAL helpers over feature-level dialect branching.
  • Centralize differences under packages/gateway/src/statestore/.
  • Normalize timestamps on read through packages/gateway/src/utils/db-time.ts.
  • If a new divergence is introduced, document it here and keep the migration names aligned across both dialects.

Existing helper touchpoints

HelperPurpose
sqlBoolParam(db, value)Bind booleans without inline SQLite/Postgres branching.
sqlActiveWhereClause(db)Build dialect-safe watchers.active predicates.