Skip to content

Commit

Permalink
✨ Added insert of DataFrames.
Browse files Browse the repository at this point in the history
  • Loading branch information
hellemo committed Apr 5, 2020
1 parent 873d51c commit f643a63
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Access"
uuid = "14e0eef2-daed-11e8-2af2-4b4a56e59020"
authors = ["Lars Hellemo <[email protected]>"]
version = "0.1.0"
version = "0.2.0"

[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Expand Down
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
# Access

WIP: Query MS Access files via [JDBC](https://github.com/JuliaDatabases/JDBC.jl) and [UCanAccess](http://ucanaccess.sourceforge.net).
Query MS Access files via [JDBC](https://github.com/JuliaDatabases/JDBC.jl) and [UCanAccess](http://ucanaccess.sourceforge.net).

## Example

Read data from Access database

```
using Access
fn = "C:\\temp\\TestDatabase.accdb"
df = Access.querydb(fn,"SELECT * FROM Person WHERE BirthYear > 1980")
df = Access.query(fn,"SELECT * FROM Person WHERE BirthYear > 1980")
```

Modify data in Access database

```
# Read table content to DataFrame
StoredValues = Access.query(fn,"SELECT * FROM StoredValues;");
# Delete everything in table
Access.query!(fn,"DELETE * FROM StoredValues;")
# Re-insert data from DataFrame (inserting into table with same name as DataFrame)
Access.@insertdf(fn,StoredValues)
# Alternatively, specify table explicitly:
Access.insertdf(fn,StoredValues,"StoredValues")
```
58 changes: 56 additions & 2 deletions src/Access.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,69 @@ function __init__()

return nothing
end

function filecursor(fn;immediaterelease=false,noinactivitytimeout=false)
fn = replace(fn,"\\" => "/")
timeout =""
if noinactivitytimeout
timeout = ";inactivityTimeout=0"
end

cursor("jdbc:ucanaccess://$fn;immediatelyReleaseResources=$(string(immediaterelease))$(timeout)")
end


function querydb(fn,qstr)
function query(fn,qstr;imrelease=false,notimeout=true)
fn = replace(fn,"\\" => "/")
df = JDBC.load(DataFrame,cursor("jdbc:ucanaccess://$fn"), qstr)
df = JDBC.load(DataFrame,filecursor(fn;immediaterelease=imrelease,noinactivitytimeout=notimeout), qstr)
return df
end

# TODO Quote strings
function createInsert(df,tablename)
function qstring(s)
return s
end
function qstring(s::String)
return "\"" * s * "\""
end
function qkey(s)
return "[" * string(s) * "]"
end

ns = names(df)
SQLquery = String[]
for r in eachrow(df)
keys = join((qkey(n) for n in ns),", ")
vals = join((qstring(r[n]) for n in ns),", ")
push!(SQLquery,"INSERT INTO $tablename ($keys) VALUES ($vals);")
end
return SQLquery
end

function query!(fn,SQLQuery;imrelease=false,notimeout=false)
csr = filecursor(fn;immediaterelease=imrelease,noinactivitytimeout=notimeout)
execute!(csr,SQLQuery)
end

# Insert all rows from a DataFrame into table"
function insertdf(fn,df::DataFrame,tablename::String;imrelease=false,notimeout=true)

csr = filecursor(fn;immediaterelease=imrelease,noinactivitytimeout=notimeout)
ns = names(df)
SQLquery = createInsert(df,tablename)
for q in SQLquery
execute!(csr,q)
end
end

# Insert all rows from a DataFram into table with same name as DataFrame
macro insertdf(fn,df)
return quote
tn = $(string(df))
insertdf($(esc(fn)),$(esc(df)),tn)
end
end


end # module
42 changes: 39 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,49 @@
using Test
using Access

fn = cp(joinpath(@__DIR__,"TestDatabase.accdb"),joinpath(mktempdir(;cleanup=false),"Test.accdb"))

@testset "Read data from db" begin

fn = joinpath(@__DIR__,"TestDatabase.accdb")
df = Access.querydb(fn,"SELECT * FROM Person WHERE BirthYear > 1980")
# fn = joinpath(@__DIR__,"TestDatabase.accdb")
@test isfile(fn)
df = Access.query(fn,"SELECT * FROM Person WHERE BirthYear > 1980")

@test size(df,1) == 1 # One Row
@test df.FirstName[1] == "Slim"
@test df.LastName[1] == "Shady"
@test df.ID[1] == 3
end
end



@testset "Modify db data" begin

# Read table content to DataFrame
StoredValues = Access.query(fn,"SELECT * FROM StoredValues;");

# Delete everything in table
Access.query!(fn,"DELETE * FROM StoredValues;")

# Check table empty?
shouldbeemtpy = Access.query(fn,"SELECT * FROM StoredValues;");
@test size(shouldbeemtpy,1) == 0

# Re-insert data from DataFrame
Access.@insertdf(fn,StoredValues)
shouldnotbeemtpy = Access.query(fn,"SELECT * FROM StoredValues;");
@test size(shouldnotbeemtpy,1) > 0

# Check table content
@test shouldnotbeemtpy[6,4] == "Mashall Mathers"
@test shouldnotbeemtpy[2,3] 3.3

# Alternatively, specify table explicitly:
Access.query!(fn,"DELETE * FROM StoredValues;")
Access.insertdf(fn,StoredValues,"StoredValues")
shouldnotbeemtpy = Access.query(fn,"SELECT * FROM StoredValues;");
@test shouldnotbeemtpy[2,3] 3.3
end

# TODO: Figure out why this (and cleanup) fails
# rm(dirname(fn);recursive=true)

0 comments on commit f643a63

Please sign in to comment.