[sqlite] more info on error

Summary: Get the error message from the database when there's an error, together with the error type.

Reviewed By: mbouaziz

Differential Revision: D6621695

fbshipit-source-id: 6bc706d
master
Jules Villard 7 years ago committed by Facebook Github Bot
parent 7a37d5914d
commit d3c6543cc8

@ -56,16 +56,16 @@ FROM (
let replace pname_blob akind loc_file attr_blob = let replace pname_blob akind loc_file attr_blob =
ResultsDatabase.with_registered_statement replace_statement ~f:(fun replace_stmt -> ResultsDatabase.with_registered_statement replace_statement ~f:(fun db replace_stmt ->
Sqlite3.bind replace_stmt 1 (* :pname *) pname_blob Sqlite3.bind replace_stmt 1 (* :pname *) pname_blob
|> SqliteUtils.check_sqlite_error ~log:"replace bind pname" ; |> SqliteUtils.check_sqlite_error db ~log:"replace bind pname" ;
Sqlite3.bind replace_stmt 2 (* :akind *) (Sqlite3.Data.INT (int64_of_attributes_kind akind)) Sqlite3.bind replace_stmt 2 (* :akind *) (Sqlite3.Data.INT (int64_of_attributes_kind akind))
|> SqliteUtils.check_sqlite_error ~log:"replace bind attribute kind" ; |> SqliteUtils.check_sqlite_error db ~log:"replace bind attribute kind" ;
Sqlite3.bind replace_stmt 3 (* :sfile *) loc_file Sqlite3.bind replace_stmt 3 (* :sfile *) loc_file
|> SqliteUtils.check_sqlite_error ~log:"replace bind source file" ; |> SqliteUtils.check_sqlite_error db ~log:"replace bind source file" ;
Sqlite3.bind replace_stmt 4 (* :pattr *) attr_blob Sqlite3.bind replace_stmt 4 (* :pattr *) attr_blob
|> SqliteUtils.check_sqlite_error ~log:"replace bind proc attributes" ; |> SqliteUtils.check_sqlite_error db ~log:"replace bind proc attributes" ;
SqliteUtils.sqlite_unit_step ~finalize:false ~log:"Attributes.replace" replace_stmt ) SqliteUtils.sqlite_unit_step db ~finalize:false ~log:"Attributes.replace" replace_stmt )
let find_more_defined_statement = let find_more_defined_statement =
@ -79,12 +79,12 @@ WHERE proc_name = :pname
let should_try_to_update pname_blob akind = let should_try_to_update pname_blob akind =
ResultsDatabase.with_registered_statement find_more_defined_statement ~f:(fun find_stmt -> ResultsDatabase.with_registered_statement find_more_defined_statement ~f:(fun db find_stmt ->
Sqlite3.bind find_stmt 1 (* :pname *) pname_blob Sqlite3.bind find_stmt 1 (* :pname *) pname_blob
|> SqliteUtils.check_sqlite_error ~log:"replace bind pname" ; |> SqliteUtils.check_sqlite_error db ~log:"replace bind pname" ;
Sqlite3.bind find_stmt 2 (* :akind *) (Sqlite3.Data.INT (int64_of_attributes_kind akind)) Sqlite3.bind find_stmt 2 (* :akind *) (Sqlite3.Data.INT (int64_of_attributes_kind akind))
|> SqliteUtils.check_sqlite_error ~log:"replace bind attribute kind" ; |> SqliteUtils.check_sqlite_error db ~log:"replace bind attribute kind" ;
SqliteUtils.sqlite_result_step ~finalize:false ~log:"Attributes.replace" find_stmt SqliteUtils.sqlite_result_step ~finalize:false ~log:"Attributes.replace" db find_stmt
|> (* there is no entry with a strictly larger "definedness" for that proc name *) |> (* there is no entry with a strictly larger "definedness" for that proc name *)
Option.is_none ) Option.is_none )
@ -101,10 +101,10 @@ let select_defined_statement =
let find ~defined pname_blob = let find ~defined pname_blob =
(if defined then select_defined_statement else select_statement) (if defined then select_defined_statement else select_statement)
|> ResultsDatabase.with_registered_statement ~f:(fun select_stmt -> |> ResultsDatabase.with_registered_statement ~f:(fun db select_stmt ->
Sqlite3.bind select_stmt 1 pname_blob Sqlite3.bind select_stmt 1 pname_blob
|> SqliteUtils.check_sqlite_error ~log:"find bind proc name" ; |> SqliteUtils.check_sqlite_error db ~log:"find bind proc name" ;
SqliteUtils.sqlite_result_step ~finalize:false ~log:"Attributes.find" select_stmt SqliteUtils.sqlite_result_step ~finalize:false ~log:"Attributes.find" db select_stmt
|> Option.map ~f:ProcAttributes.SQLite.deserialize ) |> Option.map ~f:ProcAttributes.SQLite.deserialize )

@ -100,10 +100,10 @@ module SQLite = SqliteUtils.MarshalledData (struct
end) end)
let load source = let load source =
ResultsDatabase.with_registered_statement load_statement ~f:(fun load_stmt -> ResultsDatabase.with_registered_statement load_statement ~f:(fun db load_stmt ->
SourceFile.SQLite.serialize source |> Sqlite3.bind load_stmt 1 SourceFile.SQLite.serialize source |> Sqlite3.bind load_stmt 1
|> SqliteUtils.check_sqlite_error ~log:"load bind source file" ; |> SqliteUtils.check_sqlite_error db ~log:"load bind source file" ;
SqliteUtils.sqlite_result_step ~finalize:false ~log:"Cfg.load" load_stmt SqliteUtils.sqlite_result_step ~finalize:false ~log:"Cfg.load" db load_stmt
|> Option.map ~f:SQLite.deserialize ) |> Option.map ~f:SQLite.deserialize )
@ -288,14 +288,14 @@ let store source_file cfg =
OndemandCapture module relies on it - it uses existance of the cfg as a barrier to make OndemandCapture module relies on it - it uses existance of the cfg as a barrier to make
sure that all attributes were written to disk (but not necessarily flushed) *) sure that all attributes were written to disk (but not necessarily flushed) *)
save_attributes source_file cfg ; save_attributes source_file cfg ;
ResultsDatabase.with_registered_statement store_statement ~f:(fun store_stmt -> ResultsDatabase.with_registered_statement store_statement ~f:(fun db store_stmt ->
SourceFile.SQLite.serialize source_file |> Sqlite3.bind store_stmt 1 SourceFile.SQLite.serialize source_file |> Sqlite3.bind store_stmt 1
(* :source *) (* :source *)
|> SqliteUtils.check_sqlite_error ~log:"store bind source file" ; |> SqliteUtils.check_sqlite_error db ~log:"store bind source file" ;
SQLite.serialize cfg |> Sqlite3.bind store_stmt 2 SQLite.serialize cfg |> Sqlite3.bind store_stmt 2
(* :cfg *) (* :cfg *)
|> SqliteUtils.check_sqlite_error ~log:"store bind cfg" ; |> SqliteUtils.check_sqlite_error db ~log:"store bind cfg" ;
SqliteUtils.sqlite_unit_step ~finalize:false ~log:"Cfg.store" store_stmt ) SqliteUtils.sqlite_unit_step ~finalize:false ~log:"Cfg.store" db store_stmt )
(** Applies convert_instr_list to all the instructions in all the nodes of the cfg *) (** Applies convert_instr_list to all the instructions in all the nodes of the cfg *)

