Browse code

Add sqlite as graphdb backend

Add build steps to compile docker statically
with CGO enabled

Michael Crosby authored on 2013/10/01 12:13:51
Showing 11 changed files
... ...
@@ -33,16 +33,14 @@ run	apt-get update
33 33
 run	apt-get install -y -q curl
34 34
 run	apt-get install -y -q git
35 35
 run	apt-get install -y -q mercurial
36
-run	apt-get install -y -q build-essential
36
+run apt-get install -y -q build-essential libsqlite3-dev
37 37
 
38
-# Install Go from source (for eventual cross-compiling)
39
-env	CGO_ENABLED 0
40
-run	curl -s https://go.googlecode.com/files/go1.1.2.src.tar.gz | tar -v -C / -xz && mv /go /goroot
41
-run	cd /goroot/src && ./make.bash
42
-env GOROOT	/goroot
43
-env	PATH	$PATH:/goroot/bin
38
+# Install Go
39
+run	curl -s https://go.googlecode.com/files/go1.2rc1.src.tar.gz | tar -v -C /usr/local -xz
40
+run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
41
+env	PATH	/usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
44 42
 env	GOPATH	/go:/go/src/github.com/dotcloud/docker/vendor
45
-
43
+run	cd /tmp && echo 'package main' > t.go && go test -a -i -v
46 44
 # Ubuntu stuff
47 45
 run	apt-get install -y -q ruby1.9.3 rubygems libffi-dev
48 46
 run	gem install --no-rdoc --no-ri fpm
... ...
@@ -1153,7 +1153,7 @@ func (cli *DockerCli) CmdLink(args ...string) error {
1153 1153
 	}
1154 1154
 	body := map[string]string{
1155 1155
 		"currentName": cmd.Arg(0),
1156
-		"newName":     cmd.Arg(10),
1156
+		"newName":     cmd.Arg(1),
1157 1157
 	}
1158 1158
 
1159 1159
 	_, _, err := cli.call("POST", "/containers/link", body)
... ...
@@ -1,15 +1,35 @@
1 1
 package gograph
2 2
 
3 3
 import (
4
+	_ "code.google.com/p/gosqlite/sqlite3"
5
+	"database/sql"
4 6
 	"fmt"
7
+	"os"
5 8
 	"path"
6
-	"sync"
7 9
 )
8 10
 
9
-// Entity with a unique id and user defined value
11
+const (
12
+	createEntityTable = `
13
+    CREATE TABLE IF NOT EXISTS entity (
14
+        id text NOT NULL PRIMARY KEY
15
+    );`
16
+
17
+	createEdgeTable = `
18
+    CREATE TABLE IF NOT EXISTS edge (
19
+        "entity_id" text NOT NULL,
20
+        "parent_id" text NULL,
21
+        "name" text NOT NULL,
22
+        CONSTRAINT "parent_fk" FOREIGN KEY ("parent_id") REFERENCES "entity" ("id"),
23
+        CONSTRAINT "entity_fk" FOREIGN KEY ("entity_id") REFERENCES "entity" ("id")
24
+        );
25
+
26
+    CREATE UNIQUE INDEX "name_parent_ix" ON "edge" (parent_id, name);
27
+    `
28
+)
29
+
30
+// Entity with a unique id
10 31
 type Entity struct {
11
-	id    string
12
-	Value interface{}
32
+	id string
13 33
 }
14 34
 
15 35
 // An Edge connects two entities together
... ...
@@ -26,111 +46,173 @@ type WalkFunc func(fullPath string, entity *Entity) error
26 26
 
27 27
 // Graph database for storing entities and their relationships
28 28
 type Database struct {
29
-	entities Entities
30
-	edges    Edges
31
-	mux      sync.Mutex
32
-
29
+	dbPath string
33 30
 	rootID string
34 31
 }
35 32
 
36 33
 // Create a new graph database initialized with a root entity
