Skip to content

Commit

Permalink
Implement DataAPI metadata interface
Browse files Browse the repository at this point in the history
  • Loading branch information
xitology committed Jun 15, 2024
1 parent 449ba6c commit 4011c7a
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 28 deletions.
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ version = "0.13.2"

[deps]
DBInterface = "a10d1c49-ce27-4219-8d33-6db1a4562965"
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
LRUCache = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
Expand All @@ -13,6 +14,7 @@ Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"

[compat]
DBInterface = "2.5"
DataAPI = "1.13"
LRUCache = "1.3"
OrderedCollections = "1.4"
PrettyPrinting = "0.3.2, 0.4"
Expand Down
37 changes: 37 additions & 0 deletions docs/src/test/other.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ A `SQLTable` object behaves like a read-only dictionary.
person["person_id"]
#-> SQLColumn(:person_id)

person[1]
#-> SQLColumn(:person_id)

person[:visit_occurrence]
#-> ERROR: KeyError: key :visit_occurrence not found

Expand Down Expand Up @@ -263,6 +266,40 @@ Catalog objects can be assigned arbitrary metadata.
metadata = [:model => "OMOP"])
=#

FunSQL metadata supports DataAPI metadata interface.

using DataAPI

DataAPI.metadata(metadata_catalog)
#-> Dict("model" => "OMOP")

DataAPI.metadata(metadata_catalog, style = true)
#-> Dict("model" => ("OMOP", :default))

DataAPI.metadata(metadata_catalog, :name, :default)
#-> :default

DataAPI.metadata(metadata_catalog[:person])["caption"]
#-> "Person"

DataAPI.metadata(metadata_catalog[:person], :is_view, true)
#-> false

DataAPI.colmetadata(metadata_catalog[:person])[:person_id]["label"]
#-> "Person ID"

DataAPI.colmetadata(metadata_catalog[:person], 1, :label)
#-> "Person ID"

DataAPI.colmetadata(metadata_catalog[:person], :year_of_birth, :label, "")
#-> ""

DataAPI.metadata(metadata_catalog[:person][:person_id])
#-> Dict("label" => "Person ID")

DataAPI.metadata(metadata_catalog[:person][:person_id], :label, "")
#-> "Person ID"


## `SQLDialect`

Expand Down
1 change: 1 addition & 0 deletions src/FunSQL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ using OrderedCollections: OrderedDict, OrderedSet
using Tables
using DBInterface
using LRUCache
using DataAPI

const SQLLiteralType =
Union{Missing, Bool, Number, AbstractString, Dates.AbstractTime}
Expand Down
125 changes: 97 additions & 28 deletions src/catalogs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ _metadata(dict::SQLMetadata, kvs...) =
_metadata(other) =
_metadata(SQLMetadata(), pairs(other)...)

_metadata_style(@nospecialize(val)) =
:default

_metadata_keys(dict::SQLMetadata) =
Base.Generator(string, keys(dict))

_metadata_get(dict::SQLMetadata, key::Union{Symbol, AbstractString}; style::Bool) =
let val = dict[Symbol(key)]
style ? (val, _metadata_style(val)) : val
end

_metadata_get(dict::SQLMetadata, key::Union{Symbol, AbstractString}, default; style::Bool) =
let val = get(dict, Symbol(key), default)
style ? (val, _metadata_style(val)) : val
end

"""
SQLColumn(; name, metadata = nothing)
SQLColumn(name; metadata = nothing)
Expand Down Expand Up @@ -46,26 +62,17 @@ function PrettyPrinting.quoteof(col::SQLColumn; limit::Bool = false)
ex
end

_column_map(columns::OrderedDict{Symbol, SQLColumn}) =
columns

_column_map(columns::AbstractVector{Pair{Symbol, SQLColumn}}) =
OrderedDict{Symbol, SQLColumn}(columns)

_column_map(columns) =
OrderedDict{Symbol, SQLColumn}(Pair{Symbol, SQLColumn}[_column_entry(c) for c in columns])

_column_entry(c::Symbol) =
c => SQLColumn(c)
DataAPI.metadatasupport(::Type{SQLColumn}) =
(read = true, write = false)

_column_entry(c::AbstractString) =
_column_entry(Symbol(c))
DataAPI.metadata(col::SQLColumn, key::Union{Symbol, AbstractString}; style::Bool = false) =
_metadata_get(col.metadata, key; style)

_column_entry(c::SQLColumn) =
c.name => c
DataAPI.metadata(col::SQLColumn, key::Union{Symbol, AbstractString}, default; style::Bool = false) =
_metadata_get(col.metadata, key, default; style)

_column_entry((n, c)::Pair{<:Union{Symbol, AbstractString}, SQLColumn}) =
Symbol(n) => c
DataAPI.metadatakeys(col::SQLColumn) =
_metadata_keys(col.metadata)

"""
SQLTable(; qualifiers = [], name, columns, metadata = nothing)
Expand Down Expand Up @@ -120,6 +127,27 @@ SQLTable(name; qualifiers = Symbol[], columns, metadata = nothing) =
SQLTable(name, columns...; qualifiers = Symbol[], metadata = nothing) =
SQLTable(qualifiers = qualifiers, name = name, columns = [columns...], metadata = metadata)

