Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ on:
types:
- created
workflow_dispatch:
inputs:
lookback:
default: 3
permissions:
contents: write
jobs:
TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
Expand All @@ -12,3 +17,4 @@ jobs:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
ssh: ${{ secrets.DOCUMENTER_KEY }}
54 changes: 36 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: CI

on:
pull_request:
branches:
Expand All @@ -7,15 +8,27 @@ on:
branches:
- master
tags: '*'

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
continue-on-error: ${{ matrix.version == 'nightly' }}
env:
JULIA_NUM_THREADS: 2
strategy:
fail-fast: false
matrix:
version:
- '1.0'
- '1.9'
- '1' # automatically expands to the latest stable 1.x release of Julia
- 'nightly'
os:
Expand All @@ -26,35 +39,40 @@ jobs:
- os: windows-latest
version: '1'
arch: x86
- os: windows-latest
version: '1'
arch: x64
- os: macos-latest
version: '1'
arch: aarch64
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
- uses: codecov/codecov-action@v5
with:
file: lcov.info
files: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
Documenter:
name: Documentation
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
steps:
- uses: actions/checkout@v2
- uses: julia-actions/julia-buildpkg@latest
- uses: julia-actions/julia-docdeploy@latest
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v2
with:
version: '1'
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-docdeploy@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
5 changes: 3 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name = "DBInterface"
uuid = "a10d1c49-ce27-4219-8d33-6db1a4562965"
authors = ["Jacob Quinn <quinn.jacobd@gmail.com>"]
version = "2.6.1"
version = "2.7.0"

