aboutsummaryrefslogtreecommitdiff
path: root/lib/analysers/filters.ml
blob: 15e8cda2ec5eada9654719e47dc63abfc23eb4e1 (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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
(** Build a fragment of the query match a filter *)

module Path = ImportDataTypes.Path
module Expression = ImportExpression
open StdLabels

type 'a cte_acc = {
  n : int;
  has_previous : bool;
  acc : 'a;
  cte_index : int option;
  latest_expression : Path.t Expression.T.t list;
}

let add_inner : conf:ImporterSyntax.t -> int -> Buffer.t -> unit =
 fun ~conf n b ->
  let name = "filter" ^ string_of_int n in
  (* We use an INNER JOIN here because we want to be sure to get all the rows
     fetched by the CTE *)
  Buffer.add_string b "\nINNER JOIN '";
  Buffer.add_string b name;
  Buffer.add_string b "' ON ";
  Buffer.add_string b name;
  Buffer.add_string b ".id = ";
  Buffer.add_string b conf.source.name;
  Buffer.add_string b ".id\n"

let print :
    conf:ImporterSyntax.t ->
    (Chunk.t * Chunk.t) cte_acc ->
    ImporterSyntax.CTE.t ->
    (Chunk.t * Chunk.t) cte_acc =
 fun ~conf acc cte ->
  let predicates, query = acc.acc in
  let n = acc.n in
  let cte_index =
    match cte.ImporterSyntax.CTE.group with
    | Some expression ->
        begin
          if acc.has_previous then Chunk.add_string query ", "
          else Chunk.add_string query "WITH "
        end;
        Chunk.add_string query "filter";
        Chunk.add_string query (string_of_int n);
        Chunk.add_string query " AS (";
        Chunk.add_string query "SELECT ";
        Chunk.add_string query conf.source.name;
        Chunk.add_string query ".id, ";
        Chunk.add_expression ~repr:(Printers.path ~conf) query expression;
        Chunk.add_string query " AS group_function";
        Chunk.create_from_statement_of_chunck conf query;

        if acc.has_previous then begin
          let previous_name = "filter" ^ string_of_int (n - 1) in
          add_inner ~conf (n - 1) query.Chunk.b;

          Chunk.add_string query "WHERE ";
          Chunk.add_string query previous_name;
          Chunk.add_string query ".group_function"
        end;

        begin
          match cte.ImporterSyntax.CTE.filters with
          | [] -> ()
          | _ ->
              Chunk.add_string query " WHERE ";
              Chunk.add_expressions ~sep:"\nAND " ~repr:(Printers.path ~conf)
                query cte.ImporterSyntax.CTE.filters
        end;
        Chunk.add_string query ")\n";
        Some acc.n
    | None ->
        (* Do not add the filters in the CTE (we don’t have any) but in the main
         query *)
        Chunk.add_string predicates "WHERE ";
        Chunk.add_expressions ~sep:"\nAND " ~repr:(Printers.path ~conf)
          predicates cte.ImporterSyntax.CTE.filters;
        acc.cte_index
  in
  {
    acc with
    has_previous = true;
    n = acc.n + 1;
    cte_index;
    latest_expression = cte.ImporterSyntax.CTE.filters;
  }

let generate_sql :
    conf:ImporterSyntax.t -> ImporterSyntax.CTE.t list -> Chunk.t -> Chunk.t =
 fun ~conf filters links' ->
  let predicates = Chunk.create () and links = Chunk.create () in
  let eval =
    List.fold_left filters
      ~init:
        {
          n = 0;
          has_previous = false;
          acc = (links, predicates);
          cte_index = None;
          latest_expression = [];
        }
      ~f:(print ~conf)
  in
  match (eval.cte_index, eval.latest_expression) with
  | None, [] -> predicates
  | None, _ ->
      Chunk.add_string links' " ";
      Chunk.append ~head:links' ~tail:links;
      predicates
  | Some n, [] ->
      add_inner ~conf n links'.b;
      Chunk.append ~head:links' ~tail:links;
      Chunk.add_string links' "filter";
      Chunk.add_string links' (string_of_int n);
      Chunk.add_string links' ".group_function";
      predicates
  | Some n, _ ->
      add_inner ~conf n links'.b;
      Chunk.append ~head:links' ~tail:links;
      Chunk.add_string links' " AND filter";
      Chunk.add_string links' (string_of_int n);
      Chunk.add_string links' ".group_function";
      predicates