@ -10,11 +10,12 @@ open! IStd
module L = Logging module L = Logging
let merge_attributes_table ~db_file = let merge_attributes_table ~db_file =
let db = ResultsDatabase.get_database () in
(* Do the merge purely in SQL for great speed. The query works by doing a left join between the (* Do the merge purely in SQL for great speed. The query works by doing a left join between the
sub-table and the main one, and applying the same "more defined" logic as in Attributes in the sub-table and the main one, and applying the same "more defined" logic as in Attributes in the
cases where a proc_name is present in both the sub-table and the main one (main.attr_kind != cases where a proc_name is present in both the sub-table and the main one (main.attr_kind !=
NULL). All the rows that pass this filter are inserted/updated into the main table. *) NULL). All the rows that pass this filter are inserted/updated into the main table. *)
Sqlite3.exec (ResultsDatabase.get_database ()) Sqlite3.exec db
{| {|
INSERT OR REPLACE INTO attributes INSERT OR REPLACE INTO attributes
SELECT sub.proc_name, sub.attr_kind, sub.source_file, sub.proc_attributes SELECT sub.proc_name, sub.attr_kind, sub.source_file, sub.proc_attributes
@ -27,26 +28,26 @@ WHERE
OR main.attr_kind < sub.attr_kind OR main.attr_kind < sub.attr_kind
OR (main.attr_kind = sub.attr_kind AND main.source_file < sub.source_file) OR (main.attr_kind = sub.attr_kind AND main.source_file < sub.source_file)
|} |}
|> SqliteUtils.check_sqlite_error |> SqliteUtils.check_sqlite_error db
~log:(Printf.sprintf "copying attributes of database '%s'" db_file) ~log:(Printf.sprintf "copying attributes of database '%s'" db_file)
let merge_cfg_table ~db_file = let merge_cfg_table ~db_file =
Sqlite3.exec (ResultsDatabase.get_database ()) let db = ResultsDatabase.get_database () in
"INSERT OR REPLACE INTO cfg SELECT * FROM attached.cfg" Sqlite3.exec db "INSERT OR REPLACE INTO cfg SELECT * FROM attached.cfg"
|> SqliteUtils.check_sqlite_error ~log:(Printf.sprintf "copying cfg of database '%s'" db_file) |> SqliteUtils.check_sqlite_error db ~log:(Printf.sprintf "copying cfg of database '%s'" db_file)
let merge ~db_file = let merge ~db_file =
let main_db = ResultsDatabase.get_database () in let main_db = ResultsDatabase.get_database () in
SqliteUtils.check_sqlite_error ~fatal:true Sqlite3.exec main_db (Printf.sprintf "ATTACH '%s' AS attached" db_file)
~log:(Printf.sprintf "attaching database '%s'" db_file) |> SqliteUtils.check_sqlite_error ~fatal:true main_db
(Sqlite3.exec main_db (Printf.sprintf "ATTACH '%s' AS attached" db_file)) ; ~log:(Printf.sprintf "attaching database '%s'" db_file) ;
merge_attributes_table ~db_file ; merge_attributes_table ~db_file ;
merge_cfg_table ~db_file ; merge_cfg_table ~db_file ;
SqliteUtils.check_sqlite_error ~fatal:true Sqlite3.exec main_db "DETACH attached"
~log:(Printf.sprintf "detaching database '%s'" db_file) |> SqliteUtils.check_sqlite_error ~fatal:true main_db
(Sqlite3.exec main_db "DETACH attached") ; ~log:(Printf.sprintf "detaching database '%s'" db_file) ;
() ()

