Source file albumParticipant.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
type t = { album_id : Int64.t; artist_id : Int64.t; role : Role.t }
module Column = struct
let album_id = "album_id"
let artist_id = "artist_id"
let role = "role"
end
let table_name = "album_participants"
let foreign_album_id = Column.album_id
let foreign_artist_id = Column.artist_id
let key_role = Column.role
let p_album_id = Printf.sprintf "%s.%s" table_name foreign_album_id
let p_artist_id = Printf.sprintf "%s.%s" table_name foreign_artist_id
let p_role = Printf.sprintf "%s.%s" table_name key_role
let sql_table =
Printf.sprintf
{|CREATE TABLE IF NOT EXISTS %s (
%s INTEGER NOT NULL,
%s INTEGER NOT NULL,
%s INTEGER,
UNIQUE (%s, %s, %s),
FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE CASCADE ON DELETE CASCADE
FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE CASCADE ON DELETE CASCADE
);|}
table_name foreign_album_id foreign_artist_id key_role foreign_album_id
foreign_artist_id key_role foreign_album_id Album.table_name
Album.primary_key foreign_artist_id Artist.table_name Artist.primary_key
let init ~album_id ~artist_id role = { album_id; artist_id; role }
let update_artist database old new' =
let ( let* ) rc f =
match rc with
| Sqlite3.Rc.OK | Sqlite3.Rc.DONE ->
f ()
| rc ->
let message = Sqlite3.errmsg database in
Error (rc, message)
in
let statement =
Printf.sprintf "UPDATE %s SET %s = ?1 WHERE %s = ?2;" table_name
foreign_artist_id foreign_artist_id
in
let statement = Sqlite3.prepare database statement in
let* () = Sqlite3.bind_int64 statement 1 new' in
let* () = Sqlite3.bind_int64 statement 2 old in
match Sqlite3.step statement with
| Sqlite3.Rc.OK | Sqlite3.Rc.DONE | Sqlite3.Rc.CONSTRAINT ->
let* () = Sqlite3.finalize statement in
Ok ()
| rc ->
let message = Sqlite3.errmsg database in
Error (rc, message)
let insert database t =
let { album_id; artist_id; role } = t in
let statement' =
Printf.sprintf
"INSERT INTO %s (album_id, artist_id, role) VALUES (?, ?, ?);" table_name
in
let ( let* ) rc f =
match rc with
| Sqlite3.Rc.OK | Sqlite3.Rc.DONE ->
f ()
| rc ->
let message = Sqlite3.errmsg database in
Error (rc, message)
in
let statement = Sqlite3.prepare database statement' in
let* () = Sqlite3.bind_int64 statement 1 album_id in
let* () = Sqlite3.bind_int64 statement 2 artist_id in
let* () = Sqlite3.bind_int statement 3 (Role.to_int role) in
match Sqlite3.step statement with
| Sqlite3.Rc.OK | Sqlite3.Rc.DONE | Sqlite3.Rc.CONSTRAINT ->
let* () = Sqlite3.finalize statement in
Ok ()
| rc ->
let message = Sqlite3.errmsg database in
Error (rc, message)
(** [find ~compilation database artists album] tries to find an album in
[database] with the name [album.name] and at least an arist with the name is
in [artists] unless [compilation], in this case only the [album.name] is
check to discriminate albums *)
let find ~compilation database (artists : (Artist.t * Role.t) list)
(album : Album.t) =
let module Albums = Set.Make (struct
type t = Album.t
let compare (lhs : Album.t) (rhs : Album.t) = compare lhs.id rhs.id
end) in
let query =
Printf.sprintf
"SELECT %s.*, %s FROM %s INNER JOIN %s ON %s = %s INNER JOIN %s ON %s = \
%s;"
Album.table_name Artist.p_name table_name Album.table_name p_album_id
Album.p_id Artist.table_name p_artist_id Artist.p_id
in
let statement = Sqlite3.prepare database query in
let _, albums =
Sqlite3.fold statement ~init:Albums.empty ~f:(fun albums _ ->
let qalbum, offset = Album.columns statement 0 in
let artist_name = Sqlite3.column_text statement offset in
let artists () =
compilation
|| List.exists
(fun ((artist : Artist.t), _) -> artist.name = artist_name)
artists
in
let matched = qalbum.name = album.name && artists () in
match matched with true -> Albums.add qalbum albums | false -> albums
)
in
Albums.elements albums
let find_from_metadata ~compilation database metadata =
let module Albums = Set.Make (struct
type t = Album.t
let compare (lhs : Album.t) (rhs : Album.t) = compare lhs.id rhs.id
end) in
let album_name = SkhcMetadata.Metadata.album metadata in
let artists =
SkhcMetadata.Metadata.artists metadata
@ SkhcMetadata.Metadata.album_artists metadata
in
let query =
Printf.sprintf
"SELECT %s.*, %s FROM %s INNER JOIN %s ON %s = %s INNER JOIN %s ON %s = \
%s;"
Album.table_name Artist.p_name table_name Album.table_name p_album_id
Album.p_id Artist.table_name p_artist_id Artist.p_id
in
let statement = Sqlite3.prepare database query in
let _, albums =
Sqlite3.fold statement ~init:Albums.empty ~f:(fun albums _ ->
let qalbum, offset = Album.columns statement 0 in
let artist_name = Sqlite3.column_text statement offset in
let artists () =
compilation || List.exists (( = ) artist_name) artists
in
let matched = Some qalbum.name = album_name && artists () in
match matched with true -> Albums.add qalbum albums | false -> albums
)
in
Albums.elements albums
(** [find_or_insert ~compilation database artists album] tries to find an album
in [database] with the name [album.name] and at least an arist with the name
is in [artists] unless [compilation], in this case only the [album.name] is
check to discriminate albums. If no album is found, [find_or_insert] will
try to insert [album] to [database].*)
let find_or_insert ~compilation database (artists : (Artist.t * Role.t) list)
(album : Album.t) =
let ( let* ) = Result.bind in
let albums = find ~compilation database artists album in
match albums with
| [] ->
let* qalbum = Album.insert database album in
let () =
List.iter
(fun ((artist : Artist.t), role) ->
let t = { album_id = qalbum.id; artist_id = artist.id; role } in
match insert database t with Ok () -> () | Error _e -> ()
)
artists
in
Ok qalbum
| qalbum :: _ ->
Ok qalbum
let equal : t -> t -> bool = ( = )
let compare : t -> t -> int = Stdlib.compare