(** 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