func (d *ServiceDB) GetSQLDb() (*sql.DB, string) {
	return d.db, d.dialect
}
GetSQLDb retrieves the SQL database instance of a ServiceDB and the dialect it uses (sqlite3 or postgres).
package database
import (
	"database/sql"
	"encoding/json"
	"fmt"
	"time"
	"github.com/matrix-org/go-neb/api"
	"github.com/matrix-org/go-neb/types"
	"maunium.net/go/mautrix/id"
)
type ServiceDB struct {
	db      *sql.DB
	dialect string
}
var globalServiceDB Storer
func SetServiceDB(db Storer) {
	globalServiceDB = db
}
func GetServiceDB() Storer {
	return globalServiceDB
}
func Open(databaseType, databaseURL string) (serviceDB *ServiceDB, err error) {
	db, err := sql.Open(databaseType, databaseURL)
	if err != nil {
		return
	}
	if _, err = db.Exec(schemaSQL); err != nil {
		return
	}
	if databaseType == "sqlite3" {
		db.SetMaxOpenConns(1)
	}
	serviceDB = &ServiceDB{db: db, dialect: databaseType}
	return
}
func (d *ServiceDB) StoreMatrixClientConfig(config api.ClientConfig) (oldConfig api.ClientConfig, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		oldConfig, err = selectMatrixClientConfigTxn(txn, config.UserID)
		now := time.Now()
		if err == nil {
			return updateMatrixClientConfigTxn(txn, now, config)
		} else if err == sql.ErrNoRows {
			return insertMatrixClientConfigTxn(txn, now, config)
		} else {
			return err
		}
	})
	return
}
func (d *ServiceDB) LoadMatrixClientConfigs() (configs []api.ClientConfig, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		configs, err = selectMatrixClientConfigsTxn(txn)
		return err
	})
	return
}
func (d *ServiceDB) LoadMatrixClientConfig(userID id.UserID) (config api.ClientConfig, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		config, err = selectMatrixClientConfigTxn(txn, userID)
		return err
	})
	return
}
func (d *ServiceDB) UpdateNextBatch(userID id.UserID, nextBatch string) (err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		return updateNextBatchTxn(txn, userID, nextBatch)
	})
	return
}
func (d *ServiceDB) LoadNextBatch(userID id.UserID) (nextBatch string, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		nextBatch, err = selectNextBatchTxn(txn, userID)
		return err
	})
	return
}
func (d *ServiceDB) LoadService(serviceID string) (service types.Service, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		service, err = selectServiceTxn(txn, serviceID)
		return err
	})
	return
}
func (d *ServiceDB) DeleteService(serviceID string) (err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		return deleteServiceTxn(txn, serviceID)
	})
	return
}
func (d *ServiceDB) LoadServicesForUser(serviceUserID id.UserID) (services []types.Service, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		services, err = selectServicesForUserTxn(txn, serviceUserID)
		if err != nil {
			return err
		}
		return nil
	})
	return
}
func (d *ServiceDB) LoadServicesByType(serviceType string) (services []types.Service, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		services, err = selectServicesByTypeTxn(txn, serviceType)
		if err != nil {
			return err
		}
		return nil
	})
	return
}
func (d *ServiceDB) StoreService(service types.Service) (oldService types.Service, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		oldService, err = selectServiceTxn(txn, service.ServiceID())
		if err == sql.ErrNoRows {
			return insertServiceTxn(txn, time.Now(), service)
		} else if err != nil {
			return err
		} else {
			return updateServiceTxn(txn, time.Now(), service)
		}
	})
	return
}
func (d *ServiceDB) LoadAuthRealm(realmID string) (realm types.AuthRealm, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		realm, err = selectRealmTxn(txn, realmID)
		return err
	})
	return
}
func (d *ServiceDB) LoadAuthRealmsByType(realmType string) (realms []types.AuthRealm, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		realms, err = selectRealmsByTypeTxn(txn, realmType)
		return err
	})
	return
}
func (d *ServiceDB) StoreAuthRealm(realm types.AuthRealm) (old types.AuthRealm, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		old, err = selectRealmTxn(txn, realm.ID())
		if err == sql.ErrNoRows {
			return insertRealmTxn(txn, time.Now(), realm)
		} else if err != nil {
			return err
		} else {
			return updateRealmTxn(txn, time.Now(), realm)
		}
	})
	return
}
func (d *ServiceDB) StoreAuthSession(session types.AuthSession) (old types.AuthSession, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		old, err = selectAuthSessionByUserTxn(txn, session.RealmID(), session.UserID())
		if err == sql.ErrNoRows {
			return insertAuthSessionTxn(txn, time.Now(), session)
		} else if err != nil {
			return err
		} else {
			return updateAuthSessionTxn(txn, time.Now(), session)
		}
	})
	return
}
func (d *ServiceDB) RemoveAuthSession(realmID string, userID id.UserID) error {
	return runTransaction(d.db, func(txn *sql.Tx) error {
		return deleteAuthSessionTxn(txn, realmID, userID)
	})
}
func (d *ServiceDB) LoadAuthSessionByUser(realmID string, userID id.UserID) (session types.AuthSession, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		session, err = selectAuthSessionByUserTxn(txn, realmID, userID)
		return err
	})
	return
}
func (d *ServiceDB) LoadAuthSessionByID(realmID, sessionID string) (session types.AuthSession, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		session, err = selectAuthSessionByIDTxn(txn, realmID, sessionID)
		return err
	})
	return
}
func (d *ServiceDB) LoadBotOptions(userID id.UserID, roomID id.RoomID) (opts types.BotOptions, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		opts, err = selectBotOptionsTxn(txn, userID, roomID)
		return err
	})
	return
}
func (d *ServiceDB) StoreBotOptions(opts types.BotOptions) (oldOpts types.BotOptions, err error) {
	err = runTransaction(d.db, func(txn *sql.Tx) error {
		oldOpts, err = selectBotOptionsTxn(txn, opts.UserID, opts.RoomID)
		if err == sql.ErrNoRows {
			return insertBotOptionsTxn(txn, time.Now(), opts)
		} else if err != nil {
			return err
		} else {
			return updateBotOptionsTxn(txn, time.Now(), opts)
		}
	})
	return
}
func (d *ServiceDB) InsertFromConfig(cfg *api.ConfigFile) error {
	for _, cli := range cfg.Clients {
		if _, err := d.StoreMatrixClientConfig(cli); err != nil {
			return err
		}
	}
	realms := map[string]types.AuthRealm{}  
	for _, r := range cfg.Realms {
		if err := r.Check(); err != nil {
			return err
		}
		realm, err := types.CreateAuthRealm(r.ID, r.Type, r.Config)
		if err != nil {
			return err
		}
		if _, err := d.StoreAuthRealm(realm); err != nil {
			return err
		}
		realms[realm.ID()] = realm
	}
	for _, s := range cfg.Sessions {
		if err := s.Check(); err != nil {
			return err
		}
		r := realms[s.RealmID]
		if r == nil {
			return fmt.Errorf("Session %s specifies an unknown realm ID %s", s.SessionID, s.RealmID)
		}
		session := r.AuthSession(s.SessionID, s.UserID, s.RealmID)
		if err := json.Unmarshal(s.Config, session); err != nil {
			return err
		}
		if _, err := d.StoreAuthSession(session); err != nil {
			return err
		}
	}
	return nil
}