(** The module allow to create fragment in the query which keep together the binderd parameters and the text of the query.contents. This is used a lot in order to create the CTE, where you need the create fragment used both in the main request and partially in the CTE itself. The content is mutable and all the functions are returning [unit]. *) type t = { b : Buffer.t; parameters : ImportDataTypes.Value.t Queue.t; } let create : unit -> t = fun () -> { b = Buffer.create 16; parameters = Queue.create () } let create' : Buffer.t -> ImportDataTypes.Value.t Queue.t -> t = fun b parameters -> { b; parameters } (* Append the element from [tail] at the end of [head] Tail is destroyed during the operation. *) let append : head:t -> tail:t -> unit = fun ~head ~tail -> match Buffer.length tail.b with | 0 -> () | _ -> Buffer.add_buffer head.b tail.b; Queue.transfer tail.parameters head.parameters; () (** Add a litteral string in the sequence *) let add_string : t -> string -> unit = fun t v -> Buffer.add_string t.b v let copy : t -> t = fun t -> let b = Buffer.create 16 and parameters = Queue.copy t.parameters in Buffer.add_buffer b t.b; { b; parameters } let add_parameters : t -> ImportDataTypes.Value.t Seq.t -> unit = fun t p -> Queue.add_seq t.parameters p module Table = ImportDataTypes.Table open StdLabels let add_expression : repr:(Format.formatter -> 'a -> unit) -> t -> 'a ImportExpression.T.t -> unit = fun ~repr group expression -> let formatter = Format.formatter_of_buffer group.b in Format.pp_print_char formatter '('; let queue = ImportExpression.Query.query_of_expression ImportExpression.Query.BindParam formatter repr expression in Format.pp_print_char formatter ')'; Format.pp_print_flush formatter (); add_parameters group (Queue.to_seq queue) (** Extract the informations from the dependancies. We get two informations here : - the join query in order to load the data from the external column - the column corresponding to the key in order to identify the missing links later. *) let join_external : conf:ImporterSyntax.t -> join_buffer:t -> ImporterSyntax.Extern.t -> unit = fun ~conf ~join_buffer external_ -> let extern_table = Table.name external_.target in add_string join_buffer "\nLEFT JOIN '"; add_string join_buffer extern_table; add_string join_buffer "' AS '"; add_string join_buffer external_.target.name; add_string join_buffer "' ON "; add_string join_buffer (Format.asprintf "%t = %s" (Printers.prepare_key ~f:(fun f -> let q = ImportExpression.Query.query_of_expression ImportExpression.Query.BindParam f (Printers.path ~conf) external_.intern_key in add_parameters join_buffer (Queue.to_seq q))) (Table.print_column external_.ImporterSyntax.Extern.target ("key_" ^ external_.ImporterSyntax.Extern.target.name))); (* Add the filters given for this external in the query *) let table = external_.ImporterSyntax.Extern.target and filters = external_.ImporterSyntax.Extern.filters in List.iter filters ~f:(fun f -> add_string join_buffer " AND "; add_expression ~repr:(fun formatter column -> Format.fprintf formatter "%s" (Table.print_column table ("col_" ^ string_of_int column))) join_buffer f) (** Create the from part of the query, adding all the required externals (even when not required) SQLite is able to optimize the query and do not load the table not used in the select clause. *) let create_from_statement_of_chunck : ?externals:ImporterSyntax.Extern.t list -> ImporterSyntax.t -> t -> unit = fun ?externals conf c -> let externals = Option.value externals ~default:conf.externals in add_string c "\nFROM '"; add_string c (Table.name conf.source); add_string c "' AS '"; add_string c conf.source.name; add_string c "'"; (* Add the externals in the query *) List.iter externals ~f:(join_external ~conf ~join_buffer:c) (** Add a list of expressions into the group *) let add_expressions : repr:(Format.formatter -> 'a -> unit) -> sep:string -> t -> 'a ImportExpression.T.t list -> unit = fun ~repr ~sep group exppressions -> let formatter = Format.formatter_of_buffer group.b in let () = Format.pp_print_list ~pp_sep:(fun f () -> Format.pp_print_string f sep) (fun formatter column -> Format.pp_print_char formatter '('; let seq = ImportExpression.Query.query_of_expression ImportExpression.Query.BindParam formatter repr column in Format.pp_print_char formatter ')'; Queue.transfer seq group.parameters) formatter exppressions in Format.pp_print_flush formatter ()