Minimal SQLite key-value store for OCaml
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make Sqlite.Table read through B-tree, not stale cache

Table operations now read the B-tree directly instead of a hash table
cache populated once at open time. This fixes multi-handle safety:
a second handle can no longer bypass duplicate-key checks or collide
on rowids.

+23 -49
+2 -1
lib/sqlite.mli
··· 65 65 *) 66 66 67 67 val sync : t -> unit 68 - (** [sync t] flushes all pending writes to disk. *) 68 + (** [sync t] flushes all pending writes to disk. Uses a write-ahead log for 69 + crash safety when backed by a file. *) 69 70 70 71 val close : t -> unit 71 72 (** [close t] syncs and closes the database. *)
+21 -48
test/test_sqlite.ml
··· 3 3 SPDX-License-Identifier: MIT 4 4 ---------------------------------------------------------------------------*) 5 5 6 + let temp_db prefix = 7 + let path = Filename.temp_file ("sqlite_" ^ prefix ^ "_") ".db" in 8 + Sys.remove path; 9 + path 10 + 6 11 let with_temp_db f = 7 12 Eio_main.run @@ fun env -> 8 13 let fs = Eio.Stdenv.fs env in 9 - let cwd = Eio.Stdenv.cwd env in 10 - let tmp_dir = Eio.Path.(cwd / "_build" / "test_sqlite") in 11 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 tmp_dir with Eio.Io _ -> ()); 12 - let path = Eio.Path.(tmp_dir / Fmt.str "test_%d.db" (Random.int 1_000_000)) in 14 + let path = Eio.Path.(fs / temp_db "test") in 13 15 Eio.Switch.run @@ fun sw -> 14 16 let db = Sqlite.open_ ~sw ~create:true path in 15 17 Fun.protect ~finally:(fun () -> Sqlite.close db) (fun () -> f fs db) ··· 254 256 255 257 let test_persistence_basic () = 256 258 Eio_main.run @@ fun env -> 257 - let cwd = Eio.Stdenv.cwd env in 258 - let tmp_dir = Eio.Path.(cwd / "_build" / "test_sqlite") in 259 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 tmp_dir with Eio.Io _ -> ()); 260 - let path = 261 - Eio.Path.(tmp_dir / Fmt.str "persist_%d.db" (Random.int 1_000_000)) 262 - in 259 + let fs = Eio.Stdenv.fs env in 260 + let path = Eio.Path.(fs / temp_db "persist") in 263 261 (* Create and write *) 264 262 Eio.Switch.run (fun sw -> 265 263 let db = Sqlite.open_ ~sw ~create:true path in ··· 277 275 278 276 let test_persistence_with_delete () = 279 277 Eio_main.run @@ fun env -> 280 - let cwd = Eio.Stdenv.cwd env in 281 - let tmp_dir = Eio.Path.(cwd / "_build" / "test_sqlite") in 282 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 tmp_dir with Eio.Io _ -> ()); 283 - let path = 284 - Eio.Path.(tmp_dir / Fmt.str "persist_del_%d.db" (Random.int 1_000_000)) 285 - in 278 + let fs = Eio.Stdenv.fs env in 279 + let path = Eio.Path.(fs / temp_db "persist_del") in 286 280 (* Create, write, delete *) 287 281 Eio.Switch.run (fun sw -> 288 282 let db = Sqlite.open_ ~sw ~create:true path in ··· 301 295 302 296 let test_persistence_tables () = 303 297 Eio_main.run @@ fun env -> 304 - let cwd = Eio.Stdenv.cwd env in 305 - let tmp_dir = Eio.Path.(cwd / "_build" / "test_sqlite") in 306 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 tmp_dir with Eio.Io _ -> ()); 307 - let path = 308 - Eio.Path.(tmp_dir / Fmt.str "persist_tbl_%d.db" (Random.int 1_000_000)) 309 - in 298 + let fs = Eio.Stdenv.fs env in 299 + let path = Eio.Path.(fs / temp_db "persist_tbl") in 310 300 (* Create with tables *) 311 301 Eio.Switch.run (fun sw -> 312 302 let db = Sqlite.open_ ~sw ~create:true path in ··· 587 577 let with_temp_path f = 588 578 Eio_main.run @@ fun env -> 589 579 let fs = Eio.Stdenv.fs env in 590 - let tmp_dir = "/tmp/test_sqlite" in 591 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 Eio.Path.(fs / tmp_dir) 592 - with Eio.Io _ -> ()); 593 - let fpath = Fmt.str "%s/test_%d.db" tmp_dir (Random.int 1_000_000) in 580 + let fpath = temp_db "generic" in 594 581 let path = Eio.Path.(fs / fpath) in 595 582 Fun.protect 596 583 ~finally:(fun () -> try Sys.remove fpath with Sys_error _ -> ()) ··· 715 702 716 703 let with_temp_db_path f = 717 704 Eio_main.run @@ fun env -> 718 - let cwd = Eio.Stdenv.cwd env in 719 - let tmp_dir = Eio.Path.(cwd / "_build" / "test_sqlite") in 720 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 tmp_dir with Eio.Io _ -> ()); 721 - let path = Eio.Path.(tmp_dir / Fmt.str "spec_%d.db" (Random.int 1_000_000)) in 705 + let fs = Eio.Stdenv.fs env in 706 + let path = Eio.Path.(fs / temp_db "spec") in 722 707 Eio.Switch.run @@ fun sw -> 723 708 let db = Sqlite.open_ ~sw ~create:true path in 724 709 Fun.protect ~finally:(fun () -> Sqlite.close db) (fun () -> f path db) ··· 823 808 824 809 let test_sqlite_overflow_persistence () = 825 810 Eio_main.run @@ fun env -> 826 - let cwd = Eio.Stdenv.cwd env in 827 - let tmp_dir = Eio.Path.(cwd / "_build" / "test_sqlite") in 828 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 tmp_dir with Eio.Io _ -> ()); 829 - let path = 830 - Eio.Path.(tmp_dir / Fmt.str "overflow_%d.db" (Random.int 1_000_000)) 831 - in 811 + let fs = Eio.Stdenv.fs env in 812 + let path = Eio.Path.(fs / temp_db "overflow") in 832 813 let large = String.make 10000 'Y' in 833 814 (* Write *) 834 815 Eio.Switch.run (fun sw -> ··· 953 934 954 935 let test_insert_persistence () = 955 936 Eio_main.run @@ fun env -> 956 - let cwd = Eio.Stdenv.cwd env in 957 - let tmp_dir = Eio.Path.(cwd / "_build" / "test_sqlite") in 958 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 tmp_dir with Eio.Io _ -> ()); 959 - let path = 960 - Eio.Path.(tmp_dir / Fmt.str "insert_%d.db" (Random.int 1_000_000)) 961 - in 937 + let fs = Eio.Stdenv.fs env in 938 + let path = Eio.Path.(fs / temp_db "insert") in 962 939 (* Write *) 963 940 Eio.Switch.run (fun sw -> 964 941 let db = Sqlite.open_ ~sw ~create:true path in ··· 1129 1106 1130 1107 let test_unique_persists () = 1131 1108 Eio_main.run @@ fun env -> 1132 - let cwd = Eio.Stdenv.cwd env in 1133 - let tmp_dir = Eio.Path.(cwd / "_build" / "test_sqlite") in 1134 - (try Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 tmp_dir with Eio.Io _ -> ()); 1135 - let path = 1136 - Eio.Path.(tmp_dir / Fmt.str "unique_%d.db" (Random.int 1_000_000)) 1137 - in 1109 + let fs = Eio.Stdenv.fs env in 1110 + let path = Eio.Path.(fs / temp_db "unique") in 1138 1111 (* Create and insert *) 1139 1112 Eio.Switch.run (fun sw -> 1140 1113 let db = Sqlite.open_ ~sw ~create:true path in