Skip to content

Commit 449ba6c

Browse files
committedJun 15, 2024
Use Base.ImmutableDict to store catalog metadata
·
v0.15.0v0.14.0
1 parent ea2a49a commit 449ba6c

File tree

2 files changed

+55
-41
lines changed

2 files changed

+55
-41
lines changed
 

‎docs/src/test/other.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -247,19 +247,20 @@ Catalog objects can be assigned arbitrary metadata.
247247

248248
metadata_catalog =
249249
SQLCatalog(SQLTable(:person,
250-
SQLColumn(:person_id, metadata = Dict(:label => "Person ID")),
251-
metadata = Dict(:caption => "Person")),
252-
metadata = Dict(:schema => "OMOP"))
253-
#-> SQLCatalog(…1 table…, dialect = SQLDialect(), metadata = Dict(…))
250+
SQLColumn(:person_id, metadata = (; label = "Person ID")),
251+
SQLColumn(:year_of_birth, metadata = (;)),
252+
metadata = (; caption = "Person", is_view = false)),
253+
metadata = (; model = "OMOP"))
254+
#-> SQLCatalog(…1 table…, dialect = SQLDialect(), metadata = …)
254255

255256
display(metadata_catalog)
256257
#=>
257258
SQLCatalog(SQLTable(:person,
258-
SQLColumn(:person_id,
259-
metadata = Dict(:label => "Person ID")),
260-
metadata = Dict(:caption => "Person")),
259+
SQLColumn(:person_id, metadata = [:label => "Person ID"]),
260+
SQLColumn(:year_of_birth),
261+
metadata = [:caption => "Person", :is_view => false]),
261262
dialect = SQLDialect(),
262-
metadata = Dict(:schema => "OMOP"))
263+
metadata = [:model => "OMOP"])
263264
=#
264265

265266

‎src/catalogs.jl

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
# Database structure.
22

3+
const SQLMetadata = Base.ImmutableDict{Symbol, Any}
4+
5+
_metadata(::Nothing) =
6+
SQLMetadata()
7+
8+
_metadata(dict::SQLMetadata) =
9+
dict
10+
11+
_metadata(dict::SQLMetadata, kvs...) =
12+
Base.ImmutableDict(dict, kvs...)
13+
14+
_metadata(other) =
15+
_metadata(SQLMetadata(), pairs(other)...)
16+
317
"""
4-
SQLColumn(; name)
5-
SQLColumn(name)
18+
SQLColumn(; name, metadata = nothing)
19+
SQLColumn(name; metadata = nothing)
620
7-
`SQLColumn` represents a column of a database table.
21+
`SQLColumn` represents a column with the given `name` and optional `metadata`.
822
"""
923
struct SQLColumn
1024
name::Symbol
11-
metadata::Union{Nothing, Dict{Symbol, Any}}
25+
metadata::SQLMetadata
1226

1327
function SQLColumn(; name::Union{Symbol, AbstractString}, metadata = nothing)
14-
new(Symbol(name), metadata)
28+
new(Symbol(name), _metadata(metadata))
1529
end
1630
end
1731

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

2741
function PrettyPrinting.quoteof(col::SQLColumn; limit::Bool = false)
2842
ex = Expr(:call, nameof(SQLColumn), QuoteNode(col.name))
29-
m = col.metadata
30-
if m !== nothing && !isempty(m)
31-
push!(ex.args, Expr(:kw, :metadata, limit ? : : quoteof(m)))
43+
if !isempty(col.metadata)
44+
push!(ex.args, Expr(:kw, :metadata, limit ? : : quoteof(reverse!(collect(col.metadata)))))
3245
end
3346
ex
3447
end
@@ -55,35 +68,36 @@ _column_entry((n, c)::Pair{<:Union{Symbol, AbstractString}, SQLColumn}) =
5568
Symbol(n) => c
5669

5770
"""
58-
SQLTable(; qualifiers = [], name, columns)
59-
SQLTable(name; qualifiers = [], columns)
60-
SQLTable(name, columns...; qualifiers = [])
71+
SQLTable(; qualifiers = [], name, columns, metadata = nothing)
72+
SQLTable(name; qualifiers = [], columns, metadata = nothing)
73+
SQLTable(name, columns...; qualifiers = [], metadata = nothing)
6174
6275
The structure of a SQL table or a table-like entity (`TEMP TABLE`, `VIEW`, etc)
6376
for use as a reference in assembling SQL queries.
6477
65-
The `SQLTable` constructor expects the table `name`, an ordered dictionary
66-
`columns` that maps names to columns, and, optionally, a vector containing
67-
the name of the table schema and other `qualifiers`. A name can be a `Symbol`
68-
or a `String`.
78+
The `SQLTable` constructor expects the table `name`, an optional vector
79+
containing the table schema and other `qualifiers`, an ordered dictionary
80+
`columns` that maps names to columns, and an optional `metadata`.
6981
7082
# Examples
7183
7284
```jldoctest
7385
julia> person = SQLTable(qualifiers = ["public"],
7486
name = "person",
75-
columns = ["person_id", "year_of_birth"])
87+
columns = ["person_id", "year_of_birth"],
88+
metadata = (; is_view = false))
7689
SQLTable(qualifiers = [:public],
7790
:person,
7891
SQLColumn(:person_id),
79-
SQLColumn(:year_of_birth))
92+
SQLColumn(:year_of_birth),
93+
metadata = [:is_view => false])
8094
```
8195
"""
8296
struct SQLTable <: AbstractDict{Symbol, SQLColumn}
8397
qualifiers::Vector{Symbol}
8498
name::Symbol
8599
columns::OrderedDict{Symbol, SQLColumn}
86-
metadata::Union{Nothing, Dict{Symbol, Any}}
100+
metadata::SQLMetadata
87101

