[sqlite] Normalize blobs used for comparison

Summary:
Using `Marshal.to_string` to create SQLite values used in comparisons is brittle as there is no guarantee that it will return the same value for structurally equal values.
When adding sharing, this will definitely break.

From the SQLite queries I found, only `SourceFile` and `Procname` are used in comparisons.
I haven't tested performance.
It shouldn't change anything for `SourceFile` as there is no possible sharing.
It shouldn't change much for `Procname` as they are pretty small anyway.

Reviewed By: ngorogiannis

Differential Revision: D15923122

fbshipit-source-id: ce4af1fe3
master
Mehdi Bouaziz 6 years ago committed by Facebook Github Bot
parent 330b266d28
commit 5f8514a8c2

@ -173,6 +173,6 @@ let pp f
F.fprintf f "; proc_id= %s }@]" (Typ.Procname.to_unique_id proc_name) F.fprintf f "; proc_id= %s }@]" (Typ.Procname.to_unique_id proc_name)
module SQLite = SqliteUtils.MarshalledData (struct module SQLite = SqliteUtils.MarshalledDataNOTForComparison (struct
type nonrec t = t type nonrec t = t
end) end)

@ -867,7 +867,7 @@ let is_connected proc_desc =
error error
module SQLite = SqliteUtils.MarshalledNullableData (struct module SQLite = SqliteUtils.MarshalledNullableDataNOTForComparison (struct
type nonrec t = t type nonrec t = t
end) end)

@ -76,6 +76,10 @@ let pp_per_file fmt = function
module SQLite : SqliteUtils.Data with type t = per_file = struct module SQLite : SqliteUtils.Data with type t = per_file = struct
module Serializer = SqliteUtils.MarshalledDataNOTForComparison (struct
type nonrec t = t
end)
type t = per_file type t = per_file
let global_string = "global" let global_string = "global"
@ -84,14 +88,14 @@ module SQLite : SqliteUtils.Data with type t = per_file = struct
| Global -> | Global ->
Sqlite3.Data.TEXT global_string Sqlite3.Data.TEXT global_string
| FileLocal tenv -> | FileLocal tenv ->
Sqlite3.Data.BLOB (Marshal.to_string tenv []) Serializer.serialize tenv
let deserialize = function[@warning "-8"] let deserialize = function
| Sqlite3.Data.TEXT g when String.equal g global_string -> | Sqlite3.Data.TEXT g when String.equal g global_string ->
Global Global
| Sqlite3.Data.BLOB b -> | blob ->
FileLocal (Marshal.from_string b 0) FileLocal (Serializer.deserialize blob)
end end
let merge ~src ~dst = TypenameHash.iter (fun pname cfg -> TypenameHash.replace dst pname cfg) src let merge ~src ~dst = TypenameHash.iter (fun pname cfg -> TypenameHash.replace dst pname cfg) src

@ -19,7 +19,7 @@ module IntegerWidths = struct
let java = {char_width= 16; short_width= 16; int_width= 32; long_width= 64; longlong_width= 64} let java = {char_width= 16; short_width= 16; int_width= 32; long_width= 64; longlong_width= 64}
module SQLite = SqliteUtils.MarshalledNullableData (struct module SQLite = SqliteUtils.MarshalledNullableDataNOTForComparison (struct
type nonrec t = t type nonrec t = t
end) end)
@ -1359,9 +1359,7 @@ module Procname = struct
let to_filename ?crc_only pname = to_concrete_filename ?crc_only pname let to_filename ?crc_only pname = to_concrete_filename ?crc_only pname
module SQLite = struct module SQLite = struct
let pname_to_key = module T = struct
Base.Hashtbl.create
( module struct
type nonrec t = t type nonrec t = t
let compare = compare let compare = compare
@ -1369,23 +1367,23 @@ module Procname = struct
let hash = hash let hash = hash
let sexp_of_t p = Sexp.Atom (to_string p) let sexp_of_t p = Sexp.Atom (to_string p)
end ) end
module Serializer = SqliteUtils.MarshalledDataForComparison (T)
let pname_to_key = Base.Hashtbl.create (module T)
let serialize pname = let serialize pname =
let default () = Sqlite3.Data.BLOB (Marshal.to_string pname []) in let default () = Serializer.serialize pname in
Base.Hashtbl.find_or_add pname_to_key pname ~default Base.Hashtbl.find_or_add pname_to_key pname ~default
let deserialize : Sqlite3.Data.t -> t = function[@warning "-8"] let deserialize = Serializer.deserialize
| Sqlite3.Data.BLOB b ->
Marshal.from_string b 0
let clear_cache () = Base.Hashtbl.clear pname_to_key let clear_cache () = Base.Hashtbl.clear pname_to_key
end end
module SQLiteList = SqliteUtils.MarshalledData (struct module SQLiteList = SqliteUtils.MarshalledDataNOTForComparison (struct
type nonrec t = t list type nonrec t = t list
end) end)
end end

