forked from canonical/lxd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
slices.go
148 lines (123 loc) · 3.33 KB
/
slices.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
package query
import (
"database/sql"
"fmt"
"strings"
"github.com/pkg/errors"
)
// SelectURIs returns a list of LXD API URI strings for the resource yielded by
// the given query.
//
// The f argument must be a function that formats the entity URI using the
// columns yielded by the query.
func SelectURIs(stmt *sql.Stmt, f func(a ...interface{}) string, args ...interface{}) ([]string, error) {
rows, err := stmt.Query(args...)
if err != nil {
return nil, errors.Wrapf(err, "Failed to query URIs")
}
defer rows.Close()
columns, err := rows.Columns()
if err != nil {
return nil, errors.Wrap(err, "Rows columns")
}
params := make([]interface{}, len(columns))
dest := make([]interface{}, len(params))
for i := range params {
params[i] = ""
dest[i] = ¶ms[i]
}
uris := []string{}
for rows.Next() {
err := rows.Scan(dest...)
if err != nil {
return nil, errors.Wrapf(err, "Failed to scan URI params")
}
uri := f(params...)
uris = append(uris, uri)
}
err = rows.Err()
if err != nil {
return nil, errors.Wrapf(err, "Failed to close URI result set")
}
return uris, nil
}
// SelectStrings executes a statement which must yield rows with a single string
// column. It returns the list of column values.
func SelectStrings(tx *sql.Tx, query string, args ...interface{}) ([]string, error) {
values := []string{}
scan := func(rows *sql.Rows) error {
var value string
err := rows.Scan(&value)
if err != nil {
return err
}
values = append(values, value)
return nil
}
err := scanSingleColumn(tx, query, args, "TEXT", scan)
if err != nil {
return nil, err
}
return values, nil
}
// SelectIntegers executes a statement which must yield rows with a single integer
// column. It returns the list of column values.
func SelectIntegers(tx *sql.Tx, query string, args ...interface{}) ([]int, error) {
values := []int{}
scan := func(rows *sql.Rows) error {
var value int
err := rows.Scan(&value)
if err != nil {
return err
}
values = append(values, value)
return nil
}
err := scanSingleColumn(tx, query, args, "INTEGER", scan)
if err != nil {
return nil, err
}
return values, nil
}
// InsertStrings inserts a new row for each of the given strings, using the
// given insert statement template, which must define exactly one insertion
// column and one substitution placeholder for the values. For example:
// InsertStrings(tx, "INSERT INTO foo(name) VALUES %s", []string{"bar"}).
func InsertStrings(tx *sql.Tx, stmt string, values []string) error {
n := len(values)
if n == 0 {
return nil
}
params := make([]string, n)
args := make([]interface{}, n)
for i, value := range values {
params[i] = "(?)"
args[i] = value
}
stmt = fmt.Sprintf(stmt, strings.Join(params, ", "))
_, err := tx.Exec(stmt, args...)
return err
}
// Execute the given query and ensure that it yields rows with a single column
// of the given database type. For every row yielded, execute the given
// scanner.
func scanSingleColumn(tx *sql.Tx, query string, args []interface{}, typeName string, scan scanFunc) error {
rows, err := tx.Query(query, args...)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
err := scan(rows)
if err != nil {
return err
}
}
err = rows.Err()
if err != nil {
return err
}
return nil
}
// Function to scan a single row.
type scanFunc func(*sql.Rows) error