@ -10,93 +10,90 @@ module L = Logging
module F = Format
module Implementation = struct
let attribute_replace_statement =
(* The innermost SELECT returns either the current attributes_kind and source_file associated with
the given proc name , or default values of ( - 1 , " " ) . These default values have the property that
they are always " less than " any legit value . More precisely , MAX ensures that some value is
returned even if there is no row satisfying WHERE ( we'll get NULL in that case , the value in
the row otherwise ) . COALESCE then returns the first non - NULL value , which will be either the
value of the row corresponding to that pname in the DB , or the default if no such row exists .
The next ( second - outermost ) SELECT filters out that value if it is " more defined " than the ones
we would like to insert ( which will never be the case if the default values are returned ) . If
not , it returns a trivial row ( consisting solely of NULL since we don't use its values ) and the
INSERT OR REPLACE will proceed and insert or update the values stored into the DB for that
pname . * )
(* TRICK: use the source file to be more deterministic in case the same procedure name is defined
in several files * )
(* TRICK: older versions of sqlite ( prior to version 3.15.0 ( 2016-10-14 ) ) do not support row
values so the lexicographic ordering for ( : akind , : sfile ) is done by hand * )
ResultsDatabase . register_statement
{ |
INSERT OR REPLACE INTO procedures
SELECT : pname , : proc_name_hum , : akind , : sfile , : pattr , : cfg , : callees
FROM (
SELECT NULL
let replace_attributes =
let attribute_replace_statement =
(* The innermost SELECT returns either the current attributes_kind and source_file associated with
the given proc name , or default values of ( - 1 , " " ) . These default values have the property that
they are always " less than " any legit value . More precisely , MAX ensures that some value is
returned even if there is no row satisfying WHERE ( we'll get NULL in that case , the value in
the row otherwise ) . COALESCE then returns the first non - NULL value , which will be either the
value of the row corresponding to that pname in the DB , or the default if no such row exists .
The next ( second - outermost ) SELECT filters out that value if it is " more defined " than the ones
we would like to insert ( which will never be the case if the default values are returned ) . If
not , it returns a trivial row ( consisting solely of NULL since we don't use its values ) and the
INSERT OR REPLACE will proceed and insert or update the values stored into the DB for that
pname . * )
(* TRICK: use the source file to be more deterministic in case the same procedure name is defined
in several files * )
(* TRICK: older versions of sqlite ( prior to version 3.15.0 ( 2016-10-14 ) ) do not support row
values so the lexicographic ordering for ( : akind , : sfile ) is done by hand * )
ResultsDatabase . register_statement
{ |
INSERT OR REPLACE INTO procedures
SELECT : pname , : proc_name_hum , : akind , : sfile , : pattr , : cfg , : callees
FROM (
SELECT COALESCE ( MAX ( attr_kind ) , - 1 ) AS attr_kind ,
COALESCE ( MAX ( source_file ) , " " ) AS source_file
FROM procedures
WHERE proc_name = : pname )
WHERE attr_kind < : akind
OR ( attr_kind = : akind AND source_file < = : sfile ) )
| }
let replace_attributes ~ pname_str ~ pname ~ akind ~ source_file ~ attributes ~ proc_desc ~ callees =
ResultsDatabase . with_registered_statement attribute_replace_statement ~ f : ( fun db replace_stmt ->
Sqlite3 . bind replace_stmt 1 (* :pname *) pname
| > SqliteUtils . check_result_code db ~ log : " replace bind pname " ;
Sqlite3 . bind replace_stmt 2 (* :proc_name_hum *) ( Sqlite3 . Data . TEXT pname_str )
| > SqliteUtils . check_result_code db ~ log : " replace bind proc_name_hum " ;
Sqlite3 . bind replace_stmt 3 (* :akind *) ( Sqlite3 . Data . INT akind )
| > SqliteUtils . check_result_code db ~ log : " replace bind attribute kind " ;
Sqlite3 . bind replace_stmt 4 (* :sfile *) source_file
| > SqliteUtils . check_result_code db ~ log : " replace bind source file " ;
Sqlite3 . bind replace_stmt 5 (* :pattr *) attributes
| > SqliteUtils . check_result_code db ~ log : " replace bind proc attributes " ;
Sqlite3 . bind replace_stmt 6 (* :cfg *) proc_desc
| > SqliteUtils . check_result_code db ~ log : " replace bind cfg " ;
Sqlite3 . bind replace_stmt 7 (* :callees *) callees
| > SqliteUtils . check_result_code db ~ log : " replace bind callees " ;
SqliteUtils . result_unit db ~ finalize : false ~ log : " Attributes.replace " replace_stmt )
let source_file_store_statement =
ResultsDatabase . register_statement
{ |
INSERT OR REPLACE INTO source_files
VALUES ( : source , : tenv , : integer_type_widths , : proc_names , : freshly_captured )
| }
let add_source_file ~ source_file ~ tenv ~ integer_type_widths ~ proc_names =
ResultsDatabase . with_registered_statement source_file_store_statement ~ f : ( fun db store_stmt ->
Sqlite3 . bind store_stmt 1 source_file
(* :source *)
| > SqliteUtils . check_result_code db ~ log : " store bind source file " ;
Sqlite3 . bind store_stmt 2 tenv
(* :tenv *)
| > SqliteUtils . check_result_code db ~ log : " store bind type environment " ;
Sqlite3 . bind store_stmt 3 integer_type_widths
(* :integer_type_widths *)
| > SqliteUtils . check_result_code db ~ log : " store bind integer type widths " ;
Sqlite3 . bind store_stmt 4 proc_names
(* :proc_names *)
| > SqliteUtils . check_result_code db ~ log : " store bind proc names " ;
Sqlite3 . bind store_stmt 5 ( Sqlite3 . Data . INT Int64 . one )
(* :freshly_captured *)
| > SqliteUtils . check_result_code db ~ log : " store freshness " ;
SqliteUtils . result_unit ~ finalize : false ~ log : " Cfg.store " db store_stmt )
let mark_all_source_files_stale_statement =
ResultsDatabase . register_statement " UPDATE source_files SET freshly_captured = 0 "
SELECT NULL
FROM (
SELECT COALESCE ( MAX ( attr_kind ) , - 1 ) AS attr_kind ,
COALESCE ( MAX ( source_file ) , " " ) AS source_file
FROM procedures
WHERE proc_name = : pname )
WHERE attr_kind < : akind
OR ( attr_kind = : akind AND source_file < = : sfile ) )
| }
in
fun ~ pname_str ~ pname ~ akind ~ source_file ~ attributes ~ proc_desc ~ callees ->
ResultsDatabase . with_registered_statement attribute_replace_statement
~ f : ( fun db replace_stmt ->
Sqlite3 . bind replace_stmt 1 (* :pname *) pname
| > SqliteUtils . check_result_code db ~ log : " replace bind pname " ;
Sqlite3 . bind replace_stmt 2 (* :proc_name_hum *) ( Sqlite3 . Data . TEXT pname_str )
| > SqliteUtils . check_result_code db ~ log : " replace bind proc_name_hum " ;
Sqlite3 . bind replace_stmt 3 (* :akind *) ( Sqlite3 . Data . INT akind )
| > SqliteUtils . check_result_code db ~ log : " replace bind attribute kind " ;
Sqlite3 . bind replace_stmt 4 (* :sfile *) source_file
| > SqliteUtils . check_result_code db ~ log : " replace bind source file " ;
Sqlite3 . bind replace_stmt 5 (* :pattr *) attributes
| > SqliteUtils . check_result_code db ~ log : " replace bind proc attributes " ;
Sqlite3 . bind replace_stmt 6 (* :cfg *) proc_desc
| > SqliteUtils . check_result_code db ~ log : " replace bind cfg " ;
Sqlite3 . bind replace_stmt 7 (* :callees *) callees
| > SqliteUtils . check_result_code db ~ log : " replace bind callees " ;
SqliteUtils . result_unit db ~ finalize : false ~ log : " Attributes.replace " replace_stmt )
let add_source_file =
let source_file_store_statement =
ResultsDatabase . register_statement
{ |
INSERT OR REPLACE INTO source_files
VALUES ( : source , : tenv , : integer_type_widths , : proc_names , : freshly_captured )
| }
in
fun ~ source_file ~ tenv ~ integer_type_widths ~ proc_names ->
ResultsDatabase . with_registered_statement source_file_store_statement ~ f : ( fun db store_stmt ->
Sqlite3 . bind store_stmt 1 source_file
(* :source *)
| > SqliteUtils . check_result_code db ~ log : " store bind source file " ;
Sqlite3 . bind store_stmt 2 tenv
(* :tenv *)
| > SqliteUtils . check_result_code db ~ log : " store bind type environment " ;
Sqlite3 . bind store_stmt 3 integer_type_widths
(* :integer_type_widths *)
| > SqliteUtils . check_result_code db ~ log : " store bind integer type widths " ;
Sqlite3 . bind store_stmt 4 proc_names
(* :proc_names *)
| > SqliteUtils . check_result_code db ~ log : " store bind proc names " ;
Sqlite3 . bind store_stmt 5 ( Sqlite3 . Data . INT Int64 . one )
(* :freshly_captured *)
| > SqliteUtils . check_result_code db ~ log : " store freshness " ;
SqliteUtils . result_unit ~ finalize : false ~ log : " Cfg.store " db store_stmt )
let mark_all_source_files_stale () =
ResultsDatabase . with_registered_statement mark_all_source_files_stale_statement
~ f : ( fun db stmt -> SqliteUtils . result_unit db ~ finalize : false ~ log : " mark_all_stale " stmt )
ResultsDatabase . get_database ()
| > SqliteUtils . exec ~ stmt : " UPDATE source_files SET freshly_captured = 0 " ~ log : " mark_all_stale "
let merge_procedures_table ~ db_file =