88102
function SQLTable(;
89103
qualifiers::AbstractVector{<:Union{Symbol, AbstractString}} = Symbol[],
@@ -96,7 +110,7 @@ struct SQLTable <: AbstractDict{Symbol, SQLColumn}
96110
qualifiers
97111
name = Symbol(name)
98112
columns = _column_map(columns)
99-
new(qualifiers, name, columns, metadata)
113+
new(qualifiers, name, columns, _metadata(metadata))
100114
end
101115
end
102116

@@ -126,9 +140,8 @@ function PrettyPrinting.quoteof(tbl::SQLTable; limit::Bool = false)
126140
end
127141
push!(ex.args, arg)
128142
end
129-
m = tbl.metadata
130-
if m !== nothing && !isempty(m)
131-
push!(ex.args, Expr(:kw, :metadata, quoteof(m)))
143+
if !isempty(tbl.metadata)
144+
push!(ex.args, Expr(:kw, :metadata, quoteof(reverse!(collect(tbl.metadata)))))
132145
end
133146
else
134147
push!(ex.args, :)
@@ -171,11 +184,13 @@ _table_entry((n, t)::Pair{<:Union{Symbol, AbstractString}, SQLTable}) =
171184
"""
172185
SQLCatalog(; tables = Dict{Symbol, SQLTable}(),
173186
dialect = :default,
174-
cache = $default_cache_maxsize)
175-
SQLCatalog(tables...; dialect = :default, cache = $default_cache_maxsize)
187+
cache = $default_cache_maxsize,
188+
metadata = nothing)
189+
SQLCatalog(tables...;
190+
dialect = :default, cache = $default_cache_maxsize, metadata = nothing)
176191
177192
`SQLCatalog` encapsulates available database `tables`, the target SQL `dialect`,
178-
and a `cache` of serialized queries.
193+
a `cache` of serialized queries, and an optional `metadata`.
179194
180195
Parameter `tables` is either a dictionary or a vector of [`SQLTable`](@ref)
181196
objects, where the vector will be converted to a dictionary with
@@ -210,14 +225,14 @@ struct SQLCatalog <: AbstractDict{Symbol, SQLTable}
210225
tables::Dict{Symbol, SQLTable}
211226
dialect::SQLDialect
212227
cache::Any # Union{AbstractDict{SQLNode, SQLString}, Nothing}
213-
metadata::Union{Nothing, Dict{Symbol, Any}}
228+
metadata::SQLMetadata
214229

215230
function SQLCatalog(; tables = Dict{Symbol, SQLTable}(), dialect = :default, cache = default_cache_maxsize, metadata = nothing)
216231
table_map = _table_map(tables)
217232
if cache isa Number
218233
cache = LRU{SQLNode, SQLString}(maxsize = cache)
219234
end
220-
new(table_map, dialect, cache, metadata)
235+
new(table_map, dialect, cache, _metadata(metadata))
221236
end
222237
end
223238

@@ -245,9 +260,8 @@ function PrettyPrinting.quoteof(c::SQLCatalog)
245260
else
246261
push!(ex.args, Expr(:kw, :cache, Expr(:call, typeof(cache))))
247262
end
248-
m = c.metadata
249-
if m !== nothing && !isempty(m)
250-
push!(ex.args, Expr(:kw, :metadata, quoteof(m)))
263+
if !isempty(c.metadata)
264+
push!(ex.args, Expr(:kw, :metadata, quoteof(reverse!(collect(c.metadata)))))
251265
end
252266
ex
253267
end
@@ -271,9 +285,8 @@ function Base.show(io::IO, c::SQLCatalog)
271285
else
272286
print(io, ", cache = ", typeof(cache), "()")
273287
end
274-
m = c.metadata
275-
if m !== nothing && !isempty(m)
276-
print(io, ", metadata = Dict(…)")
288+
if !isempty(c.metadata)
289+
print(io, ", metadata = …")
277290
end
278291
print(io, ')')
279292
nothing

0 commit comments

Comments
 (0)
Please sign in to comment.