@ -81,7 +81,7 @@ let db_canonicalize () =
SqliteUtils.exec db ~log:"running VACUUM" ~stmt:"VACUUM" SqliteUtils.exec db ~log:"running VACUUM" ~stmt:"VACUUM"
type registered_stmt = unit -> Sqlite3.stmt type registered_stmt = unit -> Sqlite3.stmt * Sqlite3.db
let register_statement = let register_statement =
let k stmt0 = let k stmt0 =
@ -92,26 +92,26 @@ let register_statement =
L.die InternalError "Could not prepare the following statement:@\n%s@\nReason: %s" stmt0 L.die InternalError "Could not prepare the following statement:@\n%s@\nReason: %s" stmt0
error error
in in
on_close_database ~f:(fun _ -> SqliteUtils.finalize ~log:"db close callback" stmt) ; on_close_database ~f:(fun _ -> SqliteUtils.finalize db ~log:"db close callback" stmt) ;
stmt_ref := Some stmt stmt_ref := Some (stmt, db)
in in
on_new_database_connection ~f:new_statement ; on_new_database_connection ~f:new_statement ;
fun () -> fun () ->
match !stmt_ref with match !stmt_ref with
| None -> | None ->
L.(die InternalError) "database not initialized" L.(die InternalError) "database not initialized"
| Some stmt -> | Some (stmt, db) ->
Sqlite3.clear_bindings stmt Sqlite3.clear_bindings stmt
|> SqliteUtils.check_sqlite_error ~log:"clear bindings of prepared statement" ; |> SqliteUtils.check_sqlite_error db ~log:"clear bindings of prepared statement" ;
stmt (stmt, db)
in in
fun stmt_fmt -> Printf.ksprintf k stmt_fmt fun stmt_fmt -> Printf.ksprintf k stmt_fmt
let with_registered_statement get_stmt ~f = let with_registered_statement get_stmt ~f =
let stmt = get_stmt () in let stmt, db = get_stmt () in
let result = f stmt in let result = f db stmt in
Sqlite3.reset stmt |> SqliteUtils.check_sqlite_error ~log:"reset prepared statement" ; Sqlite3.reset stmt |> SqliteUtils.check_sqlite_error db ~log:"reset prepared statement" ;
result result

