2026-04-04 08:46:59 +00:00
package store
import (
"database/sql"
2026-04-04 09:09:12 +00:00
"iter"
"text/template"
2026-04-04 08:46:59 +00:00
"time"
core "dappco.re/go/core"
)
2026-04-04 08:50:17 +00:00
// Usage example: `err := storeInstance.Transaction(func(transaction *store.StoreTransaction) error { return transaction.Set("config", "colour", "blue") })`
2026-04-04 14:23:29 +00:00
// Usage example: `if err := transaction.Delete("config", "colour"); err != nil { return err }`
2026-04-04 08:50:17 +00:00
type StoreTransaction struct {
2026-04-04 17:56:54 +00:00
storeInstance * Store
sqliteTransaction * sql . Tx
pendingEvents [ ] Event
2026-04-04 08:46:59 +00:00
}
2026-04-04 08:50:17 +00:00
// Usage example: `err := storeInstance.Transaction(func(transaction *store.StoreTransaction) error { if err := transaction.Set("tenant-a:config", "colour", "blue"); err != nil { return err }; return transaction.Set("tenant-b:config", "language", "en-GB") })`
func ( storeInstance * Store ) Transaction ( operation func ( * StoreTransaction ) error ) error {
2026-04-04 08:46:59 +00:00
if err := storeInstance . ensureReady ( "store.Transaction" ) ; err != nil {
return err
}
if operation == nil {
return core . E ( "store.Transaction" , "operation is nil" , nil )
}
2026-04-04 09:04:56 +00:00
transaction , err := storeInstance . sqliteDatabase . Begin ( )
2026-04-04 08:46:59 +00:00
if err != nil {
return core . E ( "store.Transaction" , "begin transaction" , err )
}
2026-04-04 08:50:17 +00:00
storeTransaction := & StoreTransaction {
2026-04-04 17:56:54 +00:00
storeInstance : storeInstance ,
sqliteTransaction : transaction ,
2026-04-04 08:46:59 +00:00
}
committed := false
defer func ( ) {
if ! committed {
_ = transaction . Rollback ( )
}
} ( )
if err := operation ( storeTransaction ) ; err != nil {
return core . E ( "store.Transaction" , "execute transaction" , err )
}
if err := transaction . Commit ( ) ; err != nil {
return core . E ( "store.Transaction" , "commit transaction" , err )
}
committed = true
for _ , event := range storeTransaction . pendingEvents {
storeInstance . notify ( event )
}
return nil
}
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) ensureReady ( operation string ) error {
2026-04-04 08:46:59 +00:00
if storeTransaction == nil {
return core . E ( operation , "transaction is nil" , nil )
}
2026-04-04 17:56:54 +00:00
if storeTransaction . storeInstance == nil {
2026-04-04 08:46:59 +00:00
return core . E ( operation , "transaction store is nil" , nil )
}
2026-04-04 17:56:54 +00:00
if storeTransaction . sqliteTransaction == nil {
2026-04-04 08:46:59 +00:00
return core . E ( operation , "transaction database is nil" , nil )
}
2026-04-04 17:56:54 +00:00
if err := storeTransaction . storeInstance . ensureReady ( operation ) ; err != nil {
2026-04-04 08:46:59 +00:00
return err
}
return nil
}
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) recordEvent ( event Event ) {
2026-04-04 08:46:59 +00:00
if storeTransaction == nil {
return
}
storeTransaction . pendingEvents = append ( storeTransaction . pendingEvents , event )
}
2026-04-05 08:58:26 +01:00
// Usage example: `exists, err := transaction.Exists("config", "colour")`
// Usage example: `if exists, _ := transaction.Exists("session", "token"); !exists { return core.E("auth", "session expired", nil) }`
func ( storeTransaction * StoreTransaction ) Exists ( group , key string ) ( bool , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.Exists" ) ; err != nil {
return false , err
}
return liveEntryExists ( storeTransaction . sqliteTransaction , group , key )
}
// Usage example: `exists, err := transaction.GroupExists("config")`
func ( storeTransaction * StoreTransaction ) GroupExists ( group string ) ( bool , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.GroupExists" ) ; err != nil {
return false , err
}
count , err := storeTransaction . Count ( group )
if err != nil {
return false , err
}
return count > 0 , nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `value, err := transaction.Get("config", "colour")`
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) Get ( group , key string ) ( string , error ) {
2026-04-04 08:46:59 +00:00
if err := storeTransaction . ensureReady ( "store.Transaction.Get" ) ; err != nil {
return "" , err
}
var value string
var expiresAt sql . NullInt64
2026-04-04 17:56:54 +00:00
err := storeTransaction . sqliteTransaction . QueryRow (
2026-04-04 08:46:59 +00:00
"SELECT " + entryValueColumn + ", expires_at FROM " + entriesTableName + " WHERE " + entryGroupColumn + " = ? AND " + entryKeyColumn + " = ?" ,
group , key ,
) . Scan ( & value , & expiresAt )
if err == sql . ErrNoRows {
return "" , core . E ( "store.Transaction.Get" , core . Concat ( group , "/" , key ) , NotFoundError )
}
if err != nil {
return "" , core . E ( "store.Transaction.Get" , "query row" , err )
}
if expiresAt . Valid && expiresAt . Int64 <= time . Now ( ) . UnixMilli ( ) {
if err := storeTransaction . Delete ( group , key ) ; err != nil {
return "" , core . E ( "store.Transaction.Get" , "delete expired row" , err )
}
return "" , core . E ( "store.Transaction.Get" , core . Concat ( group , "/" , key ) , NotFoundError )
}
return value , nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `if err := transaction.Set("config", "colour", "blue"); err != nil { return err }`
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) Set ( group , key , value string ) error {
2026-04-04 08:46:59 +00:00
if err := storeTransaction . ensureReady ( "store.Transaction.Set" ) ; err != nil {
return err
}
2026-04-04 17:56:54 +00:00
_ , err := storeTransaction . sqliteTransaction . Exec (
2026-04-04 08:46:59 +00:00
"INSERT INTO " + entriesTableName + " (" + entryGroupColumn + ", " + entryKeyColumn + ", " + entryValueColumn + ", expires_at) VALUES (?, ?, ?, NULL) " +
"ON CONFLICT(" + entryGroupColumn + ", " + entryKeyColumn + ") DO UPDATE SET " + entryValueColumn + " = excluded." + entryValueColumn + ", expires_at = NULL" ,
group , key , value ,
)
if err != nil {
return core . E ( "store.Transaction.Set" , "execute upsert" , err )
}
storeTransaction . recordEvent ( Event { Type : EventSet , Group : group , Key : key , Value : value , Timestamp : time . Now ( ) } )
return nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `if err := transaction.SetWithTTL("session", "token", "abc123", time.Minute); err != nil { return err }`
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) SetWithTTL ( group , key , value string , timeToLive time . Duration ) error {
2026-04-04 08:46:59 +00:00
if err := storeTransaction . ensureReady ( "store.Transaction.SetWithTTL" ) ; err != nil {
return err
}
expiresAt := time . Now ( ) . Add ( timeToLive ) . UnixMilli ( )
2026-04-04 17:56:54 +00:00
_ , err := storeTransaction . sqliteTransaction . Exec (
2026-04-04 08:46:59 +00:00
"INSERT INTO " + entriesTableName + " (" + entryGroupColumn + ", " + entryKeyColumn + ", " + entryValueColumn + ", expires_at) VALUES (?, ?, ?, ?) " +
"ON CONFLICT(" + entryGroupColumn + ", " + entryKeyColumn + ") DO UPDATE SET " + entryValueColumn + " = excluded." + entryValueColumn + ", expires_at = excluded.expires_at" ,
group , key , value , expiresAt ,
)
if err != nil {
return core . E ( "store.Transaction.SetWithTTL" , "execute upsert with expiry" , err )
}
storeTransaction . recordEvent ( Event { Type : EventSet , Group : group , Key : key , Value : value , Timestamp : time . Now ( ) } )
return nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `if err := transaction.Delete("config", "colour"); err != nil { return err }`
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) Delete ( group , key string ) error {
2026-04-04 08:46:59 +00:00
if err := storeTransaction . ensureReady ( "store.Transaction.Delete" ) ; err != nil {
return err
}
2026-04-04 17:56:54 +00:00
deleteResult , err := storeTransaction . sqliteTransaction . Exec (
2026-04-04 08:46:59 +00:00
"DELETE FROM " + entriesTableName + " WHERE " + entryGroupColumn + " = ? AND " + entryKeyColumn + " = ?" ,
group , key ,
)
if err != nil {
return core . E ( "store.Transaction.Delete" , "delete row" , err )
}
deletedRows , rowsAffectedError := deleteResult . RowsAffected ( )
if rowsAffectedError != nil {
return core . E ( "store.Transaction.Delete" , "count deleted rows" , rowsAffectedError )
}
if deletedRows > 0 {
storeTransaction . recordEvent ( Event { Type : EventDelete , Group : group , Key : key , Timestamp : time . Now ( ) } )
}
return nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `if err := transaction.DeleteGroup("cache"); err != nil { return err }`
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) DeleteGroup ( group string ) error {
2026-04-04 08:46:59 +00:00
if err := storeTransaction . ensureReady ( "store.Transaction.DeleteGroup" ) ; err != nil {
return err
}
2026-04-04 17:56:54 +00:00
deleteResult , err := storeTransaction . sqliteTransaction . Exec (
2026-04-04 08:46:59 +00:00
"DELETE FROM " + entriesTableName + " WHERE " + entryGroupColumn + " = ?" ,
group ,
)
if err != nil {
return core . E ( "store.Transaction.DeleteGroup" , "delete group" , err )
}
deletedRows , rowsAffectedError := deleteResult . RowsAffected ( )
if rowsAffectedError != nil {
return core . E ( "store.Transaction.DeleteGroup" , "count deleted rows" , rowsAffectedError )
}
if deletedRows > 0 {
storeTransaction . recordEvent ( Event { Type : EventDeleteGroup , Group : group , Timestamp : time . Now ( ) } )
}
return nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `if err := transaction.DeletePrefix("tenant-a:"); err != nil { return err }`
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) DeletePrefix ( groupPrefix string ) error {
2026-04-04 08:46:59 +00:00
if err := storeTransaction . ensureReady ( "store.Transaction.DeletePrefix" ) ; err != nil {
return err
}
var rows * sql . Rows
var err error
if groupPrefix == "" {
2026-04-04 17:56:54 +00:00
rows , err = storeTransaction . sqliteTransaction . Query (
2026-04-04 08:46:59 +00:00
"SELECT DISTINCT " + entryGroupColumn + " FROM " + entriesTableName + " ORDER BY " + entryGroupColumn ,
)
} else {
2026-04-04 17:56:54 +00:00
rows , err = storeTransaction . sqliteTransaction . Query (
2026-04-04 08:46:59 +00:00
"SELECT DISTINCT " + entryGroupColumn + " FROM " + entriesTableName + " WHERE " + entryGroupColumn + " LIKE ? ESCAPE '^' ORDER BY " + entryGroupColumn ,
escapeLike ( groupPrefix ) + "%" ,
)
}
if err != nil {
return core . E ( "store.Transaction.DeletePrefix" , "list groups" , err )
}
defer rows . Close ( )
var groupNames [ ] string
for rows . Next ( ) {
var groupName string
if err := rows . Scan ( & groupName ) ; err != nil {
return core . E ( "store.Transaction.DeletePrefix" , "scan group name" , err )
}
groupNames = append ( groupNames , groupName )
}
if err := rows . Err ( ) ; err != nil {
return core . E ( "store.Transaction.DeletePrefix" , "iterate groups" , err )
}
for _ , groupName := range groupNames {
if err := storeTransaction . DeleteGroup ( groupName ) ; err != nil {
return core . E ( "store.Transaction.DeletePrefix" , "delete group" , err )
}
}
return nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `keyCount, err := transaction.Count("config")`
2026-04-04 08:50:17 +00:00
func ( storeTransaction * StoreTransaction ) Count ( group string ) ( int , error ) {
2026-04-04 08:46:59 +00:00
if err := storeTransaction . ensureReady ( "store.Transaction.Count" ) ; err != nil {
return 0 , err
}
var count int
2026-04-04 17:56:54 +00:00
err := storeTransaction . sqliteTransaction . QueryRow (
2026-04-04 08:46:59 +00:00
"SELECT COUNT(*) FROM " + entriesTableName + " WHERE " + entryGroupColumn + " = ? AND (expires_at IS NULL OR expires_at > ?)" ,
group , time . Now ( ) . UnixMilli ( ) ,
) . Scan ( & count )
if err != nil {
return 0 , core . E ( "store.Transaction.Count" , "count rows" , err )
}
return count , nil
}
2026-04-04 09:09:12 +00:00
2026-04-04 11:09:35 +00:00
// Usage example: `colourEntries, err := transaction.GetAll("config")`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) GetAll ( group string ) ( map [ string ] string , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.GetAll" ) ; err != nil {
return nil , err
}
entriesByKey := make ( map [ string ] string )
for entry , err := range storeTransaction . All ( group ) {
if err != nil {
return nil , core . E ( "store.Transaction.GetAll" , "iterate rows" , err )
}
entriesByKey [ entry . Key ] = entry . Value
}
return entriesByKey , nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `page, err := transaction.GetPage("config", 0, 25); if err != nil { return }; for _, entry := range page { fmt.Println(entry.Key, entry.Value) }`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) GetPage ( group string , offset , limit int ) ( [ ] KeyValue , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.GetPage" ) ; err != nil {
return nil , err
}
if offset < 0 {
return nil , core . E ( "store.Transaction.GetPage" , "offset must be zero or positive" , nil )
}
if limit < 0 {
return nil , core . E ( "store.Transaction.GetPage" , "limit must be zero or positive" , nil )
}
2026-04-04 17:56:54 +00:00
rows , err := storeTransaction . sqliteTransaction . Query (
2026-04-04 09:09:12 +00:00
"SELECT " + entryKeyColumn + ", " + entryValueColumn + " FROM " + entriesTableName + " WHERE " + entryGroupColumn + " = ? AND (expires_at IS NULL OR expires_at > ?) ORDER BY " + entryKeyColumn + " LIMIT ? OFFSET ?" ,
group , time . Now ( ) . UnixMilli ( ) , limit , offset ,
)
if err != nil {
return nil , core . E ( "store.Transaction.GetPage" , "query rows" , err )
}
defer rows . Close ( )
page := make ( [ ] KeyValue , 0 , limit )
for rows . Next ( ) {
var entry KeyValue
if err := rows . Scan ( & entry . Key , & entry . Value ) ; err != nil {
return nil , core . E ( "store.Transaction.GetPage" , "scan row" , err )
}
page = append ( page , entry )
}
if err := rows . Err ( ) ; err != nil {
return nil , core . E ( "store.Transaction.GetPage" , "rows iteration" , err )
}
return page , nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `for entry, err := range transaction.All("config") { if err != nil { break }; fmt.Println(entry.Key, entry.Value) }`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) All ( group string ) iter . Seq2 [ KeyValue , error ] {
return storeTransaction . AllSeq ( group )
}
2026-04-04 11:09:35 +00:00
// Usage example: `for entry, err := range transaction.AllSeq("config") { if err != nil { break }; fmt.Println(entry.Key, entry.Value) }`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) AllSeq ( group string ) iter . Seq2 [ KeyValue , error ] {
return func ( yield func ( KeyValue , error ) bool ) {
if err := storeTransaction . ensureReady ( "store.Transaction.All" ) ; err != nil {
yield ( KeyValue { } , err )
return
}
2026-04-04 17:56:54 +00:00
rows , err := storeTransaction . sqliteTransaction . Query (
2026-04-04 09:09:12 +00:00
"SELECT " + entryKeyColumn + ", " + entryValueColumn + " FROM " + entriesTableName + " WHERE " + entryGroupColumn + " = ? AND (expires_at IS NULL OR expires_at > ?) ORDER BY " + entryKeyColumn ,
group , time . Now ( ) . UnixMilli ( ) ,
)
if err != nil {
yield ( KeyValue { } , core . E ( "store.Transaction.All" , "query rows" , err ) )
return
}
defer rows . Close ( )
for rows . Next ( ) {
var entry KeyValue
if err := rows . Scan ( & entry . Key , & entry . Value ) ; err != nil {
if ! yield ( KeyValue { } , core . E ( "store.Transaction.All" , "scan row" , err ) ) {
return
}
continue
}
if ! yield ( entry , nil ) {
return
}
}
if err := rows . Err ( ) ; err != nil {
yield ( KeyValue { } , core . E ( "store.Transaction.All" , "rows iteration" , err ) )
}
}
}
2026-04-04 11:09:35 +00:00
// Usage example: `removedRows, err := transaction.CountAll("tenant-a:")`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) CountAll ( groupPrefix string ) ( int , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.CountAll" ) ; err != nil {
return 0 , err
}
var count int
var err error
if groupPrefix == "" {
2026-04-04 17:56:54 +00:00
err = storeTransaction . sqliteTransaction . QueryRow (
2026-04-04 09:09:12 +00:00
"SELECT COUNT(*) FROM " + entriesTableName + " WHERE (expires_at IS NULL OR expires_at > ?)" ,
time . Now ( ) . UnixMilli ( ) ,
) . Scan ( & count )
} else {
2026-04-04 17:56:54 +00:00
err = storeTransaction . sqliteTransaction . QueryRow (
2026-04-04 09:09:12 +00:00
"SELECT COUNT(*) FROM " + entriesTableName + " WHERE " + entryGroupColumn + " LIKE ? ESCAPE '^' AND (expires_at IS NULL OR expires_at > ?)" ,
escapeLike ( groupPrefix ) + "%" , time . Now ( ) . UnixMilli ( ) ,
) . Scan ( & count )
}
if err != nil {
return 0 , core . E ( "store.Transaction.CountAll" , "count rows" , err )
}
return count , nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `groupNames, err := transaction.Groups("tenant-a:")`
// Usage example: `groupNames, err := transaction.Groups()`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) Groups ( groupPrefix ... string ) ( [ ] string , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.Groups" ) ; err != nil {
return nil , err
}
var groupNames [ ] string
for groupName , err := range storeTransaction . GroupsSeq ( groupPrefix ... ) {
if err != nil {
return nil , err
}
groupNames = append ( groupNames , groupName )
}
return groupNames , nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `for groupName, err := range transaction.GroupsSeq("tenant-a:") { if err != nil { break }; fmt.Println(groupName) }`
// Usage example: `for groupName, err := range transaction.GroupsSeq() { if err != nil { break }; fmt.Println(groupName) }`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) GroupsSeq ( groupPrefix ... string ) iter . Seq2 [ string , error ] {
2026-04-04 21:29:27 +00:00
actualGroupPrefix := firstStringOrEmpty ( groupPrefix )
2026-04-04 09:09:12 +00:00
return func ( yield func ( string , error ) bool ) {
if err := storeTransaction . ensureReady ( "store.Transaction.GroupsSeq" ) ; err != nil {
yield ( "" , err )
return
}
var rows * sql . Rows
var err error
now := time . Now ( ) . UnixMilli ( )
if actualGroupPrefix == "" {
2026-04-04 17:56:54 +00:00
rows , err = storeTransaction . sqliteTransaction . Query (
2026-04-04 09:09:12 +00:00
"SELECT DISTINCT " + entryGroupColumn + " FROM " + entriesTableName + " WHERE (expires_at IS NULL OR expires_at > ?) ORDER BY " + entryGroupColumn ,
now ,
)
} else {
2026-04-04 17:56:54 +00:00
rows , err = storeTransaction . sqliteTransaction . Query (
2026-04-04 09:09:12 +00:00
"SELECT DISTINCT " + entryGroupColumn + " FROM " + entriesTableName + " WHERE " + entryGroupColumn + " LIKE ? ESCAPE '^' AND (expires_at IS NULL OR expires_at > ?) ORDER BY " + entryGroupColumn ,
escapeLike ( actualGroupPrefix ) + "%" , now ,
)
}
if err != nil {
yield ( "" , core . E ( "store.Transaction.GroupsSeq" , "query group names" , err ) )
return
}
defer rows . Close ( )
for rows . Next ( ) {
var groupName string
if err := rows . Scan ( & groupName ) ; err != nil {
if ! yield ( "" , core . E ( "store.Transaction.GroupsSeq" , "scan group name" , err ) ) {
return
}
continue
}
if ! yield ( groupName , nil ) {
return
}
}
if err := rows . Err ( ) ; err != nil {
yield ( "" , core . E ( "store.Transaction.GroupsSeq" , "rows iteration" , err ) )
}
}
}
2026-04-04 11:09:35 +00:00
// Usage example: `renderedTemplate, err := transaction.Render("Hello {{ .name }}", "user")`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) Render ( templateSource , group string ) ( string , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.Render" ) ; err != nil {
return "" , err
}
templateData := make ( map [ string ] string )
for entry , err := range storeTransaction . All ( group ) {
if err != nil {
return "" , core . E ( "store.Transaction.Render" , "iterate rows" , err )
}
templateData [ entry . Key ] = entry . Value
}
renderTemplate , err := template . New ( "render" ) . Parse ( templateSource )
if err != nil {
return "" , core . E ( "store.Transaction.Render" , "parse template" , err )
}
builder := core . NewBuilder ( )
if err := renderTemplate . Execute ( builder , templateData ) ; err != nil {
return "" , core . E ( "store.Transaction.Render" , "execute template" , err )
}
return builder . String ( ) , nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `parts, err := transaction.GetSplit("config", "hosts", ","); if err != nil { return }; for part := range parts { fmt.Println(part) }`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) GetSplit ( group , key , separator string ) ( iter . Seq [ string ] , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.GetSplit" ) ; err != nil {
return nil , err
}
value , err := storeTransaction . Get ( group , key )
if err != nil {
return nil , err
}
return splitValueSeq ( value , separator ) , nil
}
2026-04-04 11:09:35 +00:00
// Usage example: `fields, err := transaction.GetFields("config", "flags"); if err != nil { return }; for field := range fields { fmt.Println(field) }`
2026-04-04 09:09:12 +00:00
func ( storeTransaction * StoreTransaction ) GetFields ( group , key string ) ( iter . Seq [ string ] , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.GetFields" ) ; err != nil {
return nil , err
}
value , err := storeTransaction . Get ( group , key )
if err != nil {
return nil , err
}
return fieldsValueSeq ( value ) , nil
}
2026-04-04 18:20:52 +00:00
// Usage example: `removedRows, err := transaction.PurgeExpired(); if err != nil { return err }; fmt.Println(removedRows)`
func ( storeTransaction * StoreTransaction ) PurgeExpired ( ) ( int64 , error ) {
if err := storeTransaction . ensureReady ( "store.Transaction.PurgeExpired" ) ; err != nil {
return 0 , err
}
removedRows , err := purgeExpiredMatchingGroupPrefix ( storeTransaction . sqliteTransaction , "" )
if err != nil {
return 0 , core . E ( "store.Transaction.PurgeExpired" , "delete expired rows" , err )
}
return removedRows , nil
}