37
-func NewDatabase(rootPath, rootId string) (*Database, error) {
38
-	db := &Database{Entities{}, Edges{}, sync.Mutex{}, rootId}
39
-	e := &Entity{
40
-		id: rootId,
34
+func NewDatabase(dbPath, rootId string) (*Database, error) {
35
+	db := &Database{dbPath, rootId}
36
+	if _, err := os.Stat(dbPath); err == nil {
37
+		return db, nil
38
+	}
39
+	conn, err := db.openConn()
40
+	if err != nil {
41
+		return nil, err
42
+	}
43
+	defer conn.Close()
44
+
45
+	if _, err := conn.Exec(createEntityTable); err != nil {
46
+		return nil, err
47
+	}
48
+	if _, err := conn.Exec(createEdgeTable); err != nil {
49
+		return nil, err
41 50
 	}
42
-	db.entities[rootId] = e
43 51
 
44
-	edge := &Edge{
45
-		EntityID: rootId,
46
-		Name:     "/",
52
+	rollback := func() {
53
+		conn.Exec("ROLLBACK")
47 54
 	}
48
-	db.edges = append(db.edges, edge)
49 55
 
56
+	// Create root entities
57
+	if _, err := conn.Exec("BEGIN"); err != nil {
58
+		return nil, err
59
+	}
60
+	if _, err := conn.Exec("INSERT INTO entity (id) VALUES (?);", rootId); err != nil {
61
+		rollback()
62
+		return nil, err
63
+	}
64
+
65
+	if _, err := conn.Exec("INSERT INTO edge (entity_id, name) VALUES(?,?);", rootId, "/"); err != nil {
66
+		rollback()
67
+		return nil, err
68
+	}
69
+
70
+	if _, err := conn.Exec("COMMIT"); err != nil {
71
+		return nil, err
72
+	}
50 73
 	return db, nil
51 74
 }
52 75
 
53 76
 // Set the entity id for a given path
54 77
 func (db *Database) Set(fullPath, id string) (*Entity, error) {
55
-	db.mux.Lock()
56
-	defer db.mux.Unlock()
57
-
58
-	e, exists := db.entities[id]
59
-	if !exists {
60
-		e = &Entity{
61
-			id: id,
78
+	conn, err := db.openConn()
79
+	if err != nil {
80
+		return nil, err
81
+	}
82
+	defer conn.Close()
83
+	rollback := func() {
84
+		conn.Exec("ROLLBACK")
85
+	}
86
+	if _, err := conn.Exec("BEGIN"); err != nil {
87
+		return nil, err
88
+	}
89
+	var entityId string
90
+	if err := conn.QueryRow("SELECT id FROM entity WHERE id = ?;", id).Scan(&entityId); err != nil {
91
+		if err == sql.ErrNoRows {
92
+			if _, err := conn.Exec("INSERT INTO entity (id) VALUES(?);", id); err != nil {
93
+				rollback()
94
+				return nil, err
95
+			}
96
+		} else {
97
+			rollback()
98
+			return nil, err
62 99
 		}
63
-		db.entities[id] = e
64 100
 	}
101
+	e := &Entity{id}
65 102
 
66 103
 	parentPath, name := splitPath(fullPath)
67
-	if err := db.setEdge(parentPath, name, e); err != nil {
104
+	if err := db.setEdge(conn, parentPath, name, e); err != nil {
105
+		rollback()
106
+		return nil, err
107
+	}
108
+
109
+	if _, err := conn.Exec("COMMIT"); err != nil {
68 110
 		return nil, err
69 111
 	}
70 112
 	return e, nil
71 113
 }
72 114
 
73
-func (db *Database) setEdge(parentPath, name string, e *Entity) error {
74
-	parent := db.Get(parentPath)
75
-	if parent == nil {
76
-		return fmt.Errorf("Parent does not exist for path: %s", parentPath)
115
+func (db *Database) setEdge(conn *sql.DB, parentPath, name string, e *Entity) error {
116
+	parent, err := db.get(conn, parentPath)
117
+	if err != nil {
118
+		return err
77 119
 	}
78 120
 	if parent.id == e.id {
79 121
 		return fmt.Errorf("Cannot set self as child")
80 122
 	}
81 123
 
82
-	edge := &Edge{
83
-		ParentID: parent.id,
84
-		EntityID: e.id,
85
-		Name:     name,
86
-	}
87
-	if db.edges.Exists(parent.id, name) {
88
-		return fmt.Errorf("Relationship already exists for %s/%s", parentPath, name)
124
+	if _, err := conn.Exec("INSERT INTO edge (parent_id, name, entity_id) VALUES (?,?,?);", parent.id, name, e.id); err != nil {
125
+		return err
89 126
 	}
90
-	db.edges = append(db.edges, edge)
91 127
 	return nil
92 128
 }
93 129
 
94 130
 // Return the root "/" entity for the database
95 131
 func (db *Database) RootEntity() *Entity {
96
-	return db.entities[db.rootID]
132
+	return &Entity{
133
+		id: db.rootID,
134
+	}
97 135
 }
98 136
 
99 137
 // Return the entity for a given path
100 138
 func (db *Database) Get(name string) *Entity {
139
+	conn, err := db.openConn()
140
+	if err != nil {
141
+		return nil
142
+	}
143
+	e, err := db.get(conn, name)
144
+	if err != nil {
145
+		return nil
146
+	}
147
+	return e
148
+}
149
+
150
+func (db *Database) get(conn *sql.DB, name string) (*Entity, error) {
101 151
 	e := db.RootEntity()
102 152
 	// We always know the root name so return it if
103 153
 	// it is requested
104 154
 	if name == "/" {
105
-		return e
155
+		return e, nil
106 156
 	}
107 157
 
108 158
 	parts := split(name)
109 159
 	for i := 1; i < len(parts); i++ {
110 160
 		p := parts[i]
111 161
 
112
-		next := db.child(e, p)
162
+		next := db.child(conn, e, p)
113 163
 		if next == nil {
114
-			return nil
164
+			return nil, fmt.Errorf("Cannot find child")
115 165
 		}
116 166
 		e = next
117
-
118 167
 	}
119
-	return e
168
+	return e, nil
169
+
120 170
 }
121 171
 
122 172
 // List all entities by from the name
123 173
 // The key will be the full path of the entity
124 174
 func (db *Database) List(name string, depth int) Entities {
125 175
 	out := Entities{}
126
-	for c := range db.children(name, depth) {
176
+	conn, err := db.openConn()
177
+	if err != nil {
178
+		return out
179
+	}
180
+	defer conn.Close()
181
+
182
+	for c := range db.children(conn, name, depth) {
127 183
 		out[c.FullPath] = c.Entity
128 184
 	}
129 185
 	return out
130 186
 }
131 187
 
132 188
 func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
133
-	for c := range db.children(name, depth) {
189
+	conn, err := db.openConn()
190
+	if err != nil {
191
+		return err
192
+	}
193
+	defer conn.Close()
194
+
195
+	for c := range db.children(conn, name, depth) {
134 196
 		if err := walkFunc(c.FullPath, c.Entity); err != nil {
135 197
 			return err
136 198
 		}
... ...
@@ -140,14 +222,46 @@ func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
140 140
 
141 141
 // Return the refrence count for a specified id
142 142
 func (db *Database) Refs(id string) int {
143
-	return len(db.RefPaths(id))
143
+	conn, err := db.openConn()
144
+	if err != nil {
145
+		return -1
146
+	}
147
+	defer conn.Close()
148
+
149
+	var count int
150
+	if err := conn.QueryRow("SELECT COUNT(*) FROM edge WHERE entity_id = ?;", id).Scan(&count); err != nil {
151
+		return 0
152
+	}
153
+	return count
144 154
 }
145 155
 
146 156
 // Return all the id's path references
147 157
 func (db *Database) RefPaths(id string) Edges {
148
-	refs := db.edges.Search(func(e *Edge) bool {
149
-		return e.EntityID == id
150
-	})
158
+	refs := Edges{}
159
+	conn, err := db.openConn()
160
+	if err != nil {
161
+		return refs
162
+	}
163
+	defer conn.Close()
164
+
165
+	rows, err := conn.Query("SELECT name, parent_id FROM edge WHERE entity_id = ?;", id)
166
+	if err != nil {
167
+		return refs
168
+	}
169
+	defer rows.Close()
170
+
171
+	for rows.Next() {
172
+		var name string
173
+		var parentId string
174
+		if err := rows.Scan(&name, &parentId); err != nil {
175
+			return refs
176
+		}
177
+		refs = append(refs, &Edge{
178
+			EntityID: id,
179
+			Name:     name,
180
+			ParentID: parentId,
181
+		})
182
+	}
151 183
 	return refs
152 184
 }
153 185
 
... ...
@@ -156,54 +270,64 @@ func (db *Database) Delete(name string) error {
156 156
 	if name == "/" {
157 157
 		return fmt.Errorf("Cannot delete root entity")
158 158
 	}
159
-	db.mux.Lock()
160
-	defer db.mux.Unlock()
159
+	conn, err := db.openConn()
160
+	if err != nil {
161
+		return err
162
+	}
163
+	defer conn.Close()
161 164
 
162 165
 	parentPath, n := splitPath(name)
163
-	parent := db.Get(parentPath)
164
-	if parent == nil {
165
-		return fmt.Errorf("Cannot find parent for %s", parentPath)
166
-	}
167
-	edge, i := db.edges.Get(parent.id, n)
168
-	if edge == nil {
169
-		return fmt.Errorf("Edge does not exist at %s", name)
166
+	parent, err := db.get(conn, parentPath)
167
+	if err != nil {
168
+		return err
170 169
 	}
171
-	db.deleteEdgeAtIndex(i)
172 170
 
171
+	if _, err := conn.Exec("DELETE FROM edge WHERE parent_id = ? AND name = ?;", parent.id, n); err != nil {
172
+		return err
173
+	}
173 174
 	return nil
174 175
 }
175 176
 
176
-func (db *Database) deleteEdgeAtIndex(i int) {
177
-	db.edges[len(db.edges)-1], db.edges[i], db.edges = nil, db.edges[len(db.edges)-1], db.edges[:len(db.edges)-1]
178
-}
179
-
180 177
 // Remove the entity with the specified id
181 178
 // Walk the graph to make sure all references to the entity
182 179
 // are removed and return the number of references removed
183 180
 func (db *Database) Purge(id string) (int, error) {
184
-	db.mux.Lock()
185
-	defer db.mux.Unlock()
186
-
187
-	getIndex := func(e *Edge) int {
188
-		for i, edge := range db.edges {
189
-			if edge.EntityID == e.EntityID &&
190
-				edge.Name == e.Name &&
191
-				edge.ParentID == e.ParentID {
192
-				return i
193
-			}
194
-		}
195
-		return -1
181
+	conn, err := db.openConn()
182
+	if err != nil {
183
+		return -1, err
196 184
 	}
185
+	defer conn.Close()
197 186
 
198
-	refsToDelete := db.RefPaths(id)
199
-	for i, e := range refsToDelete {
200
-		index := getIndex(e)
201
-		if index == -1 {
202
-			return i + 1, fmt.Errorf("Cannot find index for %s %s", e.ParentID, e.Name)
203
-		}
204
-		db.deleteEdgeAtIndex(index)
187
+	rollback := func() {
188
+		conn.Exec("ROLLBACK")
189
+	}
190
+
191
+	if _, err := conn.Exec("BEGIN"); err != nil {
192
+		return -1, err
193
+	}
194
+
195
+	// Delete all edges
196
+	rows, err := conn.Exec("DELETE FROM edge WHERE entity_id = ?;", id)
197
+	if err != nil {
198
+		rollback()
199
+		return -1, err
200
+	}
201
+
202
+	changes, err := rows.RowsAffected()
203
+	if err != nil {
204
+		return -1, err
205 205
 	}
206
-	return len(refsToDelete), nil
206
+
207
+	// Delete entity
208
+	if _, err := conn.Exec("DELETE FROM entity where id = ?;", id); err != nil {
209
+		rollback()
210
+		return -1, err
211
+	}
212
+
213
+	if _, err := conn.Exec("COMMIT"); err != nil {
214
+		return -1, err
215
+	}
216
+	return int(changes), nil
207 217
 }
208 218
 
209 219
 // Rename an edge for a given path
... ...
@@ -215,19 +339,28 @@ func (db *Database) Rename(currentName, newName string) error {
215 215
 		return fmt.Errorf("Cannot rename when root paths do not match %s != %s", parentPath, newParentPath)
216 216
 	}
217 217
 
218
-	db.mux.Lock()
219
-	defer db.mux.Unlock()
218
+	conn, err := db.openConn()
219
+	if err != nil {
220
+		return err
221
+	}
222
+	defer conn.Close()
223
+
224
+	parent, err := db.get(conn, parentPath)
225
+	if err != nil {
226
+		return err
227
+	}
220 228
 
221
-	parent := db.Get(parentPath)
222
-	if parent == nil {
223
-		return fmt.Errorf("Cannot locate parent for %s", currentName)
229
+	rows, err := conn.Exec("UPDATE edge SET name = ? WHERE parent_id = ? AND name = ?;", newEdgeName, parent.id, name)
230
+	if err != nil {
231
+		return err
232
+	}
233
+	i, err := rows.RowsAffected()
234
+	if err != nil {
235
+		return err
224 236
 	}
225
-	edge, _ := db.edges.Get(parent.id, name)
226
-	if edge == nil {
237
+	if i == 0 {
227 238
 		return fmt.Errorf("Cannot locate edge for %s %s", parent.id, name)
228 239
 	}
229
-	edge.Name = newEdgeName
230
-
231 240
 	return nil
232 241
 }
233 242
 
... ...
@@ -238,38 +371,52 @@ type WalkMeta struct {
238 238
 	Edge     *Edge
239 239
 }
240 240
 
241
-func (db *Database) children(name string, depth int) <-chan WalkMeta {
241
+func (db *Database) children(conn *sql.DB, name string, depth int) <-chan WalkMeta {
242 242
 	out := make(chan WalkMeta)
243
-	e := db.Get(name)
244
-
245
-	if e == nil {
243
+	e, err := db.get(conn, name)
244
+	if err != nil {
246 245
 		close(out)
247 246
 		return out
248 247
 	}
249 248
 
250 249
 	go func() {
251
-		for _, edge := range db.edges {
252
-			if edge.ParentID == e.id {
253
-				child := db.entities[edge.EntityID]
254
-
255
-				meta := WalkMeta{
256
-					Parent:   e,
257
-					Entity:   child,
258
-					FullPath: path.Join(name, edge.Name),
259
-					Edge:     edge,
260
-				}
261
-				out <- meta
262
-				if depth == 0 {
263
-					continue
264
-				}
265
-				nDepth := depth
266
-				if depth != -1 {
267
-					nDepth -= 1
268
-				}
269
-				sc := db.children(meta.FullPath, nDepth)
270
-				for c := range sc {
271
-					out <- c
272
-				}
250
+		rows, err := conn.Query("SELECT entity_id, name FROM edge where parent_id = ?;", e.id)
251
+		if err != nil {
252
+			close(out)
253
+		}
254
+		defer rows.Close()
255
+
256
+		for rows.Next() {
257
+			var entityId, entityName string
258
+			if err := rows.Scan(&entityId, &entityName); err != nil {
259
+				// Log error
260
+				continue
261
+			}
262
+			child := &Entity{entityId}
263
+			edge := &Edge{
264
+				ParentID: e.id,
265
+				Name:     entityName,
266
+				EntityID: child.id,
267
+			}
268
+
269
+			meta := WalkMeta{
270
+				Parent:   e,
271
+				Entity:   child,
272
+				FullPath: path.Join(name, edge.Name),
273
+				Edge:     edge,
274
+			}
275
+
276
+			out <- meta
277
+			if depth == 0 {
278
+				continue
279
+			}
280
+			nDepth := depth
281
+			if depth != -1 {
282
+				nDepth -= 1
283
+			}
284
+			sc := db.children(conn, meta.FullPath, nDepth)
285
+			for c := range sc {
286
+				out <- c
273 287
 			}
274 288
 		}
275 289
 		close(out)
... ...
@@ -278,12 +425,16 @@ func (db *Database) children(name string, depth int) <-chan WalkMeta {
278 278
 }
279 279
 
280 280
 // Return the entity based on the parent path and name
281
-func (db *Database) child(parent *Entity, name string) *Entity {
282
-	edge, _ := db.edges.Get(parent.id, name)
283
-	if edge == nil {
281
+func (db *Database) child(conn *sql.DB, parent *Entity, name string) *Entity {
282
+	var id string
283
+	if err := conn.QueryRow("SELECT entity_id FROM edge WHERE parent_id = ? AND name = ?;", parent.id, name).Scan(&id); err != nil {
284 284
 		return nil
285 285
 	}
286
-	return db.entities[edge.EntityID]
286
+	return &Entity{id}
287
+}
288
+
289
+func (db *Database) openConn() (*sql.DB, error) {
290
+	return sql.Open("sqlite3", db.dbPath)
287 291
 }
288 292
 
289 293
 // Return the id used to reference this entity
... ...
@@ -303,29 +454,3 @@ func (e Entities) Paths() []string {
303 303
 
304 304
 	return out
305 305
 }
306
-
307
-// Checks if an edge with the specified parent id and name exist in the slice
308
-func (e Edges) Exists(parendId, name string) bool {
309
-	edge, _ := e.Get(parendId, name)
310
-	return edge != nil
311
-}
312
-
313
-// Returns the edge and index in the slice with the specified parent id and name
314
-func (e Edges) Get(parentId, name string) (*Edge, int) {
315
-	for i, edge := range e {
316
-		if edge.ParentID == parentId && edge.Name == name {
317
-			return edge, i
318
-		}
319
-	}
320
-	return nil, -1
321
-}
322
-
323
-func (e Edges) Search(predicate func(edge *Edge) bool) Edges {
324
-	out := Edges{}
325
-	for _, edge := range e {
326
-		if predicate(edge) {
327
-			out = append(out, edge)
328
-		}
329
-	}
330
-	return out
331
-}
... ...
@@ -2,27 +2,34 @@ package gograph
2 2
 
3 3
 import (
4 4
 	"os"
5
+	"path"
5 6
 	"strconv"
6 7
 	"testing"
7 8
 )
8 9
 
9 10
 func newTestDb(t *testing.T) *Database {
10
-	db, err := NewDatabase(os.TempDir(), "0")
11
+	db, err := NewDatabase(path.Join(os.TempDir(), "sqlite.db"), "0")
11 12
 	if err != nil {
12 13
 		t.Fatal(err)
13 14
 	}
14 15
 	return db
15 16
 }
16 17
 
18
+func destroyTestDb(db *Database) {
19
+	os.Remove(db.dbPath)
20
+}
21
+
17 22
 func TestNewDatabase(t *testing.T) {
18 23
 	db := newTestDb(t)
19 24
 	if db == nil {
20 25
 		t.Fatal("Datbase should not be nil")
21 26
 	}
27
+	defer destroyTestDb(db)
22 28
 }
23 29
 
24 30
 func TestCreateRootEnity(t *testing.T) {
25 31
 	db := newTestDb(t)
32
+	defer destroyTestDb(db)
26 33
 	root := db.RootEntity()
27 34
 	if root == nil {
28 35
 		t.Fatal("Root entity should not be nil")
... ...
@@ -31,6 +38,7 @@ func TestCreateRootEnity(t *testing.T) {
31 31
 
32 32
 func TestGetRootEntity(t *testing.T) {
33 33
 	db := newTestDb(t)
34
+	defer destroyTestDb(db)
34 35
 
35 36
 	e := db.Get("/")
36 37
 	if e == nil {
... ...
@@ -43,6 +51,7 @@ func TestGetRootEntity(t *testing.T) {
43 43
 
44 44
 func TestSetEntityWithDifferentName(t *testing.T) {
45 45
 	db := newTestDb(t)
46
+	defer destroyTestDb(db)
46 47
 
47 48
 	db.Set("/test", "1")
48 49
 	if _, err := db.Set("/other", "1"); err != nil {
... ...
@@ -52,6 +61,7 @@ func TestSetEntityWithDifferentName(t *testing.T) {
52 52
 
53 53
 func TestCreateChild(t *testing.T) {
54 54
 	db := newTestDb(t)
55
+	defer destroyTestDb(db)
55 56
 
56 57
 	child, err := db.Set("/db", "1")
57 58
 	if err != nil {
... ...
@@ -67,6 +77,7 @@ func TestCreateChild(t *testing.T) {
67 67
 
68 68
 func TestListAllRootChildren(t *testing.T) {
69 69
 	db := newTestDb(t)
70
+	defer destroyTestDb(db)
70 71
 
71 72
 	for i := 1; i < 6; i++ {
72 73
 		a := strconv.Itoa(i)
... ...
@@ -82,6 +93,7 @@ func TestListAllRootChildren(t *testing.T) {
82 82
 
83 83
 func TestListAllSubChildren(t *testing.T) {
84 84
 	db := newTestDb(t)
85
+	defer destroyTestDb(db)
85 86
 
86 87
 	_, err := db.Set("/webapp", "1")
87 88
 	if err != nil {
... ...
@@ -123,6 +135,7 @@ func TestListAllSubChildren(t *testing.T) {
123 123
 
124 124
 func TestAddSelfAsChild(t *testing.T) {
125 125
 	db := newTestDb(t)
126
+	defer destroyTestDb(db)
126 127
 
127 128
 	child, err := db.Set("/test", "1")
128 129
 	if err != nil {
... ...
@@ -135,6 +148,7 @@ func TestAddSelfAsChild(t *testing.T) {
135 135
 
136 136
 func TestAddChildToNonExistantRoot(t *testing.T) {
137 137
 	db := newTestDb(t)
138
+	defer destroyTestDb(db)
138 139
 
139 140
 	if _, err := db.Set("/myapp", "1"); err != nil {
140 141
 		t.Fatal(err)
... ...
@@ -147,6 +161,7 @@ func TestAddChildToNonExistantRoot(t *testing.T) {
147 147
 
148 148
 func TestWalkAll(t *testing.T) {
149 149
 	db := newTestDb(t)
150
+	defer destroyTestDb(db)
150 151
 	_, err := db.Set("/webapp", "1")
151 152
 	if err != nil {
152 153
 		t.Fatal(err)
... ...
@@ -192,6 +207,7 @@ func TestWalkAll(t *testing.T) {
192 192
 
193 193
 func TestGetEntityByPath(t *testing.T) {
194 194
 	db := newTestDb(t)
195
+	defer destroyTestDb(db)
195 196
 	_, err := db.Set("/webapp", "1")
196 197
 	if err != nil {
197 198
 		t.Fatal(err)
... ...
@@ -238,6 +254,7 @@ func TestGetEntityByPath(t *testing.T) {
238 238
 
239 239
 func TestEnitiesPaths(t *testing.T) {
240 240
 	db := newTestDb(t)
241
+	defer destroyTestDb(db)
241 242
 	_, err := db.Set("/webapp", "1")
242 243
 	if err != nil {
243 244
 		t.Fatal(err)
... ...
@@ -281,6 +298,7 @@ func TestEnitiesPaths(t *testing.T) {
281 281
 
282 282
 func TestDeleteRootEntity(t *testing.T) {
283 283
 	db := newTestDb(t)
284
+	defer destroyTestDb(db)
284 285
 
285 286
 	if err := db.Delete("/"); err == nil {
286 287
 		t.Fatal("Error should not be nil")
... ...
@@ -289,6 +307,7 @@ func TestDeleteRootEntity(t *testing.T) {
289 289
 
290 290
 func TestDeleteEntity(t *testing.T) {
291 291
 	db := newTestDb(t)
292
+	defer destroyTestDb(db)
292 293
 	_, err := db.Set("/webapp", "1")
293 294
 	if err != nil {
294 295
 		t.Fatal(err)
... ...
@@ -335,6 +354,7 @@ func TestDeleteEntity(t *testing.T) {
335 335
 
336 336
 func TestCountRefs(t *testing.T) {
337 337
 	db := newTestDb(t)
338
+	defer destroyTestDb(db)
338 339
 
339 340
 	db.Set("/webapp", "1")
340 341
 
... ...
@@ -351,6 +371,7 @@ func TestCountRefs(t *testing.T) {
351 351
 
352 352
 func TestPurgeId(t *testing.T) {
353 353
 	db := newTestDb(t)
354
+	defer destroyTestDb(db)
354 355
 
355 356
 	db.Set("/webapp", "1")
356 357
 
... ...
@@ -372,6 +393,7 @@ func TestPurgeId(t *testing.T) {
372 372
 
373 373
 func TestRename(t *testing.T) {
374 374
 	db := newTestDb(t)
375
+	defer destroyTestDb(db)
375 376
 
376 377
 	db.Set("/webapp", "1")
377 378
 
... ...
@@ -400,6 +422,7 @@ func TestRename(t *testing.T) {
400 400
 
401 401
 func TestCreateMultipleNames(t *testing.T) {
402 402
 	db := newTestDb(t)
403
+	defer destroyTestDb(db)
403 404
 
404 405
 	db.Set("/db", "1")
405 406
 	if _, err := db.Set("/myapp", "1"); err != nil {
... ...
@@ -411,3 +434,19 @@ func TestCreateMultipleNames(t *testing.T) {
411 411
 		return nil
412 412
 	}, -1)
413 413
 }
414
+
415
+func TestRefPaths(t *testing.T) {
416
+	db := newTestDb(t)
417
+	defer destroyTestDb(db)
418
+
419
+	db.Set("/webapp", "1")
420
+
421
+	db.Set("/db", "2")
422
+	db.Set("/webapp/db", "2")
423
+
424
+	refs := db.RefPaths("2")
425
+	if len(refs) != 2 {
426
+		t.Fatalf("Expected reference count to be 2, got %d", len(refs))
427
+	}
428
+
429
+}
... ...
@@ -44,7 +44,8 @@ if [ -n "$(git status --porcelain)" ]; then
44 44
 fi
45 45
 
46 46
 # Use these flags when compiling the tests and final binary
47
-LDFLAGS="-X main.GITCOMMIT $GITCOMMIT -X main.VERSION $VERSION -d -w"
47
+LDFLAGS='-X main.GITCOMMIT "'$GITCOMMIT'" -X main.VERSION "'$VERSION'" -w -linkmode external -extldflags "-ldl -pthread -static -Wl,--unresolved-symbols=ignore-in-shared-libs"'
48
+BUILDFLAGS='-tags netgo'
48 49
 
49 50
 
50 51
 bundle() {
... ...
@@ -2,6 +2,6 @@
2 2
 
3 3
 DEST=$1
4 4
 
5
-if go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" ./docker; then
6
-	echo "Created binary: $DEST/docker-$VERSION"
7
-fi
5
+go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" $BUILDFLAGS ./docker
6
+
7
+echo "Created binary: $DEST/docker-$VERSION"
... ...
@@ -9,7 +9,7 @@ bundle_test() {
9 9
 		for test_dir in $(find_test_dirs); do (
10 10
 			set -x
11 11
 			cd $test_dir
12
-			go test -v -ldflags "$LDFLAGS"
12
+			go test -v -ldflags "$LDFLAGS" $BUILDFLAGS
13 13
 		)  done
14 14
 	} 2>&1 | tee $DEST/test.log
15 15
 }
... ...
@@ -199,6 +199,7 @@ func (runtime *Runtime) Destroy(container *Container) error {
199 199
 	if err := container.Stop(3); err != nil {
200 200
 		return err
201 201
 	}
202
+
202 203
 	if mounted, err := container.Mounted(); err != nil {
203 204
 		return err
204 205
 	} else if mounted {
... ...
@@ -206,6 +207,11 @@ func (runtime *Runtime) Destroy(container *Container) error {
206 206
 			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
207 207
 		}
208 208
 	}
209
+
210
+	if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
211
+		utils.Debugf("Unable to remove container from link graph: %s", err)
212
+	}
213
+
209 214
 	// Deregister the container before removing its directory, to avoid race conditions
210 215
 	runtime.idIndex.Delete(container.ID)
211 216
 	runtime.containers.Remove(element)
... ...
@@ -566,7 +572,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
566 566
 	if err != nil {
567 567
 		return nil, err
568 568
 	}
569
-	graph, err := gograph.NewDatabase("", "engine")
569
+	graph, err := gograph.NewDatabase(path.Join(config.GraphPath, "linkgraph.db"), "engine")
570 570
 	if err != nil {
571 571
 		return nil, err
572 572
 	}
... ...
@@ -500,8 +500,6 @@ func TestRestore(t *testing.T) {
500 500
 }
501 501
 
502 502
 func TestReloadContainerLinks(t *testing.T) {
503
-	t.SkipNow() // TODO: @crosbymichael
504
-
505 503
 	runtime1 := mkRuntime(t)
506 504
 	defer nuke(runtime1)
507 505
 	// Create a container with one instance of docker
508 506
new file mode 100644
... ...
@@ -0,0 +1,404 @@
0
+// Copyright 2010 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// Package sqlite provides access to the SQLite library, version 3.
5
+package sqlite
6
+
7
+/*
8
+#cgo LDFLAGS: -lsqlite3
9
+
10
+#include <sqlite3.h>
11
+#include <stdlib.h>
12
+
13
+// These wrappers are necessary because SQLITE_TRANSIENT
14
+// is a pointer constant, and cgo doesn't translate them correctly.
15
+// The definition in sqlite3.h is:
16
+//
17
+// typedef void (*sqlite3_destructor_type)(void*);
18
+// #define SQLITE_STATIC      ((sqlite3_destructor_type)0)
19
+// #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)
20
+
21
+static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
22
+	return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
23
+}
24
+static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
25
+	return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
26
+}
27
+
28
+*/
29
+import "C"
30
+
31
+import (
32
+	"errors"
33
+	"fmt"
34
+	"reflect"
35
+	"strconv"
36
+	"time"
37
+	"unsafe"
38
+)
39
+
40
+type Errno int
41
+
42
+func (e Errno) Error() string {
43
+	s := errText[e]
44
+	if s == "" {
45
+		return fmt.Sprintf("errno %d", int(e))
46
+	}
47
+	return s
48
+}
49
+
50
+var (
51
+	ErrError      error = Errno(1)   //    /* SQL error or missing database */
52
+	ErrInternal   error = Errno(2)   //    /* Internal logic error in SQLite */
53
+	ErrPerm       error = Errno(3)   //    /* Access permission denied */
54
+	ErrAbort      error = Errno(4)   //    /* Callback routine requested an abort */
55
+	ErrBusy       error = Errno(5)   //    /* The database file is locked */
56
+	ErrLocked     error = Errno(6)   //    /* A table in the database is locked */
57
+	ErrNoMem      error = Errno(7)   //    /* A malloc() failed */
58
+	ErrReadOnly   error = Errno(8)   //    /* Attempt to write a readonly database */
59
+	ErrInterrupt  error = Errno(9)   //    /* Operation terminated by sqlite3_interrupt()*/
60
+	ErrIOErr      error = Errno(10)  //    /* Some kind of disk I/O error occurred */
61
+	ErrCorrupt    error = Errno(11)  //    /* The database disk image is malformed */
62
+	ErrFull       error = Errno(13)  //    /* Insertion failed because database is full */
63
+	ErrCantOpen   error = Errno(14)  //    /* Unable to open the database file */
64
+	ErrEmpty      error = Errno(16)  //    /* Database is empty */
65
+	ErrSchema     error = Errno(17)  //    /* The database schema changed */
66
+	ErrTooBig     error = Errno(18)  //    /* String or BLOB exceeds size limit */
67
+	ErrConstraint error = Errno(19)  //    /* Abort due to constraint violation */
68
+	ErrMismatch   error = Errno(20)  //    /* Data type mismatch */
69
+	ErrMisuse     error = Errno(21)  //    /* Library used incorrectly */
70
+	ErrNolfs      error = Errno(22)  //    /* Uses OS features not supported on host */
71
+	ErrAuth       error = Errno(23)  //    /* Authorization denied */
72
+	ErrFormat     error = Errno(24)  //    /* Auxiliary database format error */
73
+	ErrRange      error = Errno(25)  //    /* 2nd parameter to sqlite3_bind out of range */
74
+	ErrNotDB      error = Errno(26)  //    /* File opened that is not a database file */
75
+	Row                 = Errno(100) //   /* sqlite3_step() has another row ready */
76
+	Done                = Errno(101) //   /* sqlite3_step() has finished executing */
77
+)
78
+
79
+var errText = map[Errno]string{
80
+	1:   "SQL error or missing database",
81
+	2:   "Internal logic error in SQLite",
82
+	3:   "Access permission denied",
83
+	4:   "Callback routine requested an abort",
84
+	5:   "The database file is locked",
85
+	6:   "A table in the database is locked",
86
+	7:   "A malloc() failed",
87
+	8:   "Attempt to write a readonly database",
88
+	9:   "Operation terminated by sqlite3_interrupt()*/",
89
+	10:  "Some kind of disk I/O error occurred",
90
+	11:  "The database disk image is malformed",
91
+	12:  "NOT USED. Table or record not found",
92
+	13:  "Insertion failed because database is full",
93
+	14:  "Unable to open the database file",
94
+	15:  "NOT USED. Database lock protocol error",
95
+	16:  "Database is empty",
96
+	17:  "The database schema changed",
97
+	18:  "String or BLOB exceeds size limit",
98
+	19:  "Abort due to constraint violation",
99
+	20:  "Data type mismatch",
100
+	21:  "Library used incorrectly",
101
+	22:  "Uses OS features not supported on host",
102
+	23:  "Authorization denied",
103
+	24:  "Auxiliary database format error",
104
+	25:  "2nd parameter to sqlite3_bind out of range",
105
+	26:  "File opened that is not a database file",
106
+	100: "sqlite3_step() has another row ready",
107
+	101: "sqlite3_step() has finished executing",
108
+}
109
+
110
+func (c *Conn) error(rv C.int) error {
111
+	if c == nil || c.db == nil {
112
+		return errors.New("nil sqlite database")
113
+	}
114
+	if rv == 0 {
115
+		return nil
116
+	}
117
+	if rv == 21 { // misuse
118
+		return Errno(rv)
119
+	}
120
+	return errors.New(Errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
121
+}
122
+
123
+type Conn struct {
124
+	db *C.sqlite3
125
+}
126
+
127
+func Version() string {
128
+	p := C.sqlite3_libversion()
129
+	return C.GoString(p)
130
+}
131
+
132
+func Open(filename string) (*Conn, error) {
133
+	if C.sqlite3_threadsafe() == 0 {
134
+		return nil, errors.New("sqlite library was not compiled for thread-safe operation")
135
+	}
136
+
137
+	var db *C.sqlite3
138
+	name := C.CString(filename)
139
+	defer C.free(unsafe.Pointer(name))
140
+	rv := C.sqlite3_open_v2(name, &db,
141
+		C.SQLITE_OPEN_FULLMUTEX|
142
+			C.SQLITE_OPEN_READWRITE|
143
+			C.SQLITE_OPEN_CREATE,
144
+		nil)
145
+	if rv != 0 {
146
+		return nil, Errno(rv)
147
+	}
148
+	if db == nil {
149
+		return nil, errors.New("sqlite succeeded without returning a database")
150
+	}
151
+	return &Conn{db}, nil
152
+}
153
+
154
+func NewBackup(dst *Conn, dstTable string, src *Conn, srcTable string) (*Backup, error) {
155
+	dname := C.CString(dstTable)
156
+	sname := C.CString(srcTable)
157
+	defer C.free(unsafe.Pointer(dname))
158
+	defer C.free(unsafe.Pointer(sname))
159
+
160
+	sb := C.sqlite3_backup_init(dst.db, dname, src.db, sname)
161
+	if sb == nil {
162
+		return nil, dst.error(C.sqlite3_errcode(dst.db))
163
+	}
164
+	return &Backup{sb, dst, src}, nil
165
+}
166
+
167
+type Backup struct {
168
+	sb       *C.sqlite3_backup
169
+	dst, src *Conn
170
+}
171
+
172
+func (b *Backup) Step(npage int) error {
173
+	rv := C.sqlite3_backup_step(b.sb, C.int(npage))
174
+	if rv == 0 || Errno(rv) == ErrBusy || Errno(rv) == ErrLocked {
175
+		return nil
176
+	}
177
+	return Errno(rv)
178
+}
179
+
180
+type BackupStatus struct {
181
+	Remaining int
182
+	PageCount int
183
+}
184
+
185
+func (b *Backup) Status() BackupStatus {
186
+	return BackupStatus{int(C.sqlite3_backup_remaining(b.sb)), int(C.sqlite3_backup_pagecount(b.sb))}
187
+}
188
+
189
+func (b *Backup) Run(npage int, period time.Duration, c chan<- BackupStatus) error {
190
+	var err error
191
+	for {
192
+		err = b.Step(npage)
193
+		if err != nil {
194
+			break
195
+		}
196
+		if c != nil {
197
+			c <- b.Status()
198
+		}
199
+		time.Sleep(period)
200
+	}
201
+	return b.dst.error(C.sqlite3_errcode(b.dst.db))
202
+}
203
+
204
+func (b *Backup) Close() error {
205
+	if b.sb == nil {
206
+		return errors.New("backup already closed")
207
+	}
208
+	C.sqlite3_backup_finish(b.sb)
209
+	b.sb = nil
210
+	return nil
211
+}
212
+
213
+func (c *Conn) BusyTimeout(ms int) error {
214
+	rv := C.sqlite3_busy_timeout(c.db, C.int(ms))
215
+	if rv == 0 {
216
+		return nil
217
+	}
218
+	return Errno(rv)
219
+}
220
+
221
+func (c *Conn) Exec(cmd string, args ...interface{}) error {
222
+	s, err := c.Prepare(cmd)
223
+	if err != nil {
224
+		return err
225
+	}
226
+	defer s.Finalize()
227
+	err = s.Exec(args...)
228
+	if err != nil {
229
+		return err
230
+	}
231
+	rv := C.sqlite3_step(s.stmt)
232
+	if Errno(rv) != Done {
233
+		return c.error(rv)
234
+	}
235
+	return nil
236
+}
237
+
238
+type Stmt struct {
239
+	c    *Conn
240
+	stmt *C.sqlite3_stmt
241
+	err  error
242
+	t0   time.Time
243
+	sql  string
244
+	args string
245
+}
246
+
247
+func (c *Conn) Prepare(cmd string) (*Stmt, error) {
248
+	if c == nil || c.db == nil {
249
+		return nil, errors.New("nil sqlite database")
250
+	}
251
+	cmdstr := C.CString(cmd)
252
+	defer C.free(unsafe.Pointer(cmdstr))
253
+	var stmt *C.sqlite3_stmt
254
+	var tail *C.char
255
+	rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &stmt, &tail)
256
+	if rv != 0 {
257
+		return nil, c.error(rv)
258
+	}
259
+	return &Stmt{c: c, stmt: stmt, sql: cmd, t0: time.Now()}, nil
260
+}
261
+
262
+func (s *Stmt) Exec(args ...interface{}) error {
263
+	s.args = fmt.Sprintf(" %v", []interface{}(args))
264
+	rv := C.sqlite3_reset(s.stmt)
265
+	if rv != 0 {
266
+		return s.c.error(rv)
267
+	}
268
+
269
+	n := int(C.sqlite3_bind_parameter_count(s.stmt))
270
+	if n != len(args) {
271
+		return errors.New(fmt.Sprintf("incorrect argument count for Stmt.Exec: have %d want %d", len(args), n))
272
+	}
273
+
274
+	for i, v := range args {
275
+		var str string
276
+		switch v := v.(type) {
277
+		case []byte:
278
+			var p *byte
279
+			if len(v) > 0 {
280
+				p = &v[0]
281
+			}
282
+			if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
283
+				return s.c.error(rv)
284
+			}
285
+			continue
286
+
287
+		case bool:
288
+			if v {
289
+				str = "1"
290
+			} else {
291
+				str = "0"
292
+			}
293
+
294
+		default:
295
+			str = fmt.Sprint(v)
296
+		}
297
+
298
+		cstr := C.CString(str)
299
+		rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
300
+		C.free(unsafe.Pointer(cstr))
301
+		if rv != 0 {
302
+			return s.c.error(rv)
303
+		}
304
+	}
305
+	return nil
306
+}
307
+
308
+func (s *Stmt) Error() error {
309
+	return s.err
310
+}
311
+
312
+func (s *Stmt) Next() bool {
313
+	rv := C.sqlite3_step(s.stmt)
314
+	err := Errno(rv)
315
+	if err == Row {
316
+		return true
317
+	}
318
+	if err != Done {
319
+		s.err = s.c.error(rv)
320
+	}
321
+	return false
322
+}
323
+
324
+func (s *Stmt) Reset() error {
325
+	C.sqlite3_reset(s.stmt)
326
+	return nil
327
+}
328
+
329
+func (s *Stmt) Scan(args ...interface{}) error {
330
+	n := int(C.sqlite3_column_count(s.stmt))
331
+	if n != len(args) {
332
+		return errors.New(fmt.Sprintf("incorrect argument count for Stmt.Scan: have %d want %d", len(args), n))
333
+	}
334
+
335
+	for i, v := range args {
336
+		n := C.sqlite3_column_bytes(s.stmt, C.int(i))
337
+		p := C.sqlite3_column_blob(s.stmt, C.int(i))
338
+		if p == nil && n > 0 {
339
+			return errors.New("got nil blob")
340
+		}
341
+		var data []byte
342
+		if n > 0 {
343
+			data = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
344
+		}
345
+		switch v := v.(type) {
346
+		case *[]byte:
347
+			*v = data
348
+		case *string:
349
+			*v = string(data)
350
+		case *bool:
351
+			*v = string(data) == "1"
352
+		case *int:
353
+			x, err := strconv.Atoi(string(data))
354
+			if err != nil {
355
+				return errors.New("arg " + strconv.Itoa(i) + " as int: " + err.Error())
356
+			}
357
+			*v = x
358
+		case *int64:
359
+			x, err := strconv.ParseInt(string(data), 10, 64)
360
+			if err != nil {
361
+				return errors.New("arg " + strconv.Itoa(i) + " as int64: " + err.Error())
362
+			}
363
+			*v = x
364
+		case *float64:
365
+			x, err := strconv.ParseFloat(string(data), 64)
366
+			if err != nil {
367
+				return errors.New("arg " + strconv.Itoa(i) + " as float64: " + err.Error())
368
+			}
369
+			*v = x
370
+		default:
371
+			return errors.New("unsupported type in Scan: " + reflect.TypeOf(v).String())
372
+		}
373
+	}
374
+	return nil
375
+}
376
+
377
+func (s *Stmt) SQL() string {
378
+	return s.sql + s.args
379
+}
380
+
381
+func (s *Stmt) Nanoseconds() int64 {
382
+	return time.Now().Sub(s.t0).Nanoseconds()
383
+}
384
+
385
+func (s *Stmt) Finalize() error {
386
+	rv := C.sqlite3_finalize(s.stmt)
387
+	if rv != 0 {
388
+		return s.c.error(rv)
389
+	}
390
+	return nil
391
+}
392
+
393
+func (c *Conn) Close() error {
394
+	if c == nil || c.db == nil {
395
+		return errors.New("nil sqlite database")
396
+	}
397
+	rv := C.sqlite3_close(c.db)
398
+	if rv != 0 {
399
+		return c.error(rv)
400
+	}
401
+	c.db = nil
402
+	return nil
403
+}
0 404
new file mode 100644
... ...
@@ -0,0 +1,498 @@
0
+// Copyright 2010 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// Package sqlite3 provides access to the SQLite library, version 3.
5
+//
6
+// The package has no exported API.
7
+// It registers a driver for the standard Go database/sql package.
8
+//
9
+//	import _ "code.google.com/p/gosqlite/sqlite3"
10
+//
11
+// (For an alternate, earlier API, see the code.google.com/p/gosqlite/sqlite package.)
12
+package sqlite
13
+
14
+/*
15
+#cgo LDFLAGS: -lsqlite3
16
+
17
+#include <sqlite3.h>
18
+#include <stdlib.h>
19
+
20
+// These wrappers are necessary because SQLITE_TRANSIENT
21
+// is a pointer constant, and cgo doesn't translate them correctly.
22
+// The definition in sqlite3.h is:
23
+//
24
+// typedef void (*sqlite3_destructor_type)(void*);
25
+// #define SQLITE_STATIC      ((sqlite3_destructor_type)0)
26
+// #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)
27
+
28
+static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
29
+	return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
30
+}
31
+static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
32
+	return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
33
+}
34
+
35
+*/
36
+import "C"
37
+
38
+import (
39
+	"database/sql"
40
+	"database/sql/driver"
41
+	"errors"
42
+	"fmt"
43
+	"io"
44
+	"strings"
45
+	"time"
46
+	"unsafe"
47
+)
48
+
49
+func init() {
50
+	sql.Register("sqlite3", impl{})
51
+}
52
+
53
+type errno int
54
+
55
+func (e errno) Error() string {
56
+	s := errText[e]
57
+	if s == "" {
58
+		return fmt.Sprintf("errno %d", int(e))
59
+	}
60
+	return s
61
+}
62
+
63
+var (
64
+	errError      error = errno(1)   //    /* SQL error or missing database */
65
+	errInternal   error = errno(2)   //    /* Internal logic error in SQLite */
66
+	errPerm       error = errno(3)   //    /* Access permission denied */
67
+	errAbort      error = errno(4)   //    /* Callback routine requested an abort */
68
+	errBusy       error = errno(5)   //    /* The database file is locked */
69
+	errLocked     error = errno(6)   //    /* A table in the database is locked */
70
+	errNoMem      error = errno(7)   //    /* A malloc() failed */
71
+	errReadOnly   error = errno(8)   //    /* Attempt to write a readonly database */
72
+	errInterrupt  error = errno(9)   //    /* Operation terminated by sqlite3_interrupt()*/
73
+	errIOErr      error = errno(10)  //    /* Some kind of disk I/O error occurred */
74
+	errCorrupt    error = errno(11)  //    /* The database disk image is malformed */
75
+	errFull       error = errno(13)  //    /* Insertion failed because database is full */
76
+	errCantOpen   error = errno(14)  //    /* Unable to open the database file */
77
+	errEmpty      error = errno(16)  //    /* Database is empty */
78
+	errSchema     error = errno(17)  //    /* The database schema changed */
79
+	errTooBig     error = errno(18)  //    /* String or BLOB exceeds size limit */
80
+	errConstraint error = errno(19)  //    /* Abort due to constraint violation */
81
+	errMismatch   error = errno(20)  //    /* Data type mismatch */
82
+	errMisuse     error = errno(21)  //    /* Library used incorrectly */
83
+	errNolfs      error = errno(22)  //    /* Uses OS features not supported on host */
84
+	errAuth       error = errno(23)  //    /* Authorization denied */
85
+	errFormat     error = errno(24)  //    /* Auxiliary database format error */
86
+	errRange      error = errno(25)  //    /* 2nd parameter to sqlite3_bind out of range */
87
+	errNotDB      error = errno(26)  //    /* File opened that is not a database file */
88
+	stepRow             = errno(100) //   /* sqlite3_step() has another row ready */
89
+	stepDone            = errno(101) //   /* sqlite3_step() has finished executing */
90
+)
91
+
92
+var errText = map[errno]string{
93
+	1:   "SQL error or missing database",
94
+	2:   "Internal logic error in SQLite",
95
+	3:   "Access permission denied",
96
+	4:   "Callback routine requested an abort",
97
+	5:   "The database file is locked",
98
+	6:   "A table in the database is locked",
99
+	7:   "A malloc() failed",
100
+	8:   "Attempt to write a readonly database",
101
+	9:   "Operation terminated by sqlite3_interrupt()*/",
102
+	10:  "Some kind of disk I/O error occurred",
103
+	11:  "The database disk image is malformed",
104
+	12:  "NOT USED. Table or record not found",
105
+	13:  "Insertion failed because database is full",
106
+	14:  "Unable to open the database file",
107
+	15:  "NOT USED. Database lock protocol error",
108
+	16:  "Database is empty",
109
+	17:  "The database schema changed",
110
+	18:  "String or BLOB exceeds size limit",
111
+	19:  "Abort due to constraint violation",
112
+	20:  "Data type mismatch",
113
+	21:  "Library used incorrectly",
114
+	22:  "Uses OS features not supported on host",
115
+	23:  "Authorization denied",
116
+	24:  "Auxiliary database format error",
117
+	25:  "2nd parameter to sqlite3_bind out of range",
118
+	26:  "File opened that is not a database file",
119
+	100: "sqlite3_step() has another row ready",
120
+	101: "sqlite3_step() has finished executing",
121
+}
122
+
123
+type impl struct{}
124
+
125
+func (impl) Open(name string) (driver.Conn, error) {
126
+	if C.sqlite3_threadsafe() == 0 {
127
+		return nil, errors.New("sqlite library was not compiled for thread-safe operation")
128
+	}
129
+
130
+	var db *C.sqlite3
131
+	cname := C.CString(name)
132
+	defer C.free(unsafe.Pointer(cname))
133
+	rv := C.sqlite3_open_v2(cname, &db,
134
+		C.SQLITE_OPEN_FULLMUTEX|
135
+			C.SQLITE_OPEN_READWRITE|
136
+			C.SQLITE_OPEN_CREATE,
137
+		nil)
138
+	if rv != 0 {
139
+		return nil, errno(rv)
140
+	}
141
+	if db == nil {
142
+		return nil, errors.New("sqlite succeeded without returning a database")
143
+	}
144
+	return &conn{db: db}, nil
145
+}
146
+
147
+type conn struct {
148
+	db     *C.sqlite3
149
+	closed bool
150
+	tx     bool
151
+}
152
+
153
+func (c *conn) error(rv C.int) error {
154
+	if rv == 0 {
155
+		return nil
156
+	}
157
+	if rv == 21 || c.closed {
158
+		return errno(rv)
159
+	}
160
+	return errors.New(errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
161
+}
162
+
163
+func (c *conn) Prepare(cmd string) (driver.Stmt, error) {
164
+	if c.closed {
165
+		panic("database/sql/driver: misuse of sqlite driver: Prepare after Close")
166
+	}
167
+	cmdstr := C.CString(cmd)
168
+	defer C.free(unsafe.Pointer(cmdstr))
169
+	var s *C.sqlite3_stmt
170
+	var tail *C.char
171
+	rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &s, &tail)
172
+	if rv != 0 {
173
+		return nil, c.error(rv)
174
+	}
175
+	return &stmt{c: c, stmt: s, sql: cmd, t0: time.Now()}, nil
176
+}
177
+
178
+func (c *conn) Close() error {
179
+	if c.closed {
180
+		panic("database/sql/driver: misuse of sqlite driver: multiple Close")
181
+	}
182
+	c.closed = true
183
+	rv := C.sqlite3_close(c.db)
184
+	c.db = nil
185
+	return c.error(rv)
186
+}
187
+
188
+func (c *conn) exec(cmd string) error {
189
+	cstring := C.CString(cmd)
190
+	defer C.free(unsafe.Pointer(cstring))
191
+	rv := C.sqlite3_exec(c.db, cstring, nil, nil, nil)
192
+	return c.error(rv)
193
+}
194
+
195
+func (c *conn) Begin() (driver.Tx, error) {
196
+	if c.tx {
197
+		panic("database/sql/driver: misuse of sqlite driver: multiple Tx")
198
+	}
199
+	if err := c.exec("BEGIN TRANSACTION"); err != nil {
200
+		return nil, err
201
+	}
202
+	c.tx = true
203
+	return &tx{c}, nil
204
+}
205
+
206
+type tx struct {
207
+	c *conn
208
+}
209
+
210
+func (t *tx) Commit() error {
211
+	if t.c == nil || !t.c.tx {
212
+		panic("database/sql/driver: misuse of sqlite driver: extra Commit")
213
+	}
214
+	t.c.tx = false
215
+	err := t.c.exec("COMMIT TRANSACTION")
216
+	t.c = nil
217
+	return err
218
+}
219
+
220
+func (t *tx) Rollback() error {
221
+	if t.c == nil || !t.c.tx {
222
+		panic("database/sql/driver: misuse of sqlite driver: extra Rollback")
223
+	}
224
+	t.c.tx = false
225
+	err := t.c.exec("ROLLBACK")
226
+	t.c = nil
227
+	return err
228
+}
229
+
230
+type stmt struct {
231
+	c        *conn
232
+	stmt     *C.sqlite3_stmt
233
+	err      error
234
+	t0       time.Time
235
+	sql      string
236
+	args     string
237
+	closed   bool
238
+	rows     bool
239
+	colnames []string
240
+	coltypes []string
241
+}
242
+
243
+func (s *stmt) Close() error {
244
+	if s.rows {
245
+		panic("database/sql/driver: misuse of sqlite driver: Close with active Rows")
246
+	}
247
+	if s.closed {
248
+		panic("database/sql/driver: misuse of sqlite driver: double Close of Stmt")
249
+	}
250
+	s.closed = true
251
+	rv := C.sqlite3_finalize(s.stmt)
252
+	if rv != 0 {
253
+		return s.c.error(rv)
254
+	}
255
+	return nil
256
+}
257
+
258
+func (s *stmt) NumInput() int {
259
+	if s.closed {
260
+		panic("database/sql/driver: misuse of sqlite driver: NumInput after Close")
261
+	}
262
+	return int(C.sqlite3_bind_parameter_count(s.stmt))
263
+}
264
+
265
+func (s *stmt) reset() error {
266
+	return s.c.error(C.sqlite3_reset(s.stmt))
267
+}
268
+
269
+func (s *stmt) start(args []driver.Value) error {
270
+	if err := s.reset(); err != nil {
271
+		return err
272
+	}
273
+
274
+	n := int(C.sqlite3_bind_parameter_count(s.stmt))
275
+	if n != len(args) {
276
+		return fmt.Errorf("incorrect argument count for command: have %d want %d", len(args), n)
277
+	}
278
+
279
+	for i, v := range args {
280
+		var str string
281
+		switch v := v.(type) {
282
+		case nil:
283
+			if rv := C.sqlite3_bind_null(s.stmt, C.int(i+1)); rv != 0 {
284
+				return s.c.error(rv)
285
+			}
286
+			continue
287
+
288
+		case float64:
289
+			if rv := C.sqlite3_bind_double(s.stmt, C.int(i+1), C.double(v)); rv != 0 {
290
+				return s.c.error(rv)
291
+			}
292
+			continue
293
+
294
+		case int64:
295
+			if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(v)); rv != 0 {
296
+				return s.c.error(rv)
297
+			}
298
+			continue
299
+
300
+		case []byte:
301
+			var p *byte
302
+			if len(v) > 0 {
303
+				p = &v[0]
304
+			}
305
+			if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
306
+				return s.c.error(rv)
307
+			}
308
+			continue
309
+
310
+		case bool:
311
+			var vi int64
312
+			if v {
313
+				vi = 1
314
+			}
315
+			if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(vi)); rv != 0 {
316
+				return s.c.error(rv)
317
+			}
318
+			continue
319
+
320
+		case time.Time:
321
+			str = v.UTC().Format(timefmt[0])
322
+
323
+		case string:
324
+			str = v
325
+
326
+		default:
327
+			str = fmt.Sprint(v)
328
+		}
329
+
330
+		cstr := C.CString(str)
331
+		rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
332
+		C.free(unsafe.Pointer(cstr))
333
+		if rv != 0 {
334
+			return s.c.error(rv)
335
+		}
336
+	}
337
+
338
+	return nil
339
+}
340
+
341
+func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
342
+	if s.closed {
343
+		panic("database/sql/driver: misuse of sqlite driver: Exec after Close")
344
+	}
345
+	if s.rows {
346
+		panic("database/sql/driver: misuse of sqlite driver: Exec with active Rows")
347
+	}
348
+
349
+	err := s.start(args)
350
+	if err != nil {
351
+		return nil, err
352
+	}
353
+
354
+	rv := C.sqlite3_step(s.stmt)
355
+	if errno(rv) != stepDone {
356
+		if rv == 0 {
357
+			rv = 21 // errMisuse
358
+		}
359
+		return nil, s.c.error(rv)
360
+	}
361
+
362
+	id := int64(C.sqlite3_last_insert_rowid(s.c.db))
363
+	rows := int64(C.sqlite3_changes(s.c.db))
364
+	return &result{id, rows}, nil
365
+}
366
+
367
+func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
368
+	if s.closed {
369
+		panic("database/sql/driver: misuse of sqlite driver: Query after Close")
370
+	}
371
+	if s.rows {
372
+		panic("database/sql/driver: misuse of sqlite driver: Query with active Rows")
373
+	}
374
+
375
+	err := s.start(args)
376
+	if err != nil {
377
+		return nil, err
378
+	}
379
+
380
+	s.rows = true
381
+	if s.colnames == nil {
382
+		n := int64(C.sqlite3_column_count(s.stmt))
383
+		s.colnames = make([]string, n)
384
+		s.coltypes = make([]string, n)
385
+		for i := range s.colnames {
386
+			s.colnames[i] = C.GoString(C.sqlite3_column_name(s.stmt, C.int(i)))
387
+			s.coltypes[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(s.stmt, C.int(i))))
388
+		}
389
+	}
390
+	return &rows{s}, nil
391
+}
392
+
393
+type rows struct {
394
+	s *stmt
395
+}
396
+
397
+func (r *rows) Columns() []string {
398
+	if r.s == nil {
399
+		panic("database/sql/driver: misuse of sqlite driver: Columns of closed Rows")
400
+	}
401
+	return r.s.colnames
402
+}
403
+
404
+const maxslice = 1<<31 - 1
405
+
406
+var timefmt = []string{
407
+	"2006-01-02 15:04:05.999999999",
408
+	"2006-01-02T15:04:05.999999999",
409
+	"2006-01-02 15:04:05",
410
+	"2006-01-02T15:04:05",
411
+	"2006-01-02 15:04",
412
+	"2006-01-02T15:04",
413
+	"2006-01-02",
414
+}
415
+
416
+func (r *rows) Next(dst []driver.Value) error {
417
+	if r.s == nil {
418
+		panic("database/sql/driver: misuse of sqlite driver: Next of closed Rows")
419
+	}
420
+
421
+	rv := C.sqlite3_step(r.s.stmt)
422
+	if errno(rv) != stepRow {
423
+		if errno(rv) == stepDone {
424
+			return io.EOF
425
+		}
426
+		if rv == 0 {
427
+			rv = 21
428
+		}
429
+		return r.s.c.error(rv)
430
+	}
431
+
432
+	for i := range dst {
433
+		switch typ := C.sqlite3_column_type(r.s.stmt, C.int(i)); typ {
434
+		default:
435
+			return fmt.Errorf("unexpected sqlite3 column type %d", typ)
436
+		case C.SQLITE_INTEGER:
437
+			val := int64(C.sqlite3_column_int64(r.s.stmt, C.int(i)))
438
+			switch r.s.coltypes[i] {
439
+			case "timestamp", "datetime":
440
+				dst[i] = time.Unix(val, 0).UTC()
441
+			case "boolean":
442
+				dst[i] = val > 0
443
+			default:
444
+				dst[i] = val
445
+			}
446
+
447
+		case C.SQLITE_FLOAT:
448
+			dst[i] = float64(C.sqlite3_column_double(r.s.stmt, C.int(i)))
449
+
450
+		case C.SQLITE_BLOB, C.SQLITE_TEXT:
451
+			n := int(C.sqlite3_column_bytes(r.s.stmt, C.int(i)))
452
+			var b []byte
453
+			if n > 0 {
454
+				p := C.sqlite3_column_blob(r.s.stmt, C.int(i))
455
+				b = (*[maxslice]byte)(unsafe.Pointer(p))[:n]
456
+			}
457
+			dst[i] = b
458
+			switch r.s.coltypes[i] {
459
+			case "timestamp", "datetime":
460
+				dst[i] = time.Time{}
461
+				s := string(b)
462
+				for _, f := range timefmt {
463
+					if t, err := time.Parse(f, s); err == nil {
464
+						dst[i] = t
465
+						break
466
+					}
467
+				}
468
+			}
469
+
470
+		case C.SQLITE_NULL:
471
+			dst[i] = nil
472
+		}
473
+	}
474
+	return nil
475
+}
476
+
477
+func (r *rows) Close() error {
478
+	if r.s == nil {
479
+		panic("database/sql/driver: misuse of sqlite driver: Close of closed Rows")
480
+	}
481
+	r.s.rows = false
482
+	r.s = nil
483
+	return nil
484
+}
485
+
486
+type result struct {
487
+	id   int64
488
+	rows int64
489
+}
490
+
491
+func (r *result) LastInsertId() (int64, error) {
492
+	return r.id, nil
493
+}
494
+
495
+func (r *result) RowsAffected() (int64, error) {
496
+	return r.rows, nil
497
+}