bench.ur 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. (** An Ur/Web solution to the TechEmpower web framework benchmarks.
  2. * For more information:
  3. * Benchmark specs: http://www.techempower.com/benchmarks/#section=code
  4. * Ur/Web: http://www.impredicative.com/ur/
  5. *)
  6. open Json
  7. (** * Utility functions *)
  8. (** The spec requires including particular HTTP response headers that the
  9. * minimal Ur/Web HTTP servers don't return, so this function is responsible
  10. * for adding them. *)
  11. fun addHeaders () =
  12. n <- now;
  13. setHeader (blessResponseHeader "Date") (timef "%a, %d %b %Y %H:%M:%S GMT" n);
  14. setHeader (blessResponseHeader "Server") "Ur/Web"
  15. (** This function handles processing of a "queries" string parameter to a few of
  16. * the benchmarks. The URL representation is mandated by the spec, so we need
  17. * to do some manual parsing to handle malformed inputs. :-( *)
  18. fun parseQueries s =
  19. case read s of
  20. None => 1
  21. | Some n => if n > 500 then 500
  22. else if n < 1 then 1
  23. else n
  24. (** Most of the benchmarks return results as JSON.
  25. * Here's a handy function to automate wrapping an arbitrary JSONable value as
  26. * a response. *)
  27. fun returnJson [a] (_ : json a) (j : a) : transaction page =
  28. addHeaders ();
  29. returnBlob (textBlob (toJson j)) (blessMime "application/json")
  30. (** Here's a similar, simpler function for returning plain text. *)
  31. fun returnText (t : string) : transaction page =
  32. addHeaders ();
  33. returnBlob (textBlob t) (blessMime "text/plain")
  34. (** Finally, an analogous wrapper for HTML *)
  35. fun returnHtml (p : page) : transaction page =
  36. addHeaders ();
  37. return p
  38. (** * Test type 1: JSON serialization *)
  39. (** Let's teach the JSON library about a new record type. *)
  40. type json_t = {Message : string}
  41. val json_conversion : json json_t = json_record {Message = "message"}
  42. fun json () =
  43. returnJson {Message = "Hello, World!"}
  44. (** * Test type 2: Single database query *)
  45. (** This test introduces a database table that a few other tests also use. *)
  46. table world : {Id : int, RandomNumber : int} PRIMARY KEY Id
  47. (** Let's tell the JSON library which record field names to use in
  48. * serialization. *)
  49. type world_t = {Id : int, RandomNumber : int}
  50. val world_conversion : json world_t =
  51. json_record {Id = "id", RandomNumber = "randomNumber"}
  52. (** Helper function to look up the entry associated with an ID *)
  53. fun world_find n =
  54. oneRow1 (SELECT World.Id, World.RandomNumber FROM world
  55. WHERE World.Id = {[n]})
  56. (** In various tests, we'll be generating random IDs for this table.
  57. * Here's a quick way to do it, limiting the legal ID range. *)
  58. val random_id =
  59. n <- rand;
  60. return ((n % 10000) + 1)
  61. (** Finally, test 2 itself! *)
  62. fun db () =
  63. n <- random_id;
  64. row <- world_find n;
  65. returnJson row
  66. (** * Test type 3: Multiple database queries *)
  67. fun queries s =
  68. rows <- List.tabulateM (fn _ => n <- random_id; world_find n) (parseQueries s);
  69. returnJson rows
  70. (** * Test type 4: Fortunes *)
  71. (** A new table, specific to this test *)
  72. table fortune : {Id : int, Message : string} PRIMARY KEY Id
  73. (** Teach the JSON library about good string names for the columns. *)
  74. type fortune_t = {Id : int, Message : string}
  75. val fortune_conversion : json fortune_t =
  76. json_record {Id = "id", Message = "message"}
  77. (** Here's the additional fortune mandated by the spec. *)
  78. val new_fortune : fortune_t =
  79. {Id = 0, Message = "Additional fortune added at request time."}
  80. (** Actual page handler *)
  81. fun fortunes () =
  82. fs <- queryL1 (SELECT Fortune.Id, Fortune.Message FROM fortune);
  83. fs' <- return (List.sort (fn x y => x.Message > y.Message)
  84. (new_fortune :: fs));
  85. returnHtml <xml>
  86. <head><title>Fortunes</title></head>
  87. <body><table>
  88. <tr><th>id</th><th>message</th></tr>
  89. {List.mapX (fn f => <xml><tr>
  90. <td>{[f.Id]}</td><td>{[f.Message]}</td>
  91. </tr></xml>) fs'}
  92. </table></body>
  93. </xml>
  94. (** * Test type 5: Database updates *)
  95. fun updates s =
  96. rows <- List.tabulateM (fn _ => n <- random_id; world_find n)
  97. (parseQueries s);
  98. rows' <- List.mapM (fn r => n <- random_id;
  99. return (r -- #RandomNumber ++ {RandomNumber = n}))
  100. rows;
  101. List.app (fn r => dml (UPDATE world SET RandomNumber = {[r.RandomNumber]}
  102. WHERE Id = {[r.Id]})) rows';
  103. returnJson rows'
  104. (** * Test type 6: Plaintext *)
  105. fun plaintext () =
  106. returnText "Hello, World!"