aboutsummaryrefslogtreecommitdiff
path: root/lib/analysers/chunk.ml
blob: 2fa4808dd163c6550859f4b317aa993eef0f727a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
(** 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 : ImportCSV.DataType.t Queue.t;
}

let create : unit -> t =
 fun () -> { b = Buffer.create 16; parameters = Queue.create () }

let create' : Buffer.t -> ImportCSV.DataType.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 -> ImportCSV.DataType.t Seq.t -> unit =
 fun t p -> Queue.add_seq t.parameters p

module Syntax = ImportConf.Syntax
module Table = ImportDataTypes.Table
module Q = ImportExpression.Query
open StdLabels

(** 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:Syntax.t -> join_buffer:t -> Syntax.Extern.t -> unit =
 fun ~conf ~join_buffer external_ ->
  let extern_table = Table.name external_.target in

  let formatter = Format.formatter_of_buffer join_buffer.b in
  Format.fprintf formatter "\nLEFT JOIN '%s' AS '%s' ON %t = %s" extern_table
    external_.target.name
    (Printers.prepare_key ~f:(fun f ->
         let q =
           Q.query_of_expression Q.BindParam f (Printers.path ~conf)
             external_.intern_key
         in

         add_parameters join_buffer (Queue.to_seq q)))
    (Table.print_column external_.Syntax.Extern.target
       ("key_" ^ external_.Syntax.Extern.target.name));

  Format.pp_print_flush formatter ()

(** 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:Syntax.Extern.t list -> Syntax.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)

let add_expression :
    conf:Syntax.t -> t -> ImportDataTypes.Path.t ImportExpression.T.t -> unit =
 fun ~conf group expression ->
  let formatter = Format.formatter_of_buffer group.b in
  let queue =
    ImportExpression.Query.query_of_expression ImportExpression.Query.BindParam
      formatter (Printers.path ~conf) expression
  in
  Format.pp_print_flush formatter ();
  add_parameters group (Queue.to_seq queue)