From f643a630470073f7d9a29cbd91246af856fbffbd Mon Sep 17 00:00:00 2001 From: Lars Hellemo Date: Sun, 5 Apr 2020 22:05:35 +0200 Subject: [PATCH] :sparkles: Added insert of DataFrames. --- Project.toml | 2 +- README.md | 22 ++++++++++++++++-- src/Access.jl | 58 ++++++++++++++++++++++++++++++++++++++++++++++-- test/runtests.jl | 42 ++++++++++++++++++++++++++++++++--- 4 files changed, 116 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index 8acc14c..5ae583f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Access" uuid = "14e0eef2-daed-11e8-2af2-4b4a56e59020" authors = ["Lars Hellemo "] -version = "0.1.0" +version = "0.2.0" [deps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" diff --git a/README.md b/README.md index 0ec2b38..b9dec20 100644 --- a/README.md +++ b/README.md @@ -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") +``` \ No newline at end of file diff --git a/src/Access.jl b/src/Access.jl index d53b57c..c46d385 100644 --- a/src/Access.jl +++ b/src/Access.jl @@ -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 diff --git a/test/runtests.jl b/test/runtests.jl index c310b94..ad2e94e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 \ No newline at end of file +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)