@ -180,19 +180,24 @@ let changed_sources_from_changed_files changed_files =
module SQLite = struct module SQLite = struct
module T = struct
type nonrec t = t type nonrec t = t
end
module Serializer = SqliteUtils.MarshalledDataForComparison (T)
include T
let serialize = function let serialize = function
| RelativeProjectRoot path -> | RelativeProjectRoot path ->
(* show the most common paths as text (for debugging, possibly perf) *) (* show the most common paths as text (for debugging, possibly perf) *)
Sqlite3.Data.TEXT path Sqlite3.Data.TEXT path
| _ as x -> | _ as x ->
Sqlite3.Data.BLOB (Marshal.to_string x []) Serializer.serialize x
let deserialize = function[@warning "-8"] let deserialize = function
| Sqlite3.Data.TEXT rel_path -> | Sqlite3.Data.TEXT rel_path ->
RelativeProjectRoot rel_path RelativeProjectRoot rel_path
| Sqlite3.Data.BLOB b -> | blob ->
Marshal.from_string b 0 Serializer.deserialize blob
end end

@ -107,10 +107,25 @@ module type Data = sig
val deserialize : Sqlite3.Data.t -> t val deserialize : Sqlite3.Data.t -> t
end end
module MarshalledData (D : sig module type T = sig
type t type t
end) = end
struct
module MarshalledDataForComparison (D : T) = struct
type t = D.t
let deserialize = function[@warning "-8"] Sqlite3.Data.BLOB b -> Marshal.from_string b 0
(*
If the serialized data is used for comparison (e.g. used in WHERE clause), we need to normalize it.
Marshalling is brittle as it depends on sharing.
For now let's suppose that marshalling with no sharing is normalizing.
*)
let serialize x = Sqlite3.Data.BLOB (Marshal.to_string x [Marshal.No_sharing])
end
module MarshalledDataNOTForComparison (D : T) = struct
type t = D.t type t = D.t
let deserialize = function[@warning "-8"] Sqlite3.Data.BLOB b -> Marshal.from_string b 0 let deserialize = function[@warning "-8"] Sqlite3.Data.BLOB b -> Marshal.from_string b 0
@ -118,10 +133,7 @@ struct
let serialize x = Sqlite3.Data.BLOB (Marshal.to_string x []) let serialize x = Sqlite3.Data.BLOB (Marshal.to_string x [])
end end
module MarshalledNullableData (D : sig module MarshalledNullableDataNOTForComparison (D : T) = struct
type t
end) =
struct
type t = D.t option type t = D.t option
let deserialize = function[@warning "-8"] let deserialize = function[@warning "-8"]

@ -72,12 +72,17 @@ module type Data = sig
val deserialize : Sqlite3.Data.t -> t val deserialize : Sqlite3.Data.t -> t
end end
(** A default implementation of the Data API that encodes every objects as marshalled blobs with no sharing *)
module MarshalledDataForComparison (D : sig
type t
end) : Data with type t = D.t
(** A default implementation of the Data API that encodes every objects as marshalled blobs *) (** A default implementation of the Data API that encodes every objects as marshalled blobs *)
module MarshalledData (D : sig module MarshalledDataNOTForComparison (D : sig
type t type t
end) : Data with type t = D.t end) : Data with type t = D.t
(** A default implementation of the Data API that encodes None as a NULL SQLite value *) (** A default implementation of the Data API that encodes None as a NULL SQLite value *)
module MarshalledNullableData (D : sig module MarshalledNullableDataNOTForComparison (D : sig
type t type t
end) : Data with type t = D.t option end) : Data with type t = D.t option

Loading…
Cancel
Save