New upstream version 0.0~git20191015.0.afcd224
Sascha Steinbiss
4 years ago
0 | 0 | language: go |
1 | 1 | sudo: false |
2 | 2 | go: |
3 | - 1.8 | |
4 | - 1.7 | |
5 | - tip | |
3 | - 1.7.x | |
4 | - 1.8.x | |
5 | - 1.9.x | |
6 | - 1.10.x | |
7 | - 1.11.x | |
8 | - 1.12.x | |
9 | - 1.13.x | |
10 | - master | |
6 | 11 | before_install: |
7 | 12 | - go get github.com/mattn/goveralls |
8 | 13 | - go get github.com/stretchr/testify |
6 | 6 | ) |
7 | 7 | |
8 | 8 | var cache = map[string]*C.char{} |
9 | var mLock sync.Mutex | |
9 | var mLock sync.RWMutex | |
10 | 10 | |
11 | 11 | func getCStringFromCache(str string) *C.char { |
12 | mLock.RLock() | |
12 | 13 | cStr, ok := cache[str] |
14 | mLock.RUnlock() | |
13 | 15 | if ok { |
14 | 16 | return cStr |
15 | 17 | } |
18 | ||
16 | 19 | mLock.Lock() |
17 | 20 | cStr, ok = cache[str] |
18 | if ok { | |
19 | return cStr | |
21 | if !ok { | |
22 | cStr = cString(str) | |
23 | cache[str] = cStr | |
20 | 24 | } |
21 | cStr = cString(str) | |
22 | cache[str] = cStr | |
23 | 25 | mLock.Unlock() |
24 | 26 | return cStr |
25 | 27 | } |
0 | package sophia | |
1 | ||
2 | import ( | |
3 | "math/rand" | |
4 | "runtime" | |
5 | "sync" | |
6 | "testing" | |
7 | ) | |
8 | ||
9 | const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
10 | ||
11 | func TestGetCStringFromCacheRace(t *testing.T) { | |
12 | var wg sync.WaitGroup | |
13 | workers := runtime.GOMAXPROCS(-1) * 8 | |
14 | const iterations = 100 | |
15 | for i := 0; i < workers; i ++ { | |
16 | wg.Add(1) | |
17 | go func(){ | |
18 | defer wg.Done() | |
19 | for i := 0; i < iterations; i++ { | |
20 | getCStringFromCache(generateString(32)) | |
21 | } | |
22 | }() | |
23 | } | |
24 | wg.Wait() | |
25 | } | |
26 | ||
27 | func generateString(n int) string { | |
28 | b := make([]byte, n) | |
29 | for i := range b { | |
30 | b[i] = letters[rand.Intn(len(letters))] | |
31 | } | |
32 | return string(b) | |
33 | } |
33 | 33 | // Cursor iterates over key-values in a database. |
34 | 34 | type Cursor struct { |
35 | 35 | ptr unsafe.Pointer |
36 | doc *Document | |
36 | doc Document | |
37 | 37 | closed bool |
38 | 38 | } |
39 | 39 | |
54 | 54 | |
55 | 55 | // Next fetches the next row for the cursor |
56 | 56 | // Returns next row if it exists else it will return nil |
57 | func (cur *Cursor) Next() *Document { | |
57 | func (cur *Cursor) Next() Document { | |
58 | 58 | if cur.closed { |
59 | return nil | |
59 | return Document{} | |
60 | 60 | } |
61 | 61 | ptr := spGet(cur.ptr, cur.doc.ptr) |
62 | 62 | if ptr == nil { |
63 | return nil | |
63 | return Document{} | |
64 | 64 | } |
65 | 65 | cur.doc.ptr = ptr |
66 | 66 | return cur.doc |
58 | 58 | |
59 | 59 | func testCursorError(t *testing.T, db *Database) { |
60 | 60 | doc := db.Document() |
61 | require.NotNil(t, doc) | |
61 | require.False(t, doc.IsEmpty()) | |
62 | 62 | |
63 | 63 | cursor, err := db.Cursor(doc) |
64 | 64 | require.Nil(t, err) |
68 | 68 | require.Nil(t, err) |
69 | 69 | |
70 | 70 | require.Error(t, cursor.Close()) |
71 | require.Nil(t, cursor.Next()) | |
71 | d := cursor.Next() | |
72 | require.True(t, d.IsEmpty()) | |
72 | 73 | } |
73 | 74 | |
74 | 75 | func testCursor(t *testing.T, db *Database, start, count int64, valueTemplate string) { |
75 | 76 | id := start |
76 | 77 | doc := db.Document() |
77 | require.NotNil(t, doc) | |
78 | require.False(t, doc.IsEmpty()) | |
78 | 79 | if start != 0 { |
79 | 80 | doc.SetInt("key", start) |
80 | 81 | } |
90 | 91 | size int |
91 | 92 | counter int64 |
92 | 93 | ) |
93 | for d := cursor.Next(); d != nil; d = cursor.Next() { | |
94 | for d := cursor.Next(); !d.IsEmpty(); d = cursor.Next() { | |
94 | 95 | require.Equal(t, id, d.GetInt("key")) |
95 | 96 | require.Equal(t, fmt.Sprintf(valueTemplate, id), d.GetString("value", &size)) |
96 | 97 | counter++ |
101 | 102 | |
102 | 103 | func testReverseCursor(t *testing.T, db *Database, count int64, valueTemplate string) { |
103 | 104 | doc := db.Document() |
104 | require.NotNil(t, doc) | |
105 | require.False(t, doc.IsEmpty()) | |
105 | 106 | |
106 | 107 | doc.Set(CursorOrder, LTE) |
107 | 108 | |
116 | 117 | size int |
117 | 118 | id int64 = count - 1 |
118 | 119 | ) |
119 | for d := cursor.Next(); d != nil; d = cursor.Next() { | |
120 | for d := cursor.Next(); !d.IsEmpty(); d = cursor.Next() { | |
120 | 121 | require.Equal(t, id, d.GetInt("key")) |
121 | 122 | require.Equal(t, fmt.Sprintf(valueTemplate, id), d.GetString("value", &size)) |
122 | 123 | id-- |
184 | 185 | prefixStr = prefixStr[:len(prefixStr)-1] |
185 | 186 | |
186 | 187 | doc := db.Document() |
187 | require.NotNil(t, doc) | |
188 | require.False(t, doc.IsEmpty()) | |
188 | 189 | |
189 | 190 | doc.Set(CursorPrefix, prefixStr) |
190 | 191 | |
202 | 203 | |
203 | 204 | // get row that match prefix |
204 | 205 | d := cursor.Next() |
205 | require.NotNil(t, d) | |
206 | require.False(t, d.IsEmpty()) | |
206 | 207 | require.Equal(t, prefixStr, d.GetString(keyPath, &size)) |
207 | 208 | require.Equal(t, fmt.Sprintf(valueTemplate, prefix/base), d.GetString(valuePath, &size)) |
208 | 209 | |
209 | 210 | // get rows that have additional symbol at the end |
210 | for d := cursor.Next(); d != nil; d = cursor.Next() { | |
211 | for d := cursor.Next(); !d.IsEmpty(); d = cursor.Next() { | |
211 | 212 | id := prefix + count |
212 | 213 | require.Equal(t, strconv.FormatInt(int64(id), base), d.GetString(keyPath, &size)) |
213 | 214 | require.Equal(t, fmt.Sprintf(valueTemplate, id), d.GetString(valuePath, &size)) |
15 | 15 | } |
16 | 16 | |
17 | 17 | // Get retrieves the row for the set of keys. |
18 | func (d *dataStore) Get(doc *Document) (*Document, error) { | |
18 | func (d *dataStore) Get(doc Document) (Document, error) { | |
19 | 19 | ptr := spGet(d.ptr, doc.ptr) |
20 | 20 | if ptr == nil { |
21 | 21 | err := d.env.Error() |
22 | 22 | if err == nil { |
23 | return nil, ErrNotFound | |
23 | return Document{}, ErrNotFound | |
24 | 24 | } |
25 | return nil, fmt.Errorf("failed Get document: err=%v", err) | |
25 | return Document{}, fmt.Errorf("failed Get document: err=%v", err) | |
26 | 26 | } |
27 | 27 | return newDocument(ptr, 0), nil |
28 | 28 | } |
29 | 29 | |
30 | 30 | // Set sets the row of the set of keys. |
31 | func (d *dataStore) Set(doc *Document) error { | |
31 | func (d *dataStore) Set(doc Document) error { | |
32 | 32 | if !spSet(d.ptr, doc.ptr) { |
33 | 33 | return fmt.Errorf("failed Set document: err=%v", d.env.Error()) |
34 | 34 | } |
36 | 36 | } |
37 | 37 | |
38 | 38 | // Upsert sets the row of the set of keys. |
39 | func (d *dataStore) Upsert(doc *Document) error { | |
39 | func (d *dataStore) Upsert(doc Document) error { | |
40 | 40 | if !spUpsert(d.ptr, doc.ptr) { |
41 | 41 | return fmt.Errorf("failed Upsert document: err=%v", d.env.Error()) |
42 | 42 | } |
44 | 44 | } |
45 | 45 | |
46 | 46 | // Delete deletes row with specified set of keys. |
47 | func (d *dataStore) Delete(doc *Document) error { | |
47 | func (d *dataStore) Delete(doc Document) error { | |
48 | 48 | if !spDelete(d.ptr, doc.ptr) { |
49 | 49 | return fmt.Errorf("failed Delete document: err=%v", d.env.Error()) |
50 | 50 | } |
85 | 85 | } |
86 | 86 | |
87 | 87 | // Document creates a Document for a single or multi-statement transactions |
88 | func (db *Database) Document() *Document { | |
88 | func (db *Database) Document() Document { | |
89 | 89 | ptr := spDocument(db.ptr) |
90 | 90 | if ptr == nil { |
91 | return nil | |
91 | return Document{} | |
92 | 92 | } |
93 | 93 | return newDocument(ptr, db.fieldsCount) |
94 | 94 | } |
95 | 95 | |
96 | 96 | // Cursor returns a Cursor for iterating over rows in the database |
97 | func (db *Database) Cursor(doc *Document) (*Cursor, error) { | |
98 | if nil == doc { | |
97 | func (db *Database) Cursor(doc Document) (*Cursor, error) { | |
98 | if doc.IsEmpty() { | |
99 | 99 | return nil, errors.New("failed to create cursor: nil Document") |
100 | 100 | } |
101 | 101 | cPtr := spCursor(db.env.ptr) |
35 | 35 | defer env.Close() |
36 | 36 | |
37 | 37 | doc := db.Document() |
38 | require.NotNil(t, doc) | |
38 | require.False(t, doc.IsEmpty()) | |
39 | 39 | require.Nil(t, env.Error()) |
40 | 40 | } |
41 | 41 | |
63 | 63 | require.NotNil(t, db) |
64 | 64 | |
65 | 65 | doc := db.Document() |
66 | require.NotNil(t, doc) | |
66 | require.False(t, doc.IsEmpty()) | |
67 | 67 | require.Nil(t, env.Error()) |
68 | 68 | defer doc.Free() |
69 | 69 | |
100 | 100 | defer env.Close() |
101 | 101 | |
102 | 102 | doc := db.Document() |
103 | require.NotNil(t, doc) | |
103 | require.False(t, doc.IsEmpty()) | |
104 | 104 | require.Nil(t, env.Error()) |
105 | 105 | defer doc.Free() |
106 | 106 | |
130 | 130 | require.NotNil(t, db) |
131 | 131 | |
132 | 132 | doc := db.Document() |
133 | require.NotNil(t, doc) | |
133 | require.False(t, doc.IsEmpty()) | |
134 | 134 | require.Nil(t, env.Error()) |
135 | 135 | defer doc.Free() |
136 | 136 | |
138 | 138 | |
139 | 139 | d, err := db.Get(doc) |
140 | 140 | require.NotNil(t, err) |
141 | require.Nil(t, d) | |
141 | require.True(t, d.IsEmpty()) | |
142 | 142 | } |
143 | 143 | |
144 | 144 | func TestDatabaseGet(t *testing.T) { |
168 | 168 | defer env.Close() |
169 | 169 | |
170 | 170 | doc := db.Document() |
171 | require.NotNil(t, doc) | |
171 | require.False(t, doc.IsEmpty()) | |
172 | 172 | require.Nil(t, env.Error()) |
173 | 173 | |
174 | 174 | require.True(t, doc.SetString(keyPath, expectedKey)) |
178 | 178 | doc.Free() |
179 | 179 | |
180 | 180 | doc = db.Document() |
181 | require.NotNil(t, doc) | |
181 | require.False(t, doc.IsEmpty()) | |
182 | 182 | require.Nil(t, env.Error()) |
183 | 183 | defer doc.Free() |
184 | 184 | |
186 | 186 | |
187 | 187 | d, err := db.Get(doc) |
188 | 188 | require.Nil(t, err) |
189 | require.NotNil(t, d) | |
189 | require.False(t, d.IsEmpty()) | |
190 | 190 | d.Destroy() |
191 | 191 | } |
192 | 192 | |
210 | 210 | require.NotNil(t, db) |
211 | 211 | |
212 | 212 | doc := db.Document() |
213 | require.NotNil(t, doc) | |
213 | require.False(t, doc.IsEmpty()) | |
214 | 214 | require.Nil(t, env.Error()) |
215 | 215 | defer doc.Free() |
216 | 216 | |
246 | 246 | defer env.Close() |
247 | 247 | |
248 | 248 | doc := db.Document() |
249 | require.NotNil(t, doc) | |
249 | require.False(t, doc.IsEmpty()) | |
250 | 250 | require.Nil(t, env.Error()) |
251 | 251 | |
252 | 252 | require.True(t, doc.SetString(keyPath, expectedKey)) |
256 | 256 | doc.Free() |
257 | 257 | |
258 | 258 | doc = db.Document() |
259 | require.NotNil(t, doc) | |
259 | require.False(t, doc.IsEmpty()) | |
260 | 260 | require.Nil(t, env.Error()) |
261 | 261 | |
262 | 262 | require.True(t, doc.SetString(keyPath, expectedKey)) |
265 | 265 | doc.Free() |
266 | 266 | |
267 | 267 | doc = db.Document() |
268 | require.NotNil(t, doc) | |
268 | require.False(t, doc.IsEmpty()) | |
269 | 269 | require.Nil(t, env.Error()) |
270 | 270 | defer doc.Free() |
271 | 271 | |
274 | 274 | d, err := db.Get(doc) |
275 | 275 | require.NotNil(t, err) |
276 | 276 | require.Equal(t, ErrNotFound, err) |
277 | require.Nil(t, d) | |
277 | require.True(t, d.IsEmpty()) | |
278 | 278 | } |
279 | 279 | |
280 | 280 | func TestDatabaseWithCustomSchema(t *testing.T) { |
308 | 308 | const expectedValue int64 = 73 |
309 | 309 | |
310 | 310 | doc := db.Document() |
311 | require.NotNil(t, doc) | |
311 | require.False(t, doc.IsEmpty()) | |
312 | 312 | require.True(t, doc.Set(keyPath, expectedKey)) |
313 | 313 | require.True(t, doc.Set(valuePath, expectedValue)) |
314 | 314 | |
317 | 317 | require.Nil(t, err) |
318 | 318 | |
319 | 319 | doc = db.Document() |
320 | require.NotNil(t, doc) | |
320 | require.False(t, doc.IsEmpty()) | |
321 | 321 | require.True(t, doc.Set(keyPath, expectedKey)) |
322 | 322 | |
323 | 323 | d, err := db.Get(doc) |
324 | 324 | doc.Free() |
325 | 325 | require.Nil(t, err) |
326 | require.NotNil(t, d) | |
327 | ||
328 | defer d.Destroy() | |
326 | require.False(t, d.IsEmpty()) | |
329 | 327 | |
330 | 328 | require.Equal(t, expectedKey, d.GetInt(keyPath)) |
331 | 329 | require.Equal(t, expectedValue, d.GetInt(valuePath)) |
332 | ||
333 | doc = db.Document() | |
334 | require.NotNil(t, doc) | |
330 | d.Destroy() | |
331 | ||
332 | doc = db.Document() | |
333 | require.False(t, doc.IsEmpty()) | |
335 | 334 | require.Nil(t, env.Error()) |
336 | 335 | |
337 | 336 | require.True(t, doc.Set(keyPath, expectedKey)) |
340 | 339 | doc.Free() |
341 | 340 | |
342 | 341 | doc = db.Document() |
343 | require.NotNil(t, doc) | |
342 | require.False(t, doc.IsEmpty()) | |
344 | 343 | require.Nil(t, env.Error()) |
345 | 344 | defer doc.Free() |
346 | 345 | |
349 | 348 | d, err = db.Get(doc) |
350 | 349 | require.NotNil(t, err) |
351 | 350 | require.Equal(t, ErrNotFound, err) |
352 | require.Nil(t, d) | |
351 | require.True(t, d.IsEmpty()) | |
353 | 352 | } |
354 | 353 | |
355 | 354 | func TestDatabaseWithMultipleKeys(t *testing.T) { |
393 | 392 | ) |
394 | 393 | |
395 | 394 | doc := db.Document() |
396 | require.NotNil(t, doc) | |
395 | require.False(t, doc.IsEmpty()) | |
397 | 396 | require.True(t, doc.Set(key1Path, expectedKey1)) |
398 | 397 | require.True(t, doc.Set(key2Path, expectedKey2)) |
399 | 398 | require.True(t, doc.Set(key3Path, expectedKey3)) |
404 | 403 | require.Nil(t, err) |
405 | 404 | |
406 | 405 | doc = db.Document() |
407 | require.NotNil(t, doc) | |
406 | require.False(t, doc.IsEmpty()) | |
408 | 407 | require.True(t, doc.Set(key1Path, expectedKey1)) |
409 | 408 | require.True(t, doc.Set(key2Path, expectedKey2)) |
410 | 409 | require.True(t, doc.Set(key3Path, expectedKey3)) |
412 | 411 | d, err := db.Get(doc) |
413 | 412 | doc.Free() |
414 | 413 | require.Nil(t, err) |
415 | require.NotNil(t, d) | |
416 | ||
417 | defer d.Destroy() | |
414 | require.False(t, d.IsEmpty()) | |
418 | 415 | |
419 | 416 | require.Equal(t, expectedKey1, d.GetInt(key1Path)) |
420 | 417 | require.Equal(t, expectedKey2, d.GetInt(key2Path)) |
421 | 418 | require.Equal(t, expectedKey3, d.GetInt(key3Path)) |
422 | 419 | require.Equal(t, expectedValue, d.GetInt(valuePath)) |
423 | ||
424 | doc = db.Document() | |
425 | require.NotNil(t, doc) | |
420 | d.Destroy() | |
421 | ||
422 | doc = db.Document() | |
423 | require.False(t, doc.IsEmpty()) | |
426 | 424 | require.Nil(t, env.Error()) |
427 | 425 | |
428 | 426 | require.True(t, doc.Set(key1Path, expectedKey1)) |
433 | 431 | doc.Free() |
434 | 432 | |
435 | 433 | doc = db.Document() |
436 | require.NotNil(t, doc) | |
434 | require.False(t, doc.IsEmpty()) | |
437 | 435 | require.Nil(t, env.Error()) |
438 | 436 | defer doc.Free() |
439 | 437 | |
444 | 442 | d, err = db.Get(doc) |
445 | 443 | require.NotNil(t, err) |
446 | 444 | require.Equal(t, ErrNotFound, err) |
447 | require.Nil(t, d) | |
445 | require.True(t, d.IsEmpty()) | |
448 | 446 | } |
449 | 447 | |
450 | 448 | func TestDatabaseUseSomeDocumentsAtTheSameTime(t *testing.T) { |
494 | 492 | doc2.Free() |
495 | 493 | |
496 | 494 | doc := db.Document() |
497 | require.NotNil(t, doc) | |
495 | require.False(t, doc.IsEmpty()) | |
498 | 496 | |
499 | 497 | doc.Set(keyPath, expectedKey1) |
500 | 498 | d, err := db.Get(doc) |
501 | 499 | doc.Free() |
502 | require.NotNil(t, d) | |
500 | require.False(t, d.IsEmpty()) | |
503 | 501 | require.Nil(t, err) |
504 | 502 | size := 0 |
505 | 503 | require.Equal(t, expectedValue1, d.GetString(valuePath, &size)) |
507 | 505 | d.Destroy() |
508 | 506 | |
509 | 507 | doc = db.Document() |
510 | require.NotNil(t, doc) | |
508 | require.False(t, doc.IsEmpty()) | |
511 | 509 | |
512 | 510 | doc.Set(keyPath, expectedKey2) |
513 | 511 | d, err = db.Get(doc) |
514 | 512 | doc.Free() |
515 | require.NotNil(t, d) | |
513 | require.False(t, d.IsEmpty()) | |
516 | 514 | require.Nil(t, err) |
517 | 515 | size = 0 |
518 | 516 | require.Equal(t, expectedValue2, d.GetString(valuePath, &size)) |
548 | 546 | require.Nil(t, db.Delete(doc)) |
549 | 547 | } |
550 | 548 | |
551 | // ATTN - This benchmark don't show real performance | |
552 | // It is just a long running tests | |
553 | 549 | func BenchmarkDatabaseSet(b *testing.B) { |
554 | 550 | const ( |
555 | 551 | keyPath = "key" |
591 | 587 | value: fmt.Sprintf(ValueTemplate, i), |
592 | 588 | }) |
593 | 589 | } |
590 | ||
594 | 591 | b.ResetTimer() |
595 | 592 | for i := 0; i < b.N; i++ { |
596 | 593 | index := i % RecordsCountBench |
597 | 594 | doc := db.Document() |
598 | require.True(b, doc.Set(keyPath, pairs[index].key)) | |
599 | require.True(b, doc.Set(valuePath, pairs[index].value)) | |
600 | require.Nil(b, db.Set(doc)) | |
595 | doc.Set(keyPath, pairs[index].key) | |
596 | doc.Set(valuePath, pairs[index].value) | |
597 | db.Set(doc) | |
601 | 598 | doc.Free() |
602 | 599 | } |
603 | 600 | } |
604 | 601 | |
605 | // ATTN - This benchmark don't show real performance | |
606 | // It is just a long running tests | |
607 | 602 | func BenchmarkDatabaseGet(b *testing.B) { |
608 | 603 | const ( |
609 | 604 | keyPath = "key" |
654 | 649 | doc.Free() |
655 | 650 | } |
656 | 651 | |
657 | var size int | |
658 | 652 | b.ResetTimer() |
659 | 653 | for i := 0; i < b.N; i++ { |
660 | 654 | index := i % RecordsCountBench |
661 | 655 | doc := db.Document() |
662 | 656 | require.True(b, doc.Set(keyPath, pairs[index].key)) |
663 | d, err := db.Get(doc) | |
657 | d, _ := db.Get(doc) | |
664 | 658 | doc.Free() |
665 | require.Nil(b, err) | |
666 | require.Equal(b, pairs[index].value, d.GetString(valuePath, &size)) | |
667 | 659 | d.Destroy() |
668 | 660 | } |
669 | 661 | } |
7 | 7 | // Document is a representation of a row in a database. |
8 | 8 | // Destroy should be called after Document usage. |
9 | 9 | type Document struct { |
10 | *varStore | |
10 | varStore | |
11 | 11 | } |
12 | 12 | |
13 | func newDocument(ptr unsafe.Pointer, size int) *Document { | |
14 | return &Document{ | |
13 | func newDocument(ptr unsafe.Pointer, size int) Document { | |
14 | return Document{ | |
15 | 15 | varStore: newVarStore(ptr, size), |
16 | 16 | } |
17 | 17 | } |
13 | 13 | // Take it's name from sophia |
14 | 14 | // Usually object with same features are called 'database' |
15 | 15 | type Environment struct { |
16 | *varStore | |
16 | varStore | |
17 | 17 | } |
18 | 18 | |
19 | 19 | // NewEnvironment creates a new environment for opening a database. |
9 | 9 | "github.com/pzhin/go-sophia" |
10 | 10 | ) |
11 | 11 | |
12 | func upsertCallback(count int, src []unsafe.Pointer, srcSize uint32, | |
13 | upsert []unsafe.Pointer, upsertSize uint32, result []unsafe.Pointer, | |
14 | resultSize uint32, arg unsafe.Pointer) int { | |
12 | func upsertCallback(count int, src []unsafe.Pointer, srcSize []uint32, | |
13 | upsert []unsafe.Pointer, upsertSize []uint32, result []unsafe.Pointer, | |
14 | resultSize []uint32, arg unsafe.Pointer) int { | |
15 | ||
16 | if src == nil { | |
17 | return 0 | |
18 | } | |
15 | 19 | ca := *(*uint32)(src[1]) |
16 | 20 | cb := *(*uint32)(upsert[1]) |
17 | 21 | cret := ca + cb |
78 | 82 | log.Fatal(err) |
79 | 83 | } |
80 | 84 | |
81 | for d := cursor.Next(); d != nil; d = cursor.Next() { | |
85 | for d := cursor.Next(); !d.IsEmpty(); d = cursor.Next() { | |
82 | 86 | var size int |
83 | 87 | fmt.Println(d.GetString("key", &size), ":", d.GetInt("value"), ":", d.GetInt("value2"), ":", size) |
84 | 88 | } |
83 | 83 | require.Nil(t, err) |
84 | 84 | |
85 | 85 | doc = db.Document() |
86 | require.NotNil(t, doc) | |
86 | require.False(t, doc.IsEmpty()) | |
87 | 87 | require.True(t, doc.Set(keyPath, expectedKey)) |
88 | 88 | |
89 | 89 | d, err := tx.Get(doc) |
90 | 90 | doc.Free() |
91 | 91 | |
92 | require.NotNil(t, d) | |
92 | require.False(t, d.IsEmpty()) | |
93 | 93 | require.Nil(t, err) |
94 | 94 | |
95 | 95 | var size int |
134 | 134 | doc.Free() |
135 | 135 | |
136 | 136 | doc = db.Document() |
137 | require.NotNil(t, doc) | |
137 | require.False(t, doc.IsEmpty()) | |
138 | 138 | require.True(t, doc.Set(keyPath, expectedKey)) |
139 | 139 | |
140 | 140 | d, err := db.Get(doc) |
141 | 141 | doc.Free() |
142 | require.NotNil(t, d) | |
142 | require.False(t, d.IsEmpty()) | |
143 | 143 | require.Nil(t, err) |
144 | 144 | |
145 | 145 | var size int |
151 | 151 | require.Nil(t, err) |
152 | 152 | |
153 | 153 | doc = db.Document() |
154 | require.NotNil(t, doc) | |
154 | require.False(t, doc.IsEmpty()) | |
155 | 155 | require.True(t, doc.Set(keyPath, expectedKey)) |
156 | 156 | require.Nil(t, tx.Delete(doc)) |
157 | 157 | doc.Free() |
158 | 158 | |
159 | 159 | doc = db.Document() |
160 | require.NotNil(t, doc) | |
160 | require.False(t, doc.IsEmpty()) | |
161 | 161 | require.True(t, doc.Set(keyPath, expectedKey)) |
162 | 162 | d, err = tx.Get(doc) |
163 | 163 | doc.Free() |
164 | require.Nil(t, d) | |
164 | require.True(t, d.IsEmpty()) | |
165 | 165 | require.NotNil(t, err) |
166 | 166 | |
167 | 167 | require.Equal(t, TxOk, tx.Commit()) |
208 | 208 | require.True(t, doc.Set(keyPath, expectedKey)) |
209 | 209 | |
210 | 210 | d, err := db.Get(doc) |
211 | require.Nil(t, d) | |
211 | require.True(t, d.IsEmpty()) | |
212 | 212 | require.Equal(t, ErrNotFound, err) |
213 | 213 | doc.Free() |
214 | 214 | } |
277 | 277 | d, err := db.Get(doc) |
278 | 278 | doc.Free() |
279 | 279 | require.Nil(t, err) |
280 | require.NotNil(t, d) | |
280 | require.False(t, d.IsEmpty()) | |
281 | 281 | value := d.GetString(valuePath, &size) |
282 | 282 | require.Equal(t, expectedValue1, value) |
283 | 283 | d.Destroy() |
33 | 33 | // UpsertFunc golang equivalent of upsert_callback. |
34 | 34 | // Should return 0 in case of success, otherwise -1. |
35 | 35 | type UpsertFunc func(count int, |
36 | src []unsafe.Pointer, srcSize uint32, | |
37 | upsert []unsafe.Pointer, upsertSize uint32, | |
38 | result []unsafe.Pointer, resultSize uint32, | |
36 | src []unsafe.Pointer, srcSize []uint32, | |
37 | upsert []unsafe.Pointer, upsertSize []uint32, | |
38 | result []unsafe.Pointer, resultSize []uint32, | |
39 | 39 | arg unsafe.Pointer) int |
40 | 40 | |
41 | 41 | //export goUpsertCall |
98 | 98 | indexPtr := &index |
99 | 99 | |
100 | 100 | upsertMap[indexPtr] = func(count C.int, |
101 | src **C.char, srcSize *C.uint32_t, | |
102 | upsert **C.char, upsertSize *C.uint32_t, | |
103 | result **C.char, resultSize *C.uint32_t, | |
101 | srcC **C.char, srcSizesC *C.uint32_t, | |
102 | upsertC **C.char, upsertSizesC *C.uint32_t, | |
103 | resultC **C.char, resultSizesC *C.uint32_t, | |
104 | 104 | arg unsafe.Pointer) C.int { |
105 | 105 | |
106 | if src == nil { | |
107 | return C.int(0) | |
108 | } | |
109 | var sSize uint32 | |
110 | if srcSize != nil { | |
111 | sSize = uint32(*srcSize) | |
112 | } | |
113 | uSize := uint32(*upsertSize) | |
114 | rSize := uint32(*resultSize) | |
115 | 106 | countN := int(count) |
116 | 107 | |
117 | 108 | // We receive C pointer to pointer which can be interpreted as an array of pointers. |
118 | 109 | // Here we cast C pointer to pointer to Go slice of pointers. |
119 | slice1 := (*[1 << 4]unsafe.Pointer)(unsafe.Pointer(src))[:countN:countN] | |
120 | slice2 := (*[1 << 4]unsafe.Pointer)(unsafe.Pointer(upsert))[:countN:countN] | |
121 | slice3 := (*[1 << 4]unsafe.Pointer)(unsafe.Pointer(result))[:countN:countN] | |
110 | upsertSizes := (*[16]uint32)(unsafe.Pointer(upsertSizesC))[:countN] | |
111 | resultSizes := (*[16]uint32)(unsafe.Pointer(resultSizesC))[:countN] | |
112 | ||
113 | upsert := (*[16]unsafe.Pointer)(unsafe.Pointer(upsertC))[:countN] | |
114 | result := (*[16]unsafe.Pointer)(unsafe.Pointer(resultC))[:countN] | |
115 | ||
116 | var src []unsafe.Pointer | |
117 | var srcSizes []uint32 | |
118 | if srcC != nil { | |
119 | srcSizes = (*[16]uint32)(unsafe.Pointer(srcSizesC))[:countN] | |
120 | src = (*[16]unsafe.Pointer)(unsafe.Pointer(srcC))[:countN] | |
121 | } | |
122 | 122 | |
123 | 123 | res := upsertFunc(countN, |
124 | slice1, sSize, | |
125 | slice2, uSize, | |
126 | slice3, rSize, | |
124 | src, srcSizes, | |
125 | upsert, upsertSizes, | |
126 | result, resultSizes, | |
127 | 127 | arg) |
128 | 128 | |
129 | 129 | return C.int(res) |
0 | 0 | package sophia |
1 | 1 | |
2 | 2 | import ( |
3 | "fmt" | |
4 | "io/ioutil" | |
3 | 5 | "os" |
4 | 6 | "testing" |
5 | 7 | "unsafe" |
6 | ||
7 | "io/ioutil" | |
8 | 8 | |
9 | 9 | "github.com/stretchr/testify/require" |
10 | 10 | ) |
117 | 117 | } |
118 | 118 | |
119 | 119 | func upsertCallback(count int, |
120 | src []unsafe.Pointer, srcSize uint32, | |
121 | upsert []unsafe.Pointer, upsertSize uint32, | |
122 | result []unsafe.Pointer, resultSize uint32, | |
120 | src []unsafe.Pointer, srcSize []uint32, | |
121 | upsert []unsafe.Pointer, upsertSize []uint32, | |
122 | result []unsafe.Pointer, resultSize []uint32, | |
123 | 123 | arg unsafe.Pointer) int { |
124 | ||
125 | if src == nil { | |
126 | return 0 | |
127 | } | |
124 | 128 | var a uint32 = *(*uint32)(src[1]) |
125 | 129 | var b uint32 = *(*uint32)(upsert[1]) |
126 | 130 | ret := a + b |
130 | 134 | } |
131 | 135 | |
132 | 136 | func upsertCallbackWithArg(count int, |
133 | src []unsafe.Pointer, srcSize uint32, | |
134 | upsert []unsafe.Pointer, upsertSize uint32, | |
135 | result []unsafe.Pointer, resultSize uint32, | |
137 | src []unsafe.Pointer, srcSize []uint32, | |
138 | upsert []unsafe.Pointer, upsertSize []uint32, | |
139 | result []unsafe.Pointer, resultSize []uint32, | |
136 | 140 | arg unsafe.Pointer) int { |
141 | ||
142 | if src == nil { | |
143 | return 0 | |
144 | } | |
137 | 145 | var a uint32 = *(*uint32)(src[1]) |
138 | 146 | var b uint32 = *(*uint32)(upsert[1]) |
139 | 147 | var c uint32 = *(*uint32)(arg) |
170 | 178 | require.Nil(t, env.Open()) |
171 | 179 | defer env.Close() |
172 | 180 | doc := db.Document() |
173 | require.NotNil(t, doc) | |
181 | require.False(t, doc.IsEmpty()) | |
174 | 182 | require.True(t, doc.Set("key", 1)) |
175 | 183 | require.True(t, doc.Set("id", 1)) |
176 | 184 | require.NotNil(t, db.Upsert(doc)) |
177 | 185 | } |
186 | ||
187 | func TestDatabaseUpsertArguments(t *testing.T) { | |
188 | const keyPath = "key" | |
189 | const valuePath = "id" | |
190 | tmpDir, err := ioutil.TempDir("", "sophia_test") | |
191 | require.Nil(t, err) | |
192 | defer os.RemoveAll(tmpDir) | |
193 | ||
194 | env, err := NewEnvironment() | |
195 | require.Nil(t, err) | |
196 | require.NotNil(t, env) | |
197 | ||
198 | require.True(t, env.SetString(EnvironmentPath, tmpDir)) | |
199 | ||
200 | schema := &Schema{} | |
201 | require.Nil(t, schema.AddKey(keyPath, FieldTypeUInt32)) | |
202 | require.Nil(t, schema.AddValue(valuePath, FieldTypeUInt32)) | |
203 | ||
204 | db, err := env.NewDatabase(DatabaseConfig{ | |
205 | Name: "test_database", | |
206 | Schema: schema, | |
207 | Upsert: func(count int, | |
208 | src []unsafe.Pointer, srcSize []uint32, | |
209 | upsert []unsafe.Pointer, upsertSize []uint32, | |
210 | result []unsafe.Pointer, resultSize []uint32, | |
211 | arg unsafe.Pointer) int { | |
212 | ||
213 | if count != 4 { | |
214 | panic(fmt.Sprintf("count should be equals 4, got: %v", count)) | |
215 | } | |
216 | ||
217 | if src != nil { | |
218 | if len(src) != count { | |
219 | panic(fmt.Sprintf("length of src should be equals count, got: %v", len(src))) | |
220 | } | |
221 | if len(srcSize) != count { | |
222 | panic(fmt.Sprintf("length of srcSize should be equals count, got: %v", len(src))) | |
223 | } | |
224 | } | |
225 | ||
226 | if len(upsert) != count { | |
227 | panic(fmt.Sprintf("length of upsert should be equals count, got: %v", len(upsert))) | |
228 | } | |
229 | if len(upsertSize) != count { | |
230 | panic(fmt.Sprintf("length of upsertSize should be equals count, got: %v", len(upsertSize))) | |
231 | } | |
232 | ||
233 | if len(result) != count { | |
234 | panic(fmt.Sprintf("length of result should be equals count, got: %v", len(result))) | |
235 | } | |
236 | if len(resultSize) != count { | |
237 | panic(fmt.Sprintf("length of resultSize should be equals count, got: %v", len(resultSize))) | |
238 | } | |
239 | ||
240 | if arg != nil { | |
241 | panic(fmt.Sprintf("arg should be nil, got: %#v", arg)) | |
242 | } | |
243 | ||
244 | return upsertCallback(count, src, srcSize, upsert, upsertSize, result, resultSize, arg) | |
245 | }, | |
246 | }) | |
247 | require.Nil(t, err) | |
248 | require.NotNil(t, db) | |
249 | ||
250 | require.Nil(t, env.Open()) | |
251 | defer env.Close() | |
252 | ||
253 | const key uint32 = 1234 | |
254 | const value uint32 = 1 | |
255 | expectedUpsertArgs := []struct { | |
256 | key, value uint32 | |
257 | }{ | |
258 | {key: key, value: value}, | |
259 | {key: key, value: value * 2}, | |
260 | {key: key, value: value * 3}, | |
261 | {key: key, value: value * 4}, | |
262 | {key: key, value: value * 5}, | |
263 | {key: key, value: value * 6}, | |
264 | } | |
265 | ||
266 | for _, expected := range expectedUpsertArgs { | |
267 | doc := db.Document() | |
268 | doc.Set(keyPath, key) | |
269 | doc.Set(valuePath, value) | |
270 | require.Nil(t, db.Upsert(doc)) | |
271 | doc.Free() | |
272 | ||
273 | doc = db.Document() | |
274 | doc.Set(keyPath, key) | |
275 | ||
276 | result, err := db.Get(doc) | |
277 | require.Nil(t, err) | |
278 | require.NotNil(t, result) | |
279 | ||
280 | require.EqualValues(t, expected.key, result.GetInt(keyPath)) | |
281 | require.EqualValues(t, expected.key, result.GetInt(keyPath)) | |
282 | } | |
283 | } |
16 | 16 | pointers []unsafe.Pointer |
17 | 17 | } |
18 | 18 | |
19 | func newVarStore(ptr unsafe.Pointer, size int) *varStore { | |
20 | return &varStore{ | |
21 | ptr: ptr, | |
22 | pointers: make([]unsafe.Pointer, 0, size), | |
19 | func newVarStore(ptr unsafe.Pointer, size int) varStore { | |
20 | ret := varStore{ptr: ptr} | |
21 | if size > 0 { | |
22 | ret.pointers = make([]unsafe.Pointer, 0, size) | |
23 | 23 | } |
24 | return ret | |
25 | } | |
26 | ||
27 | func (s *varStore) IsEmpty() bool { | |
28 | return s.ptr == nil | |
24 | 29 | } |
25 | 30 | |
26 | 31 | // TODO :: implement custom types |