diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55fc593..f842303 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,17 +21,7 @@ jobs: - x64 steps: - uses: actions/checkout@v2 - - uses: getong/mariadb-action@v1.1 - with: - host port: 3306 # Optional, default value is 3306. The port of host - container port: 3306 # Optional, default value is 3306. The port of container - character set server: 'utf8mb4' # Optional, default value is 'utf8mb4'. The '--character-set-server' option for mysqld - collation server: 'utf8mb4_general_ci' # Optional, default value is 'utf8mb4_general_ci'. The '--collation-server' option for mysqld - mariadb version: 'latest' # Optional, default value is "latest". The version of the MariaDB - mysql database: 'mysqltest' # Optional, default value is "test". The specified database which will be create - mysql root password: '' # Required if "mysql user" is empty, default is empty. The root superuser password - # mysql user: 'developer' # Required if "mysql root password" is empty, default is empty. The superuser for the specified database. Can use secrets, too - # mysql password: ${{ secrets.DatabasePassword }} # Required if "mysql user" exists. The password for the "mysql user" + - run: docker compose up -d - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} diff --git a/Project.toml b/Project.toml index 1b88cde..c7a69e9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MySQL" uuid = "39abe10b-433b-5dbd-92d4-e302a9df00cd" author = ["quinnj"] -version = "1.4.4" +version = "1.4.5" [deps] DBInterface = "a10d1c49-ce27-4219-8d33-6db1a4562965" diff --git a/README.md b/README.md index e04febe..1d93b7e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,17 @@ Package for interfacing with MySQL databases from Julia via the MariaDB C connector library, version 3.1.6. -### Documentation +## Documentation [![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://mysql.juliadatabases.org/stable) [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://mysql.juliadatabases.org/dev) + +## Contributing + +The tests require a MySQL DB to be running, which is provided by Docker: + +```sh +docker compose up -d +julia --project -e 'using Pkg; Pkg.test()' +docker compose down +``` diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..eea79f9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.9" +name: "mysqljl-test" +services: + db: + image: mysql:8 + ports: + - 3306:3306 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: true + healthcheck: + test: + [ + "CMD", + "mysql", + "-u", + "root", + "-p''", + "--silent", + "--execute", + "SELECT 1;", + ] + interval: 30s + timeout: 10s + retries: 5 + networks: + - app +networks: + app: + driver: bridge diff --git a/src/MySQL.jl b/src/MySQL.jl index 92d62b8..8c2ccf6 100644 --- a/src/MySQL.jl +++ b/src/MySQL.jl @@ -1,6 +1,7 @@ module MySQL using Dates, DBInterface, Tables, Parsers, DecFP +import DBInterface: transaction export DBInterface, DateAndTime diff --git a/src/load.jl b/src/load.jl index 1aaa58e..c2cb869 100644 --- a/src/load.jl +++ b/src/load.jl @@ -91,7 +91,7 @@ function load(itr, conn::Connection, name::AbstractString="mysql_"*Random.randst DBInterface.execute(conn, "DELETE FROM $name") end # start a transaction for inserting rows - transaction(conn) do + DBInterface.transaction(conn) do params = chop(repeat("?,", length(sch.names))) stmt = DBInterface.prepare(conn, "INSERT INTO $name VALUES ($params)") for (i, row) in enumerate(rows) @@ -104,8 +104,8 @@ function load(itr, conn::Connection, name::AbstractString="mysql_"*Random.randst return name end -function transaction(f::Function, conn) - execute(conn, "START TRANSACTION") +function DBInterface.transaction(f::Function, conn::Connection) + DBInterface.execute(conn, "START TRANSACTION") try f() API.commit(conn.mysql) @@ -113,4 +113,4 @@ function transaction(f::Function, conn) API.rollback(conn.mysql) rethrow() end -end \ No newline at end of file +end diff --git a/test/runtests.jl b/test/runtests.jl index 9339aa6..bfd18b2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -352,37 +352,46 @@ ret = columntable(res) @test_throws ArgumentError MySQL.load(ct, conn, "test194") @testset "transactions" begin - DBInterface.execute(conn, "DROP TABLE IF EXISTS TransactionTest") - DBInterface.execute(conn, "CREATE TABLE TransactionTest (a int)") - - conn2 = DBInterface.connect(MySQL.Connection, "", ""; option_file=joinpath(dirname(pathof(MySQL)), "../test/", "my.ini")) - + conn = DBInterface.connect(MySQL.Connection, "127.0.0.1", "root", ""; port=3306) try - # happy path - DBInterface.transaction(conn) do - DBInterface.execute(conn, "INSERT INTO TransactionTest (a) VALUES (1)") - - # we can see the result inside our transaction + DBInterface.execute(conn, "DROP DATABASE if exists mysqltest") + DBInterface.execute(conn, "CREATE DATABASE mysqltest") + DBInterface.execute(conn, "use mysqltest") + DBInterface.execute(conn, "DROP TABLE IF EXISTS TransactionTest") + DBInterface.execute(conn, "CREATE TABLE TransactionTest (a int)") + + conn2 = DBInterface.connect(MySQL.Connection, "127.0.0.1", "root", ""; port=3306) + DBInterface.execute(conn2, "use mysqltest") + + try + # happy path + DBInterface.transaction(conn) do + DBInterface.execute(conn, "INSERT INTO TransactionTest (a) VALUES (1)") + + # we can see the result inside our transaction + result = DBInterface.execute(conn, "SELECT * FROM TransactionTest") |> Tables.columntable + @test result.a == [1] + + # and can't see it outside our transaction + result = DBInterface.execute(conn2, "SELECT * FROM TransactionTest") |> Tables.columntable + @test isempty(result.a) + end result = DBInterface.execute(conn, "SELECT * FROM TransactionTest") |> Tables.columntable @test result.a == [1] - - # and can't see it outside our transaction result = DBInterface.execute(conn2, "SELECT * FROM TransactionTest") |> Tables.columntable - @test isempty(result.a) - end - result = DBInterface.execute(conn, "SELECT * FROM TransactionTest") |> Tables.columntable - @test result.a == [1] - result = DBInterface.execute(conn2, "SELECT * FROM TransactionTest") |> Tables.columntable - @test result.a == [1] - - # roll back due to exception - @test_throws ErrorException DBInterface.transaction(conn) do - DBInterface.execute(conn, "INSERT INTO TransactionTest (a) VALUES (2)") - error("force rollback") + @test result.a == [1] + + # roll back due to exception + @test_throws ErrorException DBInterface.transaction(conn) do + DBInterface.execute(conn, "INSERT INTO TransactionTest (a) VALUES (2)") + error("force rollback") + end + result = DBInterface.execute(conn, "SELECT * FROM TransactionTest") |> Tables.columntable + @test result.a == [1] # the table did not change + finally + DBInterface.close!(conn2) end - result = DBInterface.execute(conn, "SELECT * FROM TransactionTest") |> Tables.columntable - @test result.a == [1] # the table did not change finally - close(conn2) + DBInterface.close!(conn) end end