Skip to content

Commit

Permalink
Use Base.ImmutableDict to store catalog metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
xitology committed Jun 15, 2024
1 parent ea2a49a commit 449ba6c
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 41 deletions.
17 changes: 9 additions & 8 deletions docs/src/test/other.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,19 +247,20 @@ Catalog objects can be assigned arbitrary metadata.

metadata_catalog =
SQLCatalog(SQLTable(:person,
SQLColumn(:person_id, metadata = Dict(:label => "Person ID")),
metadata = Dict(:caption => "Person")),
metadata = Dict(:schema => "OMOP"))
#-> SQLCatalog(…1 table…, dialect = SQLDialect(), metadata = Dict(…))
SQLColumn(:person_id, metadata = (; label = "Person ID")),
SQLColumn(:year_of_birth, metadata = (;)),
metadata = (; caption = "Person", is_view = false)),
metadata = (; model = "OMOP"))
#-> SQLCatalog(…1 table…, dialect = SQLDialect(), metadata = …)

display(metadata_catalog)
#=>
SQLCatalog(SQLTable(:person,
SQLColumn(:person_id,
metadata = Dict(:label => "Person ID")),
metadata = Dict(:caption => "Person")),
SQLColumn(:person_id, metadata = [:label => "Person ID"]),
SQLColumn(:year_of_birth),
metadata = [:caption => "Person", :is_view => false]),
dialect = SQLDialect(),
metadata = Dict(:schema => "OMOP"))
metadata = [:model => "OMOP"])
=#


Expand Down
79 changes: 46 additions & 33 deletions src/catalogs.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
# Database structure.

const SQLMetadata = Base.ImmutableDict{Symbol, Any}

_metadata(::Nothing) =
SQLMetadata()

_metadata(dict::SQLMetadata) =
dict

_metadata(dict::SQLMetadata, kvs...) =
Base.ImmutableDict(dict, kvs...)

_metadata(other) =
_metadata(SQLMetadata(), pairs(other)...)

"""
SQLColumn(; name)
SQLColumn(name)
SQLColumn(; name, metadata = nothing)
SQLColumn(name; metadata = nothing)
`SQLColumn` represents a column of a database table.
`SQLColumn` represents a column with the given `name` and optional `metadata`.
"""
struct SQLColumn
name::Symbol
metadata::Union{Nothing, Dict{Symbol, Any}}
metadata::SQLMetadata

function SQLColumn(; name::Union{Symbol, AbstractString}, metadata = nothing)
new(Symbol(name), metadata)
new(Symbol(name), _metadata(metadata))
end
end

Expand All @@ -26,9 +40,8 @@ Base.show(io::IO, ::MIME"text/plain", col::SQLColumn) =

function PrettyPrinting.quoteof(col::SQLColumn; limit::Bool = false)
ex = Expr(:call, nameof(SQLColumn), QuoteNode(col.name))
m = col.metadata
if m !== nothing && !isempty(m)
push!(ex.args, Expr(:kw, :metadata, limit ? : : quoteof(m)))
if !isempty(col.metadata)
push!(ex.args, Expr(:kw, :metadata, limit ? : : quoteof(reverse!(collect(col.metadata)))))
end
ex
end
Expand All @@ -55,35 +68,36 @@ _column_entry((n, c)::Pair{<:Union{Symbol, AbstractString}, SQLColumn}) =
Symbol(n) => c

"""
SQLTable(; qualifiers = [], name, columns)
SQLTable(name; qualifiers = [], columns)
SQLTable(name, columns...; qualifiers = [])
SQLTable(; qualifiers = [], name, columns, metadata = nothing)
SQLTable(name; qualifiers = [], columns, metadata = nothing)
SQLTable(name, columns...; qualifiers = [], metadata = nothing)
The structure of a SQL table or a table-like entity (`TEMP TABLE`, `VIEW`, etc)
for use as a reference in assembling SQL queries.
The `SQLTable` constructor expects the table `name`, an ordered dictionary
`columns` that maps names to columns, and, optionally, a vector containing
the name of the table schema and other `qualifiers`. A name can be a `Symbol`
or a `String`.
The `SQLTable` constructor expects the table `name`, an optional vector
containing the table schema and other `qualifiers`, an ordered dictionary
`columns` that maps names to columns, and an optional `metadata`.
# Examples
```jldoctest
julia> person = SQLTable(qualifiers = ["public"],
name = "person",
columns = ["person_id", "year_of_birth"])
columns = ["person_id", "year_of_birth"],
metadata = (; is_view = false))
SQLTable(qualifiers = [:public],
:person,
SQLColumn(:person_id),
SQLColumn(:year_of_birth))
SQLColumn(:year_of_birth),
metadata = [:is_view => false])
```
"""
struct SQLTable <: AbstractDict{Symbol, SQLColumn}
qualifiers::Vector{Symbol}
name::Symbol
columns::OrderedDict{Symbol, SQLColumn}
metadata::Union{Nothing, Dict{Symbol, Any}}
metadata::SQLMetadata

