(* * Copyright (c) 2017 - present Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. *) open! IStd module L = Logging exception Error of string let error ~fatal fmt = (if fatal then Format.kasprintf (fun err -> raise (Error err)) else L.internal_error) fmt let check_sqlite_error ?(fatal= false) ~log rc = match (rc : Sqlite3.Rc.t) with | OK | ROW -> () | _ as err -> error ~fatal "%s: %s" log (Sqlite3.Rc.to_string err) let exec db ~log ~stmt = (* Call [check_sqlite_error] with [fatal:true] and catch exceptions to rewrite the error message. This avoids allocating the error string when not needed. *) try check_sqlite_error ~fatal:true ~log (Sqlite3.exec db stmt) with Error err -> error ~fatal:true "exec: %s" err let finalize ~log stmt = try check_sqlite_error ~fatal:true ~log (Sqlite3.finalize stmt) with | Error err -> error ~fatal:true "finalize: %s" err | Sqlite3.Error err -> error ~fatal:true "finalize: %s: %s" log err let sqlite_result_rev_list_step ?finalize:(do_finalize = true) ~log stmt = let rec aux rev_results = match Sqlite3.step stmt with | Sqlite3.Rc.ROW -> (* the operation returned a result, get it *) let value = Some (Sqlite3.column stmt 0) in aux (value :: rev_results) | DONE -> rev_results | err -> L.die InternalError "%s: %s" log (Sqlite3.Rc.to_string err) in if do_finalize then protect ~finally:(fun () -> finalize ~log stmt) ~f:(fun () -> aux []) else aux [] let sqlite_result_step ?finalize ~log stmt = match sqlite_result_rev_list_step ?finalize ~log stmt with | [] -> None | [x] -> x | l -> L.die InternalError "%s: zero or one result expected, got %d instead" log (List.length l) let sqlite_unit_step ?finalize ~log stmt = match sqlite_result_rev_list_step ?finalize ~log stmt with | [] -> () | l -> L.die InternalError "%s: exactly zero result expected, got %d instead" log (List.length l) let db_close db = if not (Sqlite3.db_close db) then raise (Error (Printf.sprintf "closing: %s (%s)" (Sqlite3.errcode db |> Sqlite3.Rc.to_string) (Sqlite3.errmsg db)))