Skip to content

Commit

Permalink
Cursor.Get handles key return values specicially for Set
Browse files Browse the repository at this point in the history
Fixes #96

Issue #56 illustrated that working with Go memory using "C-style" access
patterns does not work well in modern Go runtimes.  That problem was
thought to be properly addressed, but #96 showed a lingering case where
Set does not modify MDB_val key passed to mdb_cursor_get.

This commit addresses this lingering case by special-casing return value
processing for the Set operation of Cursor.Get.  If LMDB will not modify
the MDB_val, the returned key must use non-standard methods for copying
the value.  Likewise, when txn.RawRead is true the Cursor.Get must use a
special method to ensure that the returned key shares memory with the
corresponding input argument.
  • Loading branch information
bmatsuo committed Jan 28, 2017
1 parent 345b86e commit dca3057
Showing 1 changed file with 27 additions and 3 deletions.
30 changes: 27 additions & 3 deletions lmdb/cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ func (c *Cursor) DBI() DBI {
// returned by Get reference readonly sections of memory that must not be
// accessed after the transaction has terminated.
//
// In a Txn with RawRead set to true the Set op causes the returned key to
// share its memory with setkey (making it writable memory). In a Txn with
// RawRead set to false the Set op returns key values with memory distinct from
// setkey, as is always the case when using RawRead.
//
// Get ignores setval if setkey is empty.
//
// See mdb_cursor_get.
Expand All @@ -152,11 +157,30 @@ func (c *Cursor) Get(setkey, setval []byte, op uint) (key, val []byte, err error
*c.txn.val = C.MDB_val{}
return nil, nil, err
}
k := c.txn.bytes(c.txn.key)
v := c.txn.bytes(c.txn.val)

// When MDB_SET is passed to mdb_cursor_get its first argument will be
// returned unchanged. Unfortunately, the normal slice copy/extraction
// routines will be bad for the Go runtime when operating on Go memory
// (panic or potentially garbage memory reference).
if op == Set {
if c.txn.RawRead {
key = setkey
} else {
p := make([]byte, len(setkey))
copy(p, setkey)
key = p
}
} else {
key = c.txn.bytes(c.txn.key)
}
val = c.txn.bytes(c.txn.val)

// Clear transaction storage record storage area for future use and to
// prevent dangling references.
*c.txn.key = C.MDB_val{}
*c.txn.val = C.MDB_val{}
return k, v, nil

return key, val, nil
}

// getVal0 retrieves items from the database without using given key or value
Expand Down

0 comments on commit dca3057

Please sign in to comment.