[compat]
julia = "1"
julia = "1.9"
Test = "1.9"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
82 changes: 43 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,63 @@
[![version](https://juliahub.com/docs/DBInterface/version.svg)](https://juliahub.com/ui/Packages/DBInterface/bSj9k)
[![pkgeval](https://juliahub.com/docs/DBInterface/pkgeval.svg)](https://juliahub.com/ui/Packages/DBInterface/bSj9k)

### Purpose
DBInterface.jl provides interface definitions to allow common database operations to be implemented consistently
across various database packages.
## Purpose

### For Users
To use DBInterface.jl, select an implementing database package, then utilize the consistent DBInterface.jl interface methods:
```julia
conn = DBInterface.connect(T, args...; kw...) # create a connection to a specific database T; required parameters are database-specific
DBInterface.jl defines a small, common interface for Julia database drivers. Select a driver package, then use the `DBInterface` methods against that driver's connection, statement, and result types.

stmt = DBInterface.prepare(conn, sql) # prepare a sql statement against the connection; returns a statement object
## Basic Use

results = DBInterface.execute(stmt) # execute a prepared statement; returns an iterator of rows (property-accessible & indexable)
Use the do-block forms when a connection, statement, or result should be closed at the end of an operation:

rowid = DBInterface.lastrowid(results) # get the last row id of an INSERT statement, as supported by the database
```julia
using DBInterface

DBInterface.connect(Driver.Connection, args...; kwargs...) do conn
DBInterface.execute(conn, "SELECT id, name FROM users WHERE id = ?", (42,)) do cursor
for row in cursor
@show row.id
@show row[2]
end
end

# example of using a query resultset
for row in results
@show propertynames(row) # see possible column names of row results
row.col1 # access the value of a column named `col1`
row[1] # access the first column in the row results
DBInterface.prepare(conn, "INSERT INTO users (id, name) VALUES (?, ?)") do stmt
DBInterface.execute(_ -> nothing, stmt, (43, "Ada"))
end
end
```

# results also implicitly satisfy the Tables.jl `Tables.rows` interface, so any compatible sink can ingest results
df = DataFrame(results)
CSV.write("results.csv", results)
Rows must support property access by column name and indexing by column position. Result cursors should also satisfy the Tables.jl row-table interface, so Tables.jl-compatible sinks can consume them:

results = DBInterface.execute(conn, sql) # convenience method if statement preparation/re-use isn't needed
```julia
df = DBInterface.execute(DataFrame, conn, "SELECT * FROM users")
DBInterface.execute(cursor -> CSV.write("users.csv", cursor), conn, "SELECT * FROM users")
```

stmt = DBInterface.prepare(conn, "INSERT INTO test_table VALUES(?, ?)") # prepare a statement with positional parameters
Use `executemany` for column-oriented bulk parameters. Each parameter collection must have the same length:

DBInterface.execute(stmt, [1, 3.14]) # execute the prepared INSERT statement, passing 1 and 3.14 as positional parameters
```julia
DBInterface.executemany(
conn,
"INSERT INTO users (id, name) VALUES (?, ?)",
([1, 2, 3], ["Ada", "Grace", "Katherine"]),
)
```

stmt = DBInterface.prepare(conn, "INSERT INTO test_table VALUES(:col1, :col2)") # prepare a statement with named parameters
Named parameters can be passed as a `NamedTuple`, an `AbstractDict`, or keywords when the database and driver support named placeholders:

DBInterface.execute(stmt, (col1=1, col2=3.14)) # execute the prepared INSERT statement, with 1 and 3.14 as named parameters
```julia
DBInterface.execute(conn, "SELECT * FROM users WHERE id = :id", (id=42,))
DBInterface.execute(conn, "SELECT * FROM users WHERE id = :id"; id=42)
```

DBInterface.executemany(stmt, (col1=[1,2,3,4,5], col2=[3.14, 1.23, 2.34 3.45, 4.56])) # execute the prepared statement multiple times for each set of named parameters; each named parameter must be an indexable collection
Placeholder syntax is driver-specific. For example, a driver may require `?`, `:name`, `$1`, or another form. One placeholder normally binds one scalar value. A collection does not normally expand into an SQL `IN` list.

results = DBInterface.executemultiple(conn, sql) # where sql is a query that returns multiple resultsets
## SQL Safety

# first iterate through resultsets
for result in results
# for each resultset, we can iterate through resultset rows
for row in result
@show propertynames(row)
row.col1
row[1]
end
end
DBInterface passes SQL text to the driver unchanged. Use bound parameters for untrusted values. Do not interpolate untrusted data into SQL strings. Bound parameters do not quote table names, column names, SQL keywords, or other SQL fragments. Use the driver's identifier-quoting API when an identifier must be dynamic.

DBInterface.close!(stmt) # close the prepared statement
DBInterface.close!(conn) # close connection
```
The `sql"..."` string macro does not parse, escape, validate, or sanitize SQL.

## Driver Authors

### For Database Package Developers
See the [documentation](https://juliadatabases.org/DBInterface.jl/dev) for expanded details on required interface methods.
See the [documentation](https://juliadatabases.org/DBInterface.jl/dev) for the required methods, result contract, resource ownership rules, and optional extensions.
4 changes: 4 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
[deps]
DBInterface = "a10d1c49-ce27-4219-8d33-6db1a4562965"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"

[compat]
Documenter = "1"
3 changes: 1 addition & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ using Documenter, DBInterface

makedocs(;
modules=[DBInterface],
format=Documenter.HTML(),
format=Documenter.HTML(repolink="https://github.com/JuliaDatabases/DBInterface.jl"),
pages=[
"Home" => "index.md",
],
repo="https://github.com/JuliaDatabases/DBInterface.jl/blob/{commit}{path}#L{line}",
sitename="DBInterface.jl",
authors="Jacob Quinn",
assets=String[],
)

deploydocs(;
Expand Down
31 changes: 31 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,34 @@
*DBInterface.jl* provides interface definitions to allow common database operations to be implemented consistently
across various database packages.

## User Contract

A database driver defines the concrete connection, statement, and result types. Prefer the do-block forms of [`DBInterface.connect`](@ref), [`DBInterface.prepare`](@ref), and [`DBInterface.execute`](@ref) when the resource should be closed immediately after an operation.

SQL placeholder syntax is database- and driver-specific. Positional placeholders may use `?`, `$1`, or another form. Named placeholders may not be supported. One placeholder normally binds one scalar value, so a collection does not normally expand into an SQL `IN` list.

DBInterface does not parse, escape, validate, or sanitize SQL. Bind untrusted values as parameters. Do not interpolate them into SQL text. Parameters cannot safely replace identifiers, keywords, or SQL fragments; use a driver-specific identifier-quoting API for dynamic identifiers.

## Driver Contract

A driver should implement these core methods:

* [`DBInterface.connect`](@ref) for its database or connection selector.
* [`DBInterface.prepare`](@ref) for its [`DBInterface.Connection`](@ref) subtype.
* [`DBInterface.execute`](@ref) for its [`DBInterface.Statement`](@ref) subtype.
* [`DBInterface.getconnection`](@ref) for its statement subtype.
* [`DBInterface.close!`](@ref) for its connections, statements, and result cursors.

The generic connection form of `execute` prepares a statement and returns the driver's cursor. If that cursor depends on the statement, the driver must keep the statement alive for the cursor's lifetime and release it when the cursor is closed or collected.

Each cursor row must support `propertynames`, `getproperty`, `length`, and positional `getindex`. A cursor should implement the Tables.jl row-table interface. Statements that return no rows must still return an empty cursor or iterator. The scoped `execute(f, ...)` forms call `DBInterface.close!` on the cursor.

Drivers can override [`DBInterface.transaction`](@ref), [`DBInterface.executemany`](@ref), [`DBInterface.executemultiple`](@ref), and [`DBInterface.lastrowid`](@ref) when the generic behavior does not match the database.

## Functions
```@docs
DBInterface.connect
DBInterface.@sql_str
DBInterface.getconnection
DBInterface.prepare
DBInterface.@prepare
Expand All @@ -17,6 +42,9 @@ DBInterface.executemany
DBInterface.executemultiple
DBInterface.close!
DBInterface.lastrowid
DBInterface.ParameterError
DBInterface.Error
DBInterface.Warning
```

## Types
Expand All @@ -25,4 +53,7 @@ DBInterface.lastrowid
DBInterface.Connection
DBInterface.Statement
DBInterface.Cursor
DBInterface.PositionalStatementParams
DBInterface.NamedStatementParams
DBInterface.StatementParams
```
Loading
Loading