mirage/orm

Lazy getters

pdonadeo opened this issue · 1 comments

Another important point, now missing in this project, is the availability of some sort of "lazy getter". I have in mind something like the Enum module present in Batteries. The x_get (or x_get_enum) function should return an enumeration, so that a "SELECT *" on a large dataset wouldn't blow up the server.

This is a naive (but working) implementation for the Sqlite3 backend:

open BatStd;;

module MySqlite3 =
  struct
    include Sqlite3

    let enum (db : Sqlite3.db) (query : string) : Sqlite3.Data.t BatMap.StringMap.t BatEnum.t =
      (* stmt creation *)
      let stmt = Sqlite3.prepare db query in

      let rec prepare_tail stmt =
        match Sqlite3.prepare_tail stmt with
        | None -> ()
        | Some s -> prepare_tail s in
      prepare_tail stmt;

      (* Enum "next" *)
      let next () =
        try
          match Sqlite3.step stmt with
          | Sqlite3.Rc.ROW -> begin
              let col_names = Sqlite3.row_names stmt in
              let col_data = Sqlite3.row_data stmt in
              let len = Array.length col_names in
              let map = ref BatMap.StringMap.empty in
              for i = 0 to (len - 1) do
                map := BatMap.StringMap.add col_names.(i) col_data.(i) !map;
              done;
              !map
            end
          | Sqlite3.Rc.DONE -> begin
              Sqlite3.finalize stmt |> ignore;
              raise BatEnum.No_more_elements
            end
          | e -> begin
              failwith (Sqlite3.Rc.to_string e)
            end;
        with
        | Sqlite3.Error "Sqlite3.step called with finalized stmt" ->
            raise BatEnum.No_more_elements
      in (* let next ... *)
      BatEnum.from next
  end
;;

It returns an enumeration of maps representing records, with the keys (strings) populated with the columns name, and the values being the corresponding Sqlite3 values. You could return the "X" type, of course.

So I've pushed to my repository something which generate 't_lazy_get' which takes the same arguments as t_get, but which returns a 'unit -> t option' function instead of a 't list'. You can call the function until it returns None which means no more elements are in the query. Hopefully that should do what you want.