Documentation
¶
Overview ¶
The sqlite package defines an extensible sqlite3 store for Nostr events.
Index ¶
- Variables
- type EventPolicy
- type FilterPolicy
- type Option
- func WithAdditionalSchema(schema string) Option
- func WithBusyTimeout(d time.Duration) Option
- func WithCountBuilder(b QueryBuilder) Option
- func WithEventPolicy(p EventPolicy) Option
- func WithFilterPolicy(p FilterPolicy) Option
- func WithOptimisationEvery(n int) Option
- func WithQueryBuilder(b QueryBuilder) Option
- type Query
- type QueryBuilder
- type Store
- func (s *Store) Close() error
- func (s *Store) Count(ctx context.Context, filters ...nostr.Filter) (int64, error)
- func (s *Store) CountWithBuilder(ctx context.Context, build QueryBuilder, filters ...nostr.Filter) (int64, error)
- func (s *Store) Delete(ctx context.Context, id string) (bool, error)
- func (s *Store) Optimize(ctx context.Context) error
- func (s *Store) Query(ctx context.Context, filters ...nostr.Filter) ([]nostr.Event, error)
- func (s *Store) QueryWithBuilder(ctx context.Context, build QueryBuilder, filters ...nostr.Filter) ([]nostr.Event, error)
- func (s *Store) Replace(ctx context.Context, event *nostr.Event) (bool, error)
- func (s *Store) Save(ctx context.Context, e *nostr.Event) (bool, error)
- func (s *Store) Size(ctx context.Context) (size int, err error)
Constants ¶
This section is empty.
Variables ¶
Functions ¶
This section is empty.
Types ¶
type EventPolicy ¶
EventPolicy validates a nostr event before it's written to the store.
type FilterPolicy ¶
FilterPolicy sanitizes a list of filters before building a query. It returns a potentially modified list and an error if the input is invalid.
type Option ¶
func WithAdditionalSchema ¶
WithAdditionalSchema allows to specify an additional database schema, like new tables, virtual tables, indexes and triggers.
func WithBusyTimeout ¶
WithBusyTimeout sets the SQLite PRAGMA busy_timeout. This allows concurrent operations to retry when the database is locked, up to the specified duration. The duration is internally converted to milliseconds, as required by SQLite.
func WithCountBuilder ¶
func WithCountBuilder(b QueryBuilder) Option
WithCountBuilder allows to specify the query builder used by the store in Store.Count.
func WithEventPolicy ¶
func WithEventPolicy(p EventPolicy) Option
WithEventPolicy sets a custom [nastro.EventPolicy] on the Store. It will be used to validate events before inserting them into the database.
func WithFilterPolicy ¶
func WithFilterPolicy(p FilterPolicy) Option
WithFilterPolicy sets a custom [nastro.FilterPolicy] on the Store. It will be used to validate and modify filters before executing queries.
func WithOptimisationEvery ¶
WithOptimisationEvery sets how many writes trigger a PRAGMA optimize operation.
func WithQueryBuilder ¶
func WithQueryBuilder(b QueryBuilder) Option
WithQueryBuilder allows to specify the query builder used by the store in Store.Query.
type QueryBuilder ¶
QueryBuilder converts multiple nostr filters into one or more sqlite queries and lists of arguments. Not all filters can be combined into a single query, but many can. Filters passed to the query builder have been previously validated by the [Store.filterPolicy].
It's useful to specify custom query/count builders to leverage additional schemas that have been provided in the New constructor.
For examples, check out the DefaultQueryBuilder and DefaultCountBuilder
type Store ¶
Store of Nostr events that uses an sqlite3 database. It embeds the *sql.DB connection for direct interaction and can use custom filter/event policies and query builders.
All methods are safe for concurrent use. However, due to the file-based architecture of SQLite, there can only be one writer at the time.
This limitation remains even after applying the recommended concurrency optimisations, such as journal_mode=WAL and PRAGMA busy_timeout=1s, which are applied by default in this implementation.
Therefore it remains possible that methods return the error [sqlite3.ErrBusy]. To reduce the likelihood of this happening, you can:
- increase the busy_timeout with the option WithBusyTimeout (default is 1s).
- provide synchronisation, for example with a mutex or channel(s). This however won't help if there are other programs writing to the same sqlite file.
If instead you want to handle all [sqlite3.ErrBusy] in your application, use WithBusyTimeout(0) to make blocked writers return immediatly.
More about WAL mode and concurrency: https://sqlite.org/wal.html
func New ¶
New returns an sqlite3 store connected to the sqlite file located at the provided file path, after applying the base schema, and the provided options.
func (*Store) Close ¶
Close the underlying database connection, committing all temporary data to disk.
func (*Store) CountWithBuilder ¶
func (s *Store) CountWithBuilder(ctx context.Context, build QueryBuilder, filters ...nostr.Filter) (int64, error)
CountWithBuilder generates an sqlite query for the filters with the provided QueryBuilder, and executes it.
func (*Store) Delete ¶
Delete the event with the provided id. If the event is not found, nothing happens and nil is returned. Delete returns true if the event was deleted, false in case of errors or if the event was never present.
func (*Store) Optimize ¶
Optimize runs "PRAGMA optimize", which updates the statistics and heuristics of the query planner, which should result in improved read performance. The Store's write methods call Optimize (roughly) every [Store.optimizeEvery] successful insertions or deletions.
func (*Store) QueryWithBuilder ¶
func (s *Store) QueryWithBuilder(ctx context.Context, build QueryBuilder, filters ...nostr.Filter) ([]nostr.Event, error)
QueryWithBuilder generates an sqlite query for the filters with the provided QueryBuilder, and executes it.
func (*Store) Replace ¶
Replace an old event with the new one according to NIP-01.
The replacement happens if the event is strictly newer than the stored event within the same 'category' (kind, pubkey, and d-tag if addressable). If no such stored event exists, and the event is a replaceable/addressable kind, it is simply saved.
Calling Replace on a non-replaceable/addressable event returns ErrInvalidReplacement
Replace returns true if the event has been saved/superseded a previous one, false in case of errors or if a stored event in the same 'category' is newer or equal.
More info here: https://github.com/nostr-protocol/nips/blob/master/01.md#kinds
func (*Store) Save ¶
Save the event in the store. Save is idempotent, meaning successful calls to Save with the same event are no-ops.
Save returns true if the event has been saved, false in case of errors or if the event was already present. For replaceable/addressable events, it is recommended to call Store.Replace instead.