forked from rqlite/rqlite
-
Notifications
You must be signed in to change notification settings - Fork 0
/
db.go
145 lines (127 loc) · 3.41 KB
/
db.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package db
import (
"database/sql"
"fmt"
"os"
"strings"
_ "github.com/mattn/go-sqlite3" // required blank import
"github.com/otoolep/rqlite/log"
)
// DB is the SQL database.
type DB struct {
dbConn *sql.DB
}
// RowResult is the result type.
type RowResult map[string]string
// RowResults is the list of results.
type RowResults []map[string]string
// New creates a new database. Deletes any existing database.
func New(dbPath string) *DB {
log.Tracef("Removing any existing SQLite database at %s", dbPath)
_ = os.Remove(dbPath)
return Open(dbPath)
}
// Open an existing database, creating it if it does not exist.
func Open(dbPath string) *DB {
log.Tracef("Opening SQLite database path at %s", dbPath)
dbc, err := sql.Open("sqlite3", dbPath)
if err != nil {
log.Error(err.Error())
return nil
}
return &DB{
dbConn: dbc,
}
}
// Close closes the underlying database connection.
func (db *DB) Close() error {
return db.dbConn.Close()
}
// Query runs the supplied query against the sqlite database. It returns a slice of
// RowResults.
func (db *DB) Query(query string) (RowResults, error) {
if !strings.HasPrefix(strings.ToUpper(query), "SELECT ") {
log.Warnf("Query \"%s\" may modify the database", query)
}
rows, err := db.dbConn.Query(query)
if err != nil {
log.Errorf("failed to execute SQLite query: %s", err.Error())
return nil, err
}
defer func() {
err = rows.Close()
if err != nil {
log.Errorf("failed to close rows: %s", err.Error())
}
}()
results := make(RowResults, 0)
columns, _ := rows.Columns()
rawResult := make([][]byte, len(columns))
dest := make([]interface{}, len(columns))
for i := range rawResult {
dest[i] = &rawResult[i] // Pointers to each string in the interface slice
}
for rows.Next() {
err = rows.Scan(dest...)
if err != nil {
log.Errorf("failed to scan SQLite row: %s", err.Error())
return nil, err
}
r := make(RowResult)
for i, raw := range rawResult {
if raw == nil {
r[columns[i]] = "null"
} else {
r[columns[i]] = string(raw)
}
}
results = append(results, r)
}
log.Debugf("Executed query successfully: %s", query)
return results, nil
}
// Execute executes the given sqlite statement, of a type that doesn't return rows.
func (db *DB) Execute(stmt string) error {
_, err := db.dbConn.Exec(stmt)
log.Debug(func() string {
if err != nil {
return fmt.Sprintf("Error executing \"%s\", error: %s", stmt, err.Error())
}
return fmt.Sprintf("Successfully executed \"%s\"", stmt)
}())
return err
}
// StartTransaction starts an explicit transaction.
func (db *DB) StartTransaction() error {
_, err := db.dbConn.Exec("BEGIN")
log.Debug(func() string {
if err != nil {
return "Error starting transaction"
}
return "Successfully started transaction"
}())
return err
}
// CommitTransaction commits all changes made since StartTraction was called.
func (db *DB) CommitTransaction() error {
_, err := db.dbConn.Exec("END")
log.Debug(func() string {
if err != nil {
return "Error ending transaction"
}
return "Successfully ended transaction"
}())
return err
}
// RollbackTransaction aborts the transaction. No statement issued since
// StartTransaction was called will take effect.
func (db *DB) RollbackTransaction() error {
_, err := db.dbConn.Exec("ROLLBACK")
log.Debug(func() string {
if err != nil {
return "Error rolling back transaction"
}
return "Successfully rolled back transaction"
}())
return err
}