-
Notifications
You must be signed in to change notification settings - Fork 13
/
pragma.go
158 lines (131 loc) · 3.21 KB
/
pragma.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
146
147
148
149
150
151
152
153
154
155
156
157
158
package sqlite3
import (
"context"
"database/sql"
"fmt"
"strings"
"github.com/neilotoole/sq/libsq/core/debugz"
"github.com/neilotoole/sq/libsq/core/errz"
"github.com/neilotoole/sq/libsq/core/lg"
"github.com/neilotoole/sq/libsq/core/progress"
"github.com/neilotoole/sq/libsq/core/sqlz"
)
// getDBProperties returns a map of the DB's settings, as exposed
// via SQLite's pragma mechanism. The supplied incr func should
// be invoked for each row read from the DB.
//
// See: https://www.sqlite.org/pragma.html
func getDBProperties(ctx context.Context, db sqlz.DB) (map[string]any, error) {
pragmas, err := listPragmaNames(ctx, db)
if err != nil {
return nil, err
}
m := make(map[string]any, len(pragmas))
for _, pragma := range pragmas {
var val any
val, err = readPragma(ctx, db, pragma)
if err != nil {
return nil, errz.Wrapf(errw(err), "read pragma: %s", pragma)
}
progress.Incr(ctx, 1)
debugz.DebugSleep(ctx)
if val != nil {
m[pragma] = val
}
}
return m, nil
}
// readPragma reads the values of pragma from the DB,and returns its value,
// which is either a scalar value such as a string, or a map[string]any.
func readPragma(ctx context.Context, db sqlz.DB, pragma string) (any, error) {
var (
q = fmt.Sprintf(`SELECT * FROM "pragma_%s"`, pragma)
rows *sql.Rows
err error
)
if rows, err = db.QueryContext(ctx, q); err != nil {
if strings.HasPrefix(err.Error(), "no such table") {
// Some of the pragmas can't be selected from. Ignore these.
// SQLite returns a generic (1) SQLITE_ERROR in this case,
// so we match using the error string.
return nil, nil //nolint:nilnil
}
return nil, errw(err)
}
defer sqlz.CloseRows(lg.FromContext(ctx), rows)
if !rows.Next() {
return nil, nil //nolint:nilnil
}
cols, err := rows.Columns()
if err != nil {
return nil, errw(err)
}
switch len(cols) {
case 0:
// Shouldn't happen
return nil, nil //nolint:nilnil
case 1:
var val any
if err = rows.Scan(&val); err != nil {
return nil, errw(err)
}
return val, nil
default:
// continue below
}
arr := make([]any, 0)
for {
vals := make([]any, len(cols))
for i := range vals {
vals[i] = new(any)
}
if err = rows.Scan(vals...); err != nil {
return nil, errw(err)
}
m := map[string]any{}
for i := range cols {
v := vals[i]
switch v := v.(type) {
case nil:
m[cols[i]] = nil
case *any:
if v == nil {
m[cols[i]] = nil
} else {
m[cols[i]] = *v
}
default:
m[cols[i]] = vals[i]
}
}
arr = append(arr, m)
if !rows.Next() {
break
}
}
return arr, nil
}
// listPragmaNames lists the pragmas from pragma_pragma_list.
// See: https://www.sqlite.org/pragma.html#pragma_pragma_list
func listPragmaNames(ctx context.Context, db sqlz.DB) ([]string, error) {
const qPragmas = `SELECT name FROM pragma_pragma_list ORDER BY name`
rows, err := db.QueryContext(ctx, qPragmas)
if err != nil {
return nil, errw(err)
}
defer sqlz.CloseRows(lg.FromContext(ctx), rows)
var (
names []string
name string
)
for rows.Next() {
if err = rows.Scan(&name); err != nil {
return nil, errw(err)
}
names = append(names, name)
}
if err = rows.Err(); err != nil {
return nil, errw(err)
}
return names, nil
}