_column_map(columns::OrderedDict{Symbol, SQLColumn}) =
columns

_column_map(columns::AbstractVector{Pair{Symbol, SQLColumn}}) =
OrderedDict{Symbol, SQLColumn}(columns)

_column_map(columns) =
OrderedDict{Symbol, SQLColumn}(Pair{Symbol, SQLColumn}[_column_entry(c) for c in columns])

_column_entry(c::Symbol) =
c => SQLColumn(c)

_column_entry(c::AbstractString) =
_column_entry(Symbol(c))

_column_entry(c::SQLColumn) =
c.name => c

_column_entry((n, c)::Pair{<:Union{Symbol, AbstractString}, SQLColumn}) =
Symbol(n) => c

Base.show(io::IO, tbl::SQLTable) =
print(io, quoteof(tbl, limit = true))

Expand Down Expand Up @@ -158,28 +186,43 @@ Base.get(default::Base.Callable, tbl::SQLTable, key::Union{Symbol, AbstractStrin
Base.getindex(tbl::SQLTable, key::Union{Symbol, AbstractString}) =
tbl.columns[Symbol(key)]

Base.getindex(tbl::SQLTable, key::Integer) =
tbl.columns.vals[key]

Base.iterate(tbl::SQLTable, state...) =
iterate(tbl.columns, state...)

Base.length(tbl::SQLTable) =
length(tbl.columns)

const default_cache_maxsize = 256
DataAPI.metadatasupport(::Type{SQLTable}) =
(read = true, write = false)

_table_map(tables::Dict{Symbol, SQLTable}) =
tables
DataAPI.metadata(tbl::SQLTable, key::Union{Symbol, AbstractString}; style::Bool = false) =
_metadata_get(tbl.metadata, key; style)

_table_map(tables::AbstractVector{Pair{Symbol, SQLTable}}) =
Dict{Symbol, SQLTable}(tables)
DataAPI.metadata(tbl::SQLTable, key::Union{Symbol, AbstractString}, default; style::Bool = false) =
_metadata_get(tbl.metadata, key, default; style)

_table_map(tables) =
Dict{Symbol, SQLTable}(Pair{Symbol, SQLTable}[_table_entry(t) for t in tables])
DataAPI.metadatakeys(tbl::SQLTable) =
_metadata_keys(tbl.metadata)

_table_entry(t::SQLTable) =
t.name => t
DataAPI.colmetadatasupport(::Type{SQLTable}) =
(read = true, write = false)

_table_entry((n, t)::Pair{<:Union{Symbol, AbstractString}, SQLTable}) =
Symbol(n) => t
DataAPI.colmetadata(tbl::SQLTable, col::Union{Symbol, Integer}, key::Union{Symbol, AbstractString}; style::Bool = false) =
_metadata_get(tbl[col].metadata, key; style)

DataAPI.colmetadata(tbl::SQLTable, col::Union{Symbol, Integer}, key::Union{Symbol, AbstractString}, default; style::Bool = false) =
_metadata_get(tbl[col].metadata, key, default; style)

DataAPI.colmetadatakeys(tbl::SQLTable) =
(k => _metadata_keys(v.metadata) for (k, v) in tbl.columns)

DataAPI.colmetadatakeys(tbl::SQLTable, col::Union{Symbol, Integer}) =
_metadata_keys(tbl[col].metadata)

const default_cache_maxsize = 256

"""
SQLCatalog(; tables = Dict{Symbol, SQLTable}(),
Expand Down Expand Up @@ -239,6 +282,21 @@ end
SQLCatalog(tables...; dialect = :default, cache = default_cache_maxsize, metadata = nothing) =
SQLCatalog(tables = tables, dialect = dialect, cache = cache, metadata = metadata)

_table_map(tables::Dict{Symbol, SQLTable}) =
tables

_table_map(tables::AbstractVector{Pair{Symbol, SQLTable}}) =
Dict{Symbol, SQLTable}(tables)

_table_map(tables) =
Dict{Symbol, SQLTable}(Pair{Symbol, SQLTable}[_table_entry(t) for t in tables])

_table_entry(t::SQLTable) =
t.name => t

_table_entry((n, t)::Pair{<:Union{Symbol, AbstractString}, SQLTable}) =
Symbol(n) => t

function PrettyPrinting.quoteof(c::SQLCatalog)
ex = Expr(:call, nameof(SQLCatalog))
for name in sort!(collect(keys(c.tables)))
Expand Down Expand Up @@ -310,3 +368,14 @@ Base.iterate(c::SQLCatalog, state...) =
Base.length(c::SQLCatalog) =
length(c.tables)

DataAPI.metadatasupport(::Type{SQLCatalog}) =
(read = true, write = false)

DataAPI.metadata(c::SQLCatalog, key::Union{Symbol, AbstractString}; style::Bool = false) =
_metadata_get(c.metadata, key; style)

DataAPI.metadata(c::SQLCatalog, key::Union{Symbol, AbstractString}, default; style::Bool = false) =
_metadata_get(c.metadata, key, default; style)

DataAPI.metadatakeys(c::SQLCatalog) =
_metadata_keys(c.metadata)
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[deps]
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Expand Down

0 comments on commit 4011c7a

Please sign in to comment.