/ kvdb / sqlite / db.go
db.go
 1  //go:build kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))
 2  
 3  package sqlite
 4  
 5  import (
 6  	"context"
 7  	"fmt"
 8  	"net/url"
 9  	"path/filepath"
10  
11  	"github.com/btcsuite/btcwallet/walletdb"
12  	"github.com/lightningnetwork/lnd/kvdb/sqlbase"
13  	_ "modernc.org/sqlite" // Register relevant drivers.
14  )
15  
16  const (
17  	// sqliteOptionPrefix is the string prefix sqlite uses to set various
18  	// options. This is used in the following format:
19  	//   * sqliteOptionPrefix || option_name = option_value.
20  	sqliteOptionPrefix = "_pragma"
21  
22  	// sqliteTxLockImmediate is a dsn option used to ensure that write
23  	// transactions are started immediately.
24  	sqliteTxLockImmediate = "_txlock=immediate"
25  )
26  
27  // pragmaOption holds a key-value pair for a SQLite pragma setting.
28  type pragmaOption struct {
29  	name  string
30  	value string
31  }
32  
33  // NewSqliteBackend returns a db object initialized with the passed backend
34  // config. If a sqlite connection cannot be established, then an error is
35  // returned.
36  func NewSqliteBackend(ctx context.Context, cfg *Config, dbPath, fileName,
37  	prefix string) (walletdb.DB, error) {
38  
39  	// First, we add a set of mandatory pragma options to the query.
40  	pragmaOptions := []pragmaOption{
41  		{
42  			name: "busy_timeout",
43  			value: fmt.Sprintf(
44  				"%d", cfg.BusyTimeout.Milliseconds(),
45  			),
46  		},
47  		{
48  			name:  "foreign_keys",
49  			value: "on",
50  		},
51  		{
52  			name:  "journal_mode",
53  			value: "WAL",
54  		},
55  		{
56  			name:  "auto_vacuum",
57  			value: "incremental",
58  		},
59  	}
60  
61  	sqliteOptions := make(url.Values)
62  	for _, option := range pragmaOptions {
63  		sqliteOptions.Add(
64  			sqliteOptionPrefix,
65  			fmt.Sprintf("%v=%v", option.name, option.value),
66  		)
67  	}
68  
69  	// Then we add any user specified pragma options. Note that these can
70  	// be of the form: "key=value", "key(N)" or "key".
71  	for _, option := range cfg.PragmaOptions {
72  		sqliteOptions.Add(sqliteOptionPrefix, option)
73  	}
74  
75  	// Construct the DSN which is just the database file name, appended
76  	// with the series of pragma options as a query URL string. For more
77  	// details on the formatting here, see the modernc.org/sqlite docs:
78  	// https://pkg.go.dev/modernc.org/sqlite#Driver.Open.
79  	dsn := fmt.Sprintf(
80  		"%v?%v&%v", filepath.Join(dbPath, fileName),
81  		sqliteOptions.Encode(), sqliteTxLockImmediate,
82  	)
83  	sqlCfg := &sqlbase.Config{
84  		DriverName:      "sqlite",
85  		Dsn:             dsn,
86  		Timeout:         cfg.Timeout,
87  		TableNamePrefix: prefix,
88  	}
89  
90  	return sqlbase.NewSqlBackend(ctx, sqlCfg)
91  }