package sqlstore import ( "context" "database/sql" "fmt" "github.com/dariuszjaniak/warp/server/internal/storage" ) // AppServices is the shared implementation of storage.AppServiceStore. type AppServices struct { db *sql.DB rebind func(string) string } // NewAppServices builds an application service store. func NewAppServices(db *sql.DB, rebind func(string) string) *AppServices { if rebind == nil { rebind = func(query string) string { return query } } return &AppServices{db: db, rebind: rebind} } func (s *AppServices) q(query string) string { return s.rebind(query) } const ( insertAppServiceStateSQL = ` INSERT INTO appservice_state (as_id, stream_pos) VALUES (?, ?) ON CONFLICT (as_id) DO NOTHING` selectAppServiceStateSQL = ` SELECT as_id, stream_pos, txn_id, txn_to FROM appservice_state WHERE as_id = ?` beginAppServiceTxnSQL = `UPDATE appservice_state SET txn_id = ?, txn_to = ? WHERE as_id = ?` completeAppServiceTxnSQL = `UPDATE appservice_state SET stream_pos = ?, txn_to = 0 WHERE as_id = ?` ) // AppServiceState implements storage.AppServiceStore. func (s *AppServices) AppServiceState(ctx context.Context, id string, startPos int64) (storage.AppServiceState, error) { if _, err := s.db.ExecContext(ctx, s.q(insertAppServiceStateSQL), id, startPos); err != nil { return storage.AppServiceState{}, fmt.Errorf("start the cursor of application service %q: %w", id, err) } var st storage.AppServiceState if err := s.db.QueryRowContext(ctx, s.q(selectAppServiceStateSQL), id). Scan(&st.ID, &st.StreamPos, &st.TxnID, &st.TxnTo); err != nil { return storage.AppServiceState{}, fmt.Errorf("read the cursor of application service %q: %w", id, err) } return st, nil } // BeginTransaction implements storage.AppServiceStore. func (s *AppServices) BeginTransaction(ctx context.Context, id string, txnID, to int64) error { if _, err := s.db.ExecContext(ctx, s.q(beginAppServiceTxnSQL), txnID, to, id); err != nil { return fmt.Errorf("begin transaction %d of application service %q: %w", txnID, id, err) } return nil } // CompleteTransaction implements storage.AppServiceStore. func (s *AppServices) CompleteTransaction(ctx context.Context, id string, to int64) error { if _, err := s.db.ExecContext(ctx, s.q(completeAppServiceTxnSQL), to, id); err != nil { return fmt.Errorf("advance the cursor of application service %q: %w", id, err) } return nil } // Compile-time proof that the shared implementation satisfies the interface. var _ storage.AppServiceStore = (*AppServices)(nil)