Source file musicParticipant.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
(*****************************************************************************)
(*                                                                           *)
(*  Copyright (C) 2026 Yves Ndiaye                                           *)
(*                                                                           *)
(* This Source Code Form is subject to the terms of the Mozilla Public       *)
(* License, v. 2.0. If a copy of the MPL was not distributed with this       *)
(* file, You can obtain one at https://mozilla.org/MPL/2.0/.                 *)
(*                                                                           *)
(*****************************************************************************)

type t = { music_id : Int64.t; artist_id : Int64.t; role : Role.t }

module Column = struct
  let music_id = "music_id"
  let artist_id = "artist_id"
  let role = "role"
end

let table_name = "music_participants"
let foreign_music_id = Column.music_id
let foreign_artist_id = Column.artist_id
let key_role = Column.role
let p_music_id = Printf.sprintf "%s.%s" table_name foreign_music_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 init ~music_id ~artist_id role = { music_id; artist_id; 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_music_id foreign_artist_id key_role foreign_music_id
    foreign_artist_id key_role foreign_music_id MusicFile.table_name
    MusicFile.primary_key foreign_artist_id Artist.table_name Artist.primary_key

let columns statement offset =
  let music_id = Sqlite3.column_int64 statement offset in
  let artist_id = Sqlite3.column_int64 statement (offset + 1) in
  let role = Sqlite3.column_int statement (offset + 2) in
  let role = Role.of_int role in
  ({ music_id; artist_id; role }, offset + 3)

let filter_map database f =
  let statement =
    Printf.sprintf "SELECT %s, %s, %s FROM %s;" Column.music_id Column.artist_id
      Column.role table_name
  in
  let statement = Sqlite3.prepare database statement in
  let queue = Queue.create () in
  let rc =
    Sqlite3.iter statement ~f:(fun _ ->
        let participant, _ = columns statement 0 in
        match f participant with None -> () | Some e -> Queue.add e queue
    )
  in
  let values = queue |> Queue.to_seq |> List.of_seq in
  Util.bind_ok database rc @@ fun () -> Ok values

let update_artist database old new' =
  let ( let* ) = Util.bind_constraint_ok database 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
  let* () = Sqlite3.step statement in
  let* () = Sqlite3.finalize statement in
  Ok ()

let remove database t =
  let ( let* ) = Util.bind_ok database in
  let { music_id; artist_id; role } = t in
  let statement =
    Printf.sprintf "DELETE FROM %s WHERE (%s = ?) AND (%s = ?) AND (%s = ?);"
      table_name Column.music_id Column.artist_id Column.role
  in
  let statement = Sqlite3.prepare database statement in
  let* () = Sqlite3.bind_int64 statement 1 music_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 ->
      let* () = Sqlite3.finalize statement in
      Ok ()
  | rc ->
      let message = Sqlite3.errmsg database in
      Error (rc, message)

let remove_all_id database music_id =
  let ( let* ) = Util.bind_ok database in
  let statement =
    Printf.sprintf "DELETE FROM %s WHERE %s = ?;" table_name Column.music_id
  in
  let statement = Sqlite3.prepare database statement in
  let* () = Sqlite3.bind_int64 statement 1 music_id in
  match Sqlite3.step statement with
  | Sqlite3.Rc.OK | Sqlite3.Rc.DONE ->
      let* () = Sqlite3.finalize statement in
      Ok ()
  | rc ->
      let message = Sqlite3.errmsg database in
      Error (rc, message)

let insert database t =
  let ( let* ) = Util.bind_constraint_ok database in
  let { music_id; artist_id; role } = t in
  let statement' =
    Printf.sprintf "INSERT INTO %s (%s, %s, %s) VALUES (?, ?, ?);" table_name
      foreign_music_id foreign_artist_id key_role
  in
  let statement = Sqlite3.prepare database statement' in
  let* () = Sqlite3.bind_int64 statement 1 music_id in
  let* () = Sqlite3.bind_int64 statement 2 artist_id in
  let* () = Sqlite3.bind_int statement 3 (Role.to_int role) in
  let* () = Sqlite3.step statement in
  let* () = Sqlite3.finalize statement in
  Ok ()

let music_id s = s.music_id
let artist_id s = s.artist_id
let role s = s.role
let set_artist_id artist_id t = { t with artist_id }
let set_role role t = { t with role }

(* Test *)
let equal : t -> t -> bool = ( = )
let compare : t -> t -> int = Stdlib.compare