-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add Serialize and Deserialize support #1089
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
1d8534a
Add Serialize and Deserialize support
otoolep ecd2680
Merge pull request #1 from mattn/master
otoolep 613b879
Add support for sqlite_omit_deserialize
otoolep 7069591
Fix up imports
otoolep 0cc635c
Add missing import
otoolep 17653b6
go fmt fixes
otoolep b3777b7
Use existing build tag style
otoolep a594404
Merge remote-tracking branch 'origin' into add-serialize-deserialize
otoolep d4d2212
No Serialize with libsqlite tests
otoolep d607e75
More libsqlite3 test fixups
otoolep 596960a
Update expected test output
otoolep 21b8dd9
Fix up build tags
otoolep 03ba6ac
Serialize() should return an explicit error
otoolep 464b7f3
Add missing rows.Close()
otoolep 7035040
More build tag fixes
otoolep ba8ffa0
Update build tags
otoolep 270263f
Update README for build tags
otoolep 491eab1
AND logic for build tags
otoolep ac1d03c
Remove blank line
otoolep c6d6296
More memory management fixes
otoolep 99fe433
Update sqlite3_opt_serialize.go
otoolep 7ed94a9
Update sqlite3_opt_serialize_omit.go
otoolep 03bb574
More fixes
otoolep 9dd368b
Merge branch 'add-serialize-deserialize' of github.com:otoolep/go-sql…
otoolep d2e464d
Remove unused const
otoolep 469d82c
Update sqlite3_opt_serialize_omit.go
otoolep a404658
Fix up build tags
otoolep 0740ee9
Fix error messages
otoolep 4516d61
Remove unused import
otoolep a0dc1ef
Import "errors"
otoolep f1d96d7
go fmt
otoolep ec6e50d
Fix copy 'n' paste error
otoolep 3bbd5c1
Address latest comments
otoolep 96afe68
Correct typo
otoolep 6b69194
Write Serialization test using database/sql
otoolep 8873883
Use existing style for build tags
otoolep 2f60ff2
Update test as per PR comments
otoolep 9ec6bc6
Fix up Serialize unit test
otoolep File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// +build !libsqlite3 sqlite_serialize | ||
|
||
package sqlite3 | ||
|
||
/* | ||
#ifndef USE_LIBSQLITE3 | ||
#include <sqlite3-binding.h> | ||
#else | ||
#include <sqlite3.h> | ||
#endif | ||
#include <stdlib.h> | ||
#include <stdint.h> | ||
*/ | ||
import "C" | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"reflect" | ||
"unsafe" | ||
) | ||
|
||
// Serialize returns a byte slice that is a serialization of the database. | ||
// | ||
// See https://www.sqlite.org/c3ref/serialize.html | ||
func (c *SQLiteConn) Serialize(schema string) ([]byte, error) { | ||
if schema == "" { | ||
schema = "main" | ||
} | ||
var zSchema *C.char | ||
zSchema = C.CString(schema) | ||
defer C.free(unsafe.Pointer(zSchema)) | ||
|
||
var sz C.sqlite3_int64 | ||
ptr := C.sqlite3_serialize(c.db, zSchema, &sz, 0) | ||
if ptr == nil { | ||
return nil, fmt.Errorf("serialize failed") | ||
} | ||
defer C.sqlite3_free(unsafe.Pointer(ptr)) | ||
|
||
if sz > C.sqlite3_int64(math.MaxInt) { | ||
otoolep marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil, fmt.Errorf("serialized database is too large (%d bytes)", sz) | ||
} | ||
|
||
cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ | ||
Data: uintptr(unsafe.Pointer(ptr)), | ||
Len: int(sz), | ||
Cap: int(sz), | ||
})) | ||
|
||
res := make([]byte, int(sz)) | ||
copy(res, cBuf) | ||
return res, nil | ||
} | ||
|
||
// Deserialize causes the connection to disconnect from the current database and | ||
// then re-open as an in-memory database based on the contents of the byte slice. | ||
// | ||
// See https://www.sqlite.org/c3ref/deserialize.html | ||
func (c *SQLiteConn) Deserialize(b []byte, schema string) error { | ||
if schema == "" { | ||
schema = "main" | ||
} | ||
var zSchema *C.char | ||
zSchema = C.CString(schema) | ||
defer C.free(unsafe.Pointer(zSchema)) | ||
|
||
tmpBuf := (*C.uchar)(C.sqlite3_malloc64(C.sqlite3_uint64(len(b)))) | ||
cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ | ||
Data: uintptr(unsafe.Pointer(tmpBuf)), | ||
Len: len(b), | ||
Cap: len(b), | ||
})) | ||
copy(cBuf, b) | ||
|
||
rc := C.sqlite3_deserialize(c.db, zSchema, tmpBuf, C.sqlite3_int64(len(b)), | ||
C.sqlite3_int64(len(b)), C.SQLITE_DESERIALIZE_FREEONCLOSE) | ||
if rc != C.SQLITE_OK { | ||
return fmt.Errorf("deserialize failed with return %v", rc) | ||
otoolep marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// +build libsqlite3,!sqlite_serialize | ||
|
||
package sqlite3 | ||
|
||
import ( | ||
"errors" | ||
) | ||
|
||
/* | ||
#cgo CFLAGS: -DSQLITE_OMIT_DESERIALIZE | ||
*/ | ||
import "C" | ||
|
||
func (c *SQLiteConn) Serialize(schema string) ([]byte, error) { | ||
return nil, errors.New("sqlite3: Serialize requires the sqlite_serialize build tag when using the libsqlite3 build tag") | ||
} | ||
|
||
func (c *SQLiteConn) Deserialize(b []byte, schema string) error { | ||
return errors.New("sqlite3: Deserialize requires the sqlite_serialize build tag when using the libsqlite3 build tag") | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// +build !libsqlite3 sqlite_serialize | ||
|
||
package sqlite3 | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"os" | ||
"testing" | ||
) | ||
|
||
func TestSerializeDeserialize(t *testing.T) { | ||
// Connect to the source database. | ||
srcTempFilename := TempFilename(t) | ||
defer os.Remove(srcTempFilename) | ||
srcDb, err := sql.Open(driverName, srcTempFilename) | ||
if err != nil { | ||
t.Fatal("Failed to open the source database:", err) | ||
} | ||
defer srcDb.Close() | ||
err = srcDb.Ping() | ||
if err != nil { | ||
t.Fatal("Failed to connect to the source database:", err) | ||
} | ||
|
||
// Connect to the destination database. | ||
destTempFilename := TempFilename(t) | ||
defer os.Remove(destTempFilename) | ||
destDb, err := sql.Open(driverName, destTempFilename) | ||
if err != nil { | ||
t.Fatal("Failed to open the destination database:", err) | ||
} | ||
defer destDb.Close() | ||
err = destDb.Ping() | ||
if err != nil { | ||
t.Fatal("Failed to connect to the destination database:", err) | ||
} | ||
|
||
// Write data to source database. | ||
_, err = srcDb.Exec(`CREATE TABLE foo (name string)`) | ||
if err != nil { | ||
t.Fatal("Failed to create table in source database:", err) | ||
} | ||
_, err = srcDb.Exec(`INSERT INTO foo(name) VALUES("alice")`) | ||
if err != nil { | ||
t.Fatal("Failed to insert data into source database", err) | ||
} | ||
|
||
// Serialize the source database | ||
srcConn, err := srcDb.Conn(context.Background()) | ||
if err != nil { | ||
t.Fatal("Failed to get connection to source database:", err) | ||
} | ||
defer srcConn.Close() | ||
|
||
var serialized []byte | ||
if err := srcConn.Raw(func(raw interface{}) error { | ||
var err error | ||
serialized, err = raw.(*SQLiteConn).Serialize("") | ||
return err | ||
}); err != nil { | ||
t.Fatal("Failed to serialize source database:", err) | ||
} | ||
srcConn.Close() | ||
|
||
// Confirm that the destination database is initially empty. | ||
var destTableCount int | ||
err = destDb.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").Scan(&destTableCount) | ||
if err != nil { | ||
t.Fatal("Failed to check the destination table count:", err) | ||
} | ||
if destTableCount != 0 { | ||
t.Fatalf("The destination database is not empty; %v table(s) found.", destTableCount) | ||
} | ||
|
||
// Deserialize to destination database | ||
destConn, err := destDb.Conn(context.Background()) | ||
if err != nil { | ||
t.Fatal("Failed to get connection to destination database:", err) | ||
} | ||
defer destConn.Close() | ||
|
||
if err := destConn.Raw(func(raw interface{}) error { | ||
return raw.(*SQLiteConn).Deserialize(serialized, "") | ||
}); err != nil { | ||
t.Fatal("Failed to deserialize source database:", err) | ||
} | ||
destConn.Close() | ||
|
||
// Confirm that destination database has been loaded correctly. | ||
var destRowCount int | ||
err = destDb.QueryRow(`SELECT COUNT(*) FROM foo`).Scan(&destRowCount) | ||
if err != nil { | ||
t.Fatal("Failed to count rows in destination database table", err) | ||
} | ||
if destRowCount != 1 { | ||
t.Fatalf("Destination table does not have the expected records") | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.