diff --git a/src/common.jl b/src/common.jl index a0e4f7f..798abbd 100644 --- a/src/common.jl +++ b/src/common.jl @@ -1,29 +1,24 @@ const Cmode_t = Cushort "Generic structure used for passing keys and data in and out of the database." -type MDBValue +immutable MDBValue size::Csize_t # size of the data item data::Ptr{Void} # address of the data item end MDBValue() = MDBValue(zero(Csize_t), C_NULL) -function MDBValue(val) - val_size = sizeof(val) - val = isa(val, Number) ? typeof(val)[val] : val - return MDBValue(val_size, pointer(val)) -end +MDBValue(_::Void) = MDBValue() +MDBValue{T<:Union{String,Array}}(val::T) = MDBValue(sizeof(val), pointer(val)) +MDBValue{T<:Number}(val::T) = error("can not wrap a $T in MDBValue, use a $T array instead") +MDBValue(val) = MDBValue(sizeof(val), pointer_from_objref(val)) -function convert{T}(::Type{T}, mdb_val_ref::Ref{MDBValue}) - mdb_val = mdb_val_ref[] - return if T <: String - unsafe_string(convert(Ptr{UInt8}, mdb_val.data), mdb_val.size) - elseif T <: AbstractVector - E = eltype(T) - nvals = floor(Int, mdb_val.size/sizeof(E)) - unsafe_wrap(T, convert(Ptr{E}, mdb_val.data), nvals) - else - unsafe_load(convert(Ptr{T}, mdb_val.data)) - end +convert{T}(::Type{T}, mdb_val_ref::Ref{MDBValue}) = _convert(T, mdb_val_ref[]) +_convert(::Type{String}, mdb_val::MDBValue) = unsafe_string(convert(Ptr{UInt8}, mdb_val.data), mdb_val.size) +function _convert{T}(::Type{Vector{T}}, mdb_val::MDBValue) + nvals = floor(Int, mdb_val.size/sizeof(T)) + unsafe_wrap(Array, convert(Ptr{T}, mdb_val.data), nvals) end +_convert{T<:Number}(::Type{T}, mdb_val::MDBValue) = unsafe_wrap(Array, convert(Ptr{T}, mdb_val.data), 1)[1] +_convert{T}(::Type{T}, mdb_val::MDBValue) = unsafe_load(convert(Ptr{T}, mdb_val.data), 1) # Environment Flags # ----------------- @@ -117,9 +112,9 @@ end """LMDB exception type""" immutable LMDBError <: Exception code::Cint - msg::AbstractString - LMDBError(code::Cint) = new(code, errormsg(code)) + msg::String end +LMDBError(code::Cint) = LMDBError(code, errormsg(code)) Base.show(io::IO, err::LMDBError) = print(io, "Code[$(err.code)]: $(err.msg)") """ Check if binary flag is set in provided value""" diff --git a/src/cur.jl b/src/cur.jl index 2486db3..f04244b 100644 --- a/src/cur.jl +++ b/src/cur.jl @@ -65,6 +65,7 @@ end This function retrieves key/data pairs from the database. """ +get{K<:Number,T}(cur::Cursor, key::K, ::Type{T}) = get(cur, [key], T) function get{T}(cur::Cursor, key, ::Type{T}, op::CursorOps=FIRST) # Setup parameters mdb_key_ref = Ref(MDBValue(key)) @@ -85,6 +86,12 @@ end This function stores key/data pairs into the database. The cursor is positioned at the new item, or on failure usually near it. """ function put!(cur::Cursor, key, val; flags::Cuint = zero(Cuint)) + if isa(key, Number) || isa(val, Number) + key = isa(key, Number) ? [key] : key + val = isa(val, Number) ? [val] : val + return put!(cur, key, val; flags=flags) + end + mdb_key_ref = Ref(MDBValue(key)) mdb_val_ref = Ref(MDBValue(val)) diff --git a/src/dbi.jl b/src/dbi.jl index 7b91ba9..f520636 100644 --- a/src/dbi.jl +++ b/src/dbi.jl @@ -68,6 +68,12 @@ end "Store items into a database" function put!(txn::Transaction, dbi::DBI, key, val; flags::Cuint = zero(Cuint)) + if isa(key, Number) || isa(val, Number) + key = isa(key, Number) ? [key] : key + val = isa(val, Number) ? [val] : val + return put!(txn, dbi, key, val; flags=flags) + end + mdb_key_ref = Ref(MDBValue(key)) mdb_val_ref = Ref(MDBValue(val)) @@ -81,6 +87,12 @@ end "Delete items from a database" function delete!(txn::Transaction, dbi::DBI, key, val=C_NULL) + if isa(key, Number) || isa(val, Number) + key = isa(key, Number) ? [key] : key + val = isa(val, Number) ? [val] : val + return delete!(txn, dbi, key, val) + end + mdb_key_ref = Ref(MDBValue(key)) mdb_val_ref = (val === C_NULL) ? C_NULL : Ref(MDBValue(val)) @@ -92,7 +104,8 @@ function delete!(txn::Transaction, dbi::DBI, key, val=C_NULL) return ret end -"Get items from a database" +"Get items from a database. Returned values are safe to access as long as the transaction is not closed." +get{K<:Number,T}(txn::Transaction, dbi::DBI, key::K, ::Type{T}) = get(txn, dbi, [key], T) function get{T}(txn::Transaction, dbi::DBI, key, ::Type{T}) # Setup parameters mdb_key_ref = Ref(MDBValue(key)) diff --git a/test/common.jl b/test/common.jl index d36685d..654b88e 100644 --- a/test/common.jl +++ b/test/common.jl @@ -19,7 +19,7 @@ module LMDB_Common val = [1233] T = eltype(val) val_size = sizeof(val) - mdb_val = MDBValue(val[1]) + mdb_val = MDBValue(val) @test val_size == mdb_val.size nvals = floor(Int, mdb_val.size/sizeof(T)) value = unsafe_wrap(Array, convert(Ptr{T}, mdb_val.data), nvals) diff --git a/test/dbi.jl b/test/dbi.jl index abb9b80..8dab604 100644 --- a/test/dbi.jl +++ b/test/dbi.jl @@ -3,62 +3,156 @@ module LMDB_DBI using Base.Test const dbname = "testdb" - key = 10 - val = "key value is " - # Create dir - mkdir(dbname) - try + immutable MyType + intval::Int + boolval::Bool + end + + function test_int_str() + println(" int keys, string values") + key = 10 + val = "key value is " - # Procedural style - env = create() + # Create dir + mkdir(dbname) try - open(env, dbname) - txn = start(env) - dbi = open(txn) - put!(txn, dbi, key+1, val*string(key+1)) - put!(txn, dbi, key, val*string(key)) - put!(txn, dbi, key+2, key+2) - put!(txn, dbi, key+3, [key, key+1, key+2]) - @test isopen(txn) - commit(txn) - @test !isopen(txn) - close(env, dbi) - @test !isopen(dbi) + # Procedural style + env = create() + try + open(env, dbname) + txn = start(env) + dbi = open(txn) + put!(txn, dbi, key+1, val*string(key+1)) + println(" put value for key $(key+1): $(val*string(key+1))") + put!(txn, dbi, key, val*string(key)) + println(" put value for key $(key): $(val*string(key))") + @test isopen(txn) + commit(txn) + @test !isopen(txn) + close(env, dbi) + @test !isopen(dbi) + finally + close(env) + end + @test !isopen(env) + + # Block style + create() do env + set!(env, LMDB.NOSYNC) + open(env, dbname) + start(env) do txn + open(txn, flags = Cuint(LMDB.REVERSEKEY)) do dbi + k = key + value = get(txn, dbi, k, String) + println(" got value for key $(k): $(value)") + @test value == val*string(k) + delete!(txn, dbi, k) + k += 1 + value = get(txn, dbi, k, String) + println(" got value for key $(k): $(value)") + @test value == val*string(k) + delete!(txn, dbi, k, value) + @test_throws LMDBError get(txn, dbi, k, String) + end + end + end finally - close(env) + rm(dbname, recursive=true) end - @test !isopen(env) + end + + function test_str_int() + println(" string keys, int values") + key = "key1" + val = 10 + + # Create dir + mkdir(dbname) + try + env = create() + try + open(env, dbname) + txn = start(env) + dbi = open(txn) + put!(txn, dbi, key, val) + println(" put value for key $(key): $(val)") + @test isopen(txn) + commit(txn) + @test !isopen(txn) + close(env, dbi) + @test !isopen(dbi) + finally + close(env) + end + @test !isopen(env) - # Block style - create() do env - set!(env, LMDB.NOSYNC) - open(env, dbname) - start(env) do txn - open(txn, flags = Cuint(LMDB.REVERSEKEY)) do dbi - k = key - value = get(txn, dbi, k, String) - println("Got value for key $(k): $(value)") - @test value == val*string(k) - delete!(txn, dbi, k) - k += 1 - value = get(txn, dbi, k, String) - println("Got value for key $(k): $(value)") - @test value == val*string(k) - delete!(txn, dbi, k, value) - @test_throws LMDBError get(txn, dbi, k, String) - k += 1 - value = get(txn, dbi, k, Int) - println("Got value for key $(k): $(value)") - @test value == k - k += 1 - value = get(txn, dbi, k, Vector{Int}) - println("Got value for key $(k): $(value)") - @test value == [key, key+1, key+2] + # Block style + create() do env + set!(env, LMDB.NOSYNC) + open(env, dbname) + start(env) do txn + open(txn, flags = Cuint(LMDB.REVERSEKEY)) do dbi + k = key + value = get(txn, dbi, k, Int) + println(" got value for key $(k): $(value)") + @test value == val + delete!(txn, dbi, k) + @test_throws LMDBError get(txn, dbi, k, Int) + end end end + finally + rm(dbname, recursive=true) end - finally - rm(dbname, recursive=true) end + + function test_struct_struct() + println(" struct keys, struct values") + key = MyType(10, true) + val = MyType(11, false) + + # Create dir + mkdir(dbname) + try + env = create() + try + open(env, dbname) + txn = start(env) + dbi = open(txn) + put!(txn, dbi, key, val) + println(" put value for key $(key): $(val)") + @test isopen(txn) + commit(txn) + @test !isopen(txn) + close(env, dbi) + @test !isopen(dbi) + finally + close(env) + end + @test !isopen(env) + + # Block style + create() do env + set!(env, LMDB.NOSYNC) + open(env, dbname) + start(env) do txn + open(txn, flags = Cuint(LMDB.REVERSEKEY)) do dbi + k = key + value = get(txn, dbi, k, MyType) + println(" got value for key $(k): $(value)") + @test value == val + delete!(txn, dbi, k) + @test_throws LMDBError get(txn, dbi, k, MyType) + end + end + end + finally + rm(dbname, recursive=true) + end + end + + test_int_str() + test_str_int() + test_struct_struct() end diff --git a/test/env.jl b/test/env.jl index ffd06d1..331ce5c 100644 --- a/test/env.jl +++ b/test/env.jl @@ -10,6 +10,9 @@ module LMDB_Env @test env[:Readers] == 126 @test env[:KeySize] == 511 @test env[:Flags] == 0 + @test path(env) == "" + + show(STDOUT, env) # Manipulate flags @test !isflagset(env[:Flags], Cuint(LMDB.NOSYNC)) @@ -30,9 +33,12 @@ module LMDB_Env ret = open(env, dbname) @test ret[1] == 0 + @show env + # Close environment close(env) @test !isopen(env) + @test_throws LMDBError close(env) # do block create() do env @@ -43,4 +49,4 @@ module LMDB_Env finally rm(dbname, recursive=true) end -end \ No newline at end of file +end