aboutsummaryrefslogtreecommitdiff
path: root/lib/sql/hashs.ml
blob: af1f0927a539dc030ddd4bd0ec5b42d66cd65cb5 (plain)
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
(** 
    This module store the hash of the indexes ([extern_key]) for each table in
    order to update the file if the configuration changed.

    The hashes are stored in a table named [hashes] and are evaluated just
    before inserting the values. 
*)

open StdLabels
module Table = ImportDataTypes.Table

let ( let* ) = Result.bind

let create_table : 'a T.t -> unit T.result =
 fun db ->
  Sqlite3.exec db
    "CREATE TABLE IF NOT EXISTS 'hashes' ('table' TEXT, 'hash' INTEGER, \
     PRIMARY KEY ('table'))"
  |> T.to_result

let evaluate : ImportAnalyser.Dependency.t -> int =
 fun table ->
  (* Extract all the references to this table *)
  let keys =
    List.map (ImportAnalyser.Dependency.keys table)
      ~f:(fun ImportAnalyser.Dependency.{ name; columns; expression } ->
        ignore columns;
        (name, expression))
  in
  Hashtbl.hash keys

let insert : 'a T.t -> ImportAnalyser.Dependency.t -> unit T.result =
 fun db table ->
  let source = ImportAnalyser.Dependency.table table in
  let table_name = Table.name source in

  let hash = evaluate table in

  let query =
    String.concat ~sep:""
      [
        "INSERT INTO 'hashes' ('table', 'hash') VALUES ('";
        table_name;
        "', :hash) ON CONFLICT(hashes.'table') DO UPDATE SET 'hash' = :hash";
      ]
  in
  let* statement =
    try Ok (Sqlite3.prepare db query) with
    | e -> Error e
  in

  let* _ = T.begin_transaction db in
  let sql_data = Sqlite3.Data.INT (Int64.of_int hash) in

  let* _ = Sqlite3.bind_name statement ":hash" sql_data |> T.to_result in
  let* _ = T.to_result (Sqlite3.step statement) in
  T.commit db

let query : 'a T.t -> ImportDataTypes.Table.t -> int option T.result =
 fun db table ->
  let table_name = Table.name table in
  let query =
    String.concat ~sep:""
      [ "SELECT hash FROM 'hashes' WHERE hashes.'table' = '"; table_name; "'" ]
  in

  let* stmt =
    try Ok (Sqlite3.prepare db query) with
    | e -> Error e
  in
  let state, res =
    Sqlite3.fold stmt ~init:None ~f:(fun _ d ->
        Some (T.to_datatype (Array.get d 0)))
  in

  let* _ = T.to_result state in
  match res with
  | Some (ImportCSV.DataType.Integer i) -> Ok (Some i)
  | _ -> Ok None