From 27308d19c19a2d7215e5ea88fda97074e146d44c Mon Sep 17 00:00:00 2001 From: David Scott Date: Wed, 26 Jan 2011 17:39:05 +0000 Subject: [PATCH] Always regenerate the 'many' side of a 'one-to-many' relationship at indexing time. This means that every system start acts equivalently to an upgrade from a previous version, simplifying testing. Signed-off-by: David Scott --- ocaml/database/database_test.ml | 6 +++-- ocaml/database/db_backend.ml | 1 - ocaml/database/db_cache_types.ml | 41 +++++++++++++++++++++++++++++++- ocaml/database/schema.ml | 28 ++++++++++++---------- 4 files changed, 59 insertions(+), 17 deletions(-) diff --git a/ocaml/database/database_test.ml b/ocaml/database/database_test.ml index c73dddd9..3a35073a 100644 --- a/ocaml/database/database_test.ml +++ b/ocaml/database/database_test.ml @@ -361,8 +361,10 @@ module Tests = functor(Client: Db_interface.DB_ACCESS) -> struct Printf.printf "read_record foreign key\n"; Client.create_row "VBD" (make_vbd valid_ref vbd_ref vbd_uuid) vbd_ref; let fv_list, fvs_list = Client.read_record "VM" valid_ref in - if List.assoc "VBDs" fvs_list <> [ vbd_ref ] - then failwith "read_record 3"; + if List.assoc "VBDs" fvs_list <> [ vbd_ref ] then begin + Printf.printf "fv_list = [ %s ] fvs_list = [ %s ]\n%!" (String.concat "; " (List.map (fun (k, v) -> k ^":" ^ v) fv_list)) (String.concat "; " (List.map (fun (k, v) -> k ^ ":" ^ (String.concat ", " v)) fvs_list)); + failwith "read_record 3" + end; Printf.printf "read_record deleted foreign key\n"; Client.delete_row "VBD" vbd_ref; let fv_list, fvs_list = Client.read_record "VM" valid_ref in diff --git a/ocaml/database/db_backend.ml b/ocaml/database/db_backend.ml index ea71000e..46262df3 100644 --- a/ocaml/database/db_backend.ml +++ b/ocaml/database/db_backend.ml @@ -42,7 +42,6 @@ let get_database () = !database (* non-persistent fields will have been flushed to disk anyway [since non-persistent just means dont trigger a flush if I change]. Hence we blank non-persistent fields with a suitable empty value, depending on their type *) let blow_away_non_persistent_fields (schema: Schema.t) db = - Printf.printf "blow_away\n%!"; (* Generate a new row given a table schema *) let row schema row : Row.t = Row.fold diff --git a/ocaml/database/db_cache_types.ml b/ocaml/database/db_cache_types.ml index 28c37989..197812b7 100644 --- a/ocaml/database/db_cache_types.ml +++ b/ocaml/database/db_cache_types.ml @@ -222,8 +222,47 @@ module Database = struct ) tbl acc) x.tables KeyMap.empty in + (* For each of the one-to-many relationships, recompute the many end *) + let tables = + Schema.StringMap.fold + (fun one_tblname rels tables -> + List.fold_left (fun tables (one_fldname, many_tblname, many_fldname) -> + (* VBD.VM : Ref(VM) -> VM.VBDs : Set(Ref(VBD)) *) + let one_tbl = TableSet.find one_tblname tables in + let many_tbl = TableSet.find many_tblname tables in + (* Initialise all VM.VBDs = [] (otherwise VMs with no + VBDs may be missing a VBDs field altogether on + upgrade) *) + let many_tbl' = Table.fold + (fun vm row acc -> + let row' = Row.add many_fldname (SExpr.string_of (SExpr.Node [])) row in + Table.add vm row' acc) + many_tbl Table.empty in + + (* Build up a table of VM -> VBDs *) + + let vm_to_vbds = Table.fold + (fun vbd row acc -> + let vm = Row.find one_fldname row in + let existing = if Schema.StringMap.mem vm acc then Schema.StringMap.find vm acc else [] in + Schema.StringMap.add vm (vbd :: existing) acc) + one_tbl Schema.StringMap.empty in + let many_tbl'' = Schema.StringMap.fold + (fun vm vbds acc -> + if not(Table.mem vm acc) + then acc + else + let row = Table.find vm acc in + let vbds' = SExpr.string_of (SExpr.Node (List.map (fun x -> SExpr.String x) vbds)) in + let row' = Row.add many_fldname vbds' row in + Table.add vm row' acc) + vm_to_vbds many_tbl' in + TableSet.add many_tblname many_tbl'' tables) + tables rels) + x.schema.Schema.one_to_many + x.tables in - { x with keymap = keymap } + { x with keymap = keymap; tables = tables } let table_of_ref rf db = fst (KeyMap.find (Ref rf) db.keymap) diff --git a/ocaml/database/schema.ml b/ocaml/database/schema.ml index 5e8df5dc..2818e3f1 100644 --- a/ocaml/database/schema.ml +++ b/ocaml/database/schema.ml @@ -84,20 +84,22 @@ let of_datamodel () = [] -> acc | (Datamodel_types.Field f)::fs -> flatten_fields fs (f::acc) | (Datamodel_types.Namespace (_,internal_fs))::fs -> flatten_fields fs (flatten_fields internal_fs acc) in - let column f = { - Column.name = Escaping.escape_id f.Datamodel_types.full_name; - persistent = f.Datamodel_types.field_persist; - empty = Datamodel_values.gen_empty_db_val f.Datamodel_types.ty; - (* NB Set(Ref _) fields aren't allowed to have a default value specified so we hardcode one here *) - default = begin match f.Datamodel_types.ty with - | Datamodel_types.Set (Datamodel_types.Ref _) -> Some (SExpr.string_of (SExpr.Node [])) - | _ -> Opt.map Datamodel_values.to_db_string f.Datamodel_types.default_value - end ; - issetref = begin match f.Datamodel_types.ty with + let column f = + let issetref = match f.Datamodel_types.ty with | Datamodel_types.Set (Datamodel_types.Ref _) -> true - | _ -> false - end ; - } in + | _ -> false in + { + Column.name = Escaping.escape_id f.Datamodel_types.full_name; + (* NB we always regenerate Set(Ref _) fields *) + persistent = f.Datamodel_types.field_persist && not issetref; + empty = Datamodel_values.gen_empty_db_val f.Datamodel_types.ty; + (* NB Set(Ref _) fields aren't allowed to have a default value specified so we hardcode one here *) + default = + if issetref + then Some (SExpr.string_of (SExpr.Node [])) + else Opt.map Datamodel_values.to_db_string f.Datamodel_types.default_value ; + issetref = issetref; + } in (* We store the reference in two places for no good reason still: *) let _ref = { -- 2.39.5