@ -42,4 +42,4 @@ val register_statement : ('a, unit, string, registered_stmt) Base.format4 -> 'a
statement, or about generating new statements when the connection to the DB changes: this is all statement, or about generating new statements when the connection to the DB changes: this is all
handled internally. *) handled internally. *)
val with_registered_statement : registered_stmt -> f:(Sqlite3.stmt -> 'a) -> 'a val with_registered_statement : registered_stmt -> f:(Sqlite3.db -> Sqlite3.stmt -> 'a) -> 'a

@ -15,29 +15,29 @@ let error ~fatal fmt =
(if fatal then Format.kasprintf (fun err -> raise (Error err)) else L.internal_error) fmt (if fatal then Format.kasprintf (fun err -> raise (Error err)) else L.internal_error) fmt
let check_sqlite_error ?(fatal= false) ~log rc = let check_sqlite_error ?(fatal= false) db ~log rc =
match (rc : Sqlite3.Rc.t) with match (rc : Sqlite3.Rc.t) with
| OK | ROW -> | OK | ROW ->
() ()
| _ as err -> | _ as err ->
error ~fatal "%s: %s" log (Sqlite3.Rc.to_string err) error ~fatal "%s: %s (%s)" log (Sqlite3.Rc.to_string err) (Sqlite3.errmsg db)
let exec db ~log ~stmt = 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. *) (* 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 -> try check_sqlite_error ~fatal:true db ~log (Sqlite3.exec db stmt) with Error err ->
error ~fatal:true "exec: %s" err error ~fatal:true "exec: %s (%s)" err (Sqlite3.errmsg db)
let finalize ~log stmt = let finalize db ~log stmt =
try check_sqlite_error ~fatal:true ~log (Sqlite3.finalize stmt) with try check_sqlite_error ~fatal:true db ~log (Sqlite3.finalize stmt) with
| Error err -> | Error err ->
error ~fatal:true "finalize: %s" err error ~fatal:true "finalize: %s (%s)" err (Sqlite3.errmsg db)
| Sqlite3.Error err -> | Sqlite3.Error err ->
error ~fatal:true "finalize: %s: %s" log err error ~fatal:true "finalize: %s: %s (%s)" log err (Sqlite3.errmsg db)
let sqlite_result_rev_list_step ?finalize:(do_finalize = true) ~log stmt = let sqlite_result_rev_list_step ?finalize:(do_finalize = true) db ~log stmt =
let rec aux rev_results = let rec aux rev_results =
match Sqlite3.step stmt with match Sqlite3.step stmt with
| Sqlite3.Rc.ROW -> | Sqlite3.Rc.ROW ->
@ -47,14 +47,14 @@ let sqlite_result_rev_list_step ?finalize:(do_finalize = true) ~log stmt =
| DONE -> | DONE ->
rev_results rev_results
| err -> | err ->
L.die InternalError "%s: %s" log (Sqlite3.Rc.to_string err) L.die InternalError "%s: %s (%s)" log (Sqlite3.Rc.to_string err) (Sqlite3.errmsg db)
in in
if do_finalize then protect ~finally:(fun () -> finalize ~log stmt) ~f:(fun () -> aux []) if do_finalize then protect ~finally:(fun () -> finalize db ~log stmt) ~f:(fun () -> aux [])
else aux [] else aux []
let sqlite_result_step ?finalize ~log stmt = let sqlite_result_step ?finalize db ~log stmt =
match sqlite_result_rev_list_step ?finalize ~log stmt with match sqlite_result_rev_list_step ?finalize db ~log stmt with
| [] -> | [] ->
None None
| [x] -> | [x] ->
@ -63,8 +63,8 @@ let sqlite_result_step ?finalize ~log stmt =
L.die InternalError "%s: zero or one result expected, got %d instead" log (List.length l) L.die InternalError "%s: zero or one result expected, got %d instead" log (List.length l)
let sqlite_unit_step ?finalize ~log stmt = let sqlite_unit_step ?finalize db ~log stmt =
match sqlite_result_rev_list_step ?finalize ~log stmt with match sqlite_result_rev_list_step ?finalize db ~log stmt with
| [] -> | [] ->
() ()
| l -> | l ->

@ -12,23 +12,24 @@ open! IStd
exception Error of string exception Error of string
(** The functions in this module tend to raise more often than their counterparts in [Sqlite3]. In particular, they may raise if the [Sqlite3.Rc.t] result of certain operations is unexpected. *) (** The functions in this module tend to raise more often than their counterparts in [Sqlite3]. In particular, they may raise if the [Sqlite3.Rc.t] result of certain operations is unexpected. *)
val check_sqlite_error : ?fatal:bool -> log:string -> Sqlite3.Rc.t -> unit val check_sqlite_error : ?fatal:bool -> Sqlite3.db -> log:string -> Sqlite3.Rc.t -> unit
(** Assert that the result is either [Sqlite3.Rc.OK]. If [row_is_ok] then [Sqlite3.Rc.ROW] is also accepted. If the result is not valid, then if [fatal] is set raise [Error], otherwise log the error and proceed. *) (** Assert that the result is either [Sqlite3.Rc.OK]. If [row_is_ok] then [Sqlite3.Rc.ROW] is also accepted. If the result is not valid, then if [fatal] is set raise [Error], otherwise log the error and proceed. *)
val exec : Sqlite3.db -> log:string -> stmt:string -> unit val exec : Sqlite3.db -> log:string -> stmt:string -> unit
(** Execute the given Sqlite [stmt] and asserts that it resulted in [Sqlite3.Rc.OK]. Otherwise, fail similarly to [check_sqlite_error ~fatal:true]. *) (** Execute the given Sqlite [stmt] and asserts that it resulted in [Sqlite3.Rc.OK]. Otherwise, fail similarly to [check_sqlite_error ~fatal:true]. *)
val finalize : log:string -> Sqlite3.stmt -> unit val finalize : Sqlite3.db -> log:string -> Sqlite3.stmt -> unit
(** Finalize the given [stmt]. Raises [Error] on failure. *) (** Finalize the given [stmt]. Raises [Error] on failure. *)
val sqlite_result_rev_list_step : val sqlite_result_rev_list_step :
?finalize:bool -> log:string -> Sqlite3.stmt -> Sqlite3.Data.t option list ?finalize:bool -> Sqlite3.db -> log:string -> Sqlite3.stmt -> Sqlite3.Data.t option list
(** Return a reversed list of results obtained by repeatedly stepping through [stmt] and saving only column 0 of each returned row (all that's been needed so far). *) (** Return a reversed list of results obtained by repeatedly stepping through [stmt] and saving only column 0 of each returned row (all that's been needed so far). *)
val sqlite_result_step : ?finalize:bool -> log:string -> Sqlite3.stmt -> Sqlite3.Data.t option val sqlite_result_step :
?finalize:bool -> Sqlite3.db -> log:string -> Sqlite3.stmt -> Sqlite3.Data.t option
(** Same as [sqlite_result_rev_list_step] but asserts that at most one result is returned. *) (** Same as [sqlite_result_rev_list_step] but asserts that at most one result is returned. *)
val sqlite_unit_step : ?finalize:bool -> log:string -> Sqlite3.stmt -> unit val sqlite_unit_step : ?finalize:bool -> Sqlite3.db -> log:string -> Sqlite3.stmt -> unit
(** Same as [sqlite_result_rev_list_step] but asserts that no result is returned. *) (** Same as [sqlite_result_rev_list_step] but asserts that no result is returned. *)
val db_close : Sqlite3.db -> unit val db_close : Sqlite3.db -> unit

Loading…
Cancel
Save