function SQLTable(;
qualifiers::AbstractVector{<:Union{Symbol, AbstractString}} = Symbol[],
Expand All @@ -96,7 +110,7 @@ struct SQLTable <: AbstractDict{Symbol, SQLColumn}
qualifiers
name = Symbol(name)
columns = _column_map(columns)
new(qualifiers, name, columns, metadata)
new(qualifiers, name, columns, _metadata(metadata))
end
end

Expand Down Expand Up @@ -126,9 +140,8 @@ function PrettyPrinting.quoteof(tbl::SQLTable; limit::Bool = false)
end
push!(ex.args, arg)
end
m = tbl.metadata
if m !== nothing && !isempty(m)
push!(ex.args, Expr(:kw, :metadata, quoteof(m)))
if !isempty(tbl.metadata)
push!(ex.args, Expr(:kw, :metadata, quoteof(reverse!(collect(tbl.metadata)))))
end
else
push!(ex.args, :)
Expand Down Expand Up @@ -171,11 +184,13 @@ _table_entry((n, t)::Pair{<:Union{Symbol, AbstractString}, SQLTable}) =
"""
SQLCatalog(; tables = Dict{Symbol, SQLTable}(),
dialect = :default,
cache = $default_cache_maxsize)
SQLCatalog(tables...; dialect = :default, cache = $default_cache_maxsize)
cache = $default_cache_maxsize,
metadata = nothing)
SQLCatalog(tables...;
dialect = :default, cache = $default_cache_maxsize, metadata = nothing)
`SQLCatalog` encapsulates available database `tables`, the target SQL `dialect`,
and a `cache` of serialized queries.
a `cache` of serialized queries, and an optional `metadata`.
Parameter `tables` is either a dictionary or a vector of [`SQLTable`](@ref)
objects, where the vector will be converted to a dictionary with
Expand Down Expand Up @@ -210,14 +225,14 @@ struct SQLCatalog <: AbstractDict{Symbol, SQLTable}
tables::Dict{Symbol, SQLTable}
dialect::SQLDialect
cache::Any # Union{AbstractDict{SQLNode, SQLString}, Nothing}
metadata::Union{Nothing, Dict{Symbol, Any}}
metadata::SQLMetadata

function SQLCatalog(; tables = Dict{Symbol, SQLTable}(), dialect = :default, cache = default_cache_maxsize, metadata = nothing)
table_map = _table_map(tables)
if cache isa Number
cache = LRU{SQLNode, SQLString}(maxsize = cache)
end
new(table_map, dialect, cache, metadata)
new(table_map, dialect, cache, _metadata(metadata))
end
end

Expand Down Expand Up @@ -245,9 +260,8 @@ function PrettyPrinting.quoteof(c::SQLCatalog)
else
push!(ex.args, Expr(:kw, :cache, Expr(:call, typeof(cache))))
end
m = c.metadata
if m !== nothing && !isempty(m)
push!(ex.args, Expr(:kw, :metadata, quoteof(m)))
if !isempty(c.metadata)
push!(ex.args, Expr(:kw, :metadata, quoteof(reverse!(collect(c.metadata)))))
end
ex
end
Expand All @@ -271,9 +285,8 @@ function Base.show(io::IO, c::SQLCatalog)
else
print(io, ", cache = ", typeof(cache), "()")
end
m = c.metadata
if m !== nothing && !isempty(m)
print(io, ", metadata = Dict(…)")
if !isempty(c.metadata)
print(io, ", metadata = …")
end
print(io, ')')
nothing
Expand Down

0 comments on commit 449ba6c

Please sign in to comment.