• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

A Nix-friendly SQLite-enhanced fork of Flitter, a speedrunning split timer for Unix-style terminals


Commit MetaInfo

Revision972e7876e9fba5fa38161ed5a793e045db96c91d (tree)
Time2023-05-23 03:12:25
AuthorCorbin <cds@corb...>
CommiterCorbin

Log Message

Implement the bulk of the migration.

I think I've got the data model nailed down. Aside from a couple
optional fields for datetimes, which we'll want in the future, this is
basically complete.

One of my savefiles doesn't import. It uses different split titles in
its attempts, due to rerouting. This is exactly the sort of thing I
wanted to preserve, so I'll fix it.

Change Summary

Incremental Difference

--- a/bin/migrate_main.ml
+++ b/bin/migrate_main.ml
@@ -8,7 +8,7 @@ let usage =
88 type split = { title : string; time : Duration.t }
99 [@@deriving of_sexp] [@@sexp.allow_extra_fields]
1010
11-type run = { attempt : int; splits : split array }
11+type run = { attempt : int; splits : split list }
1212 [@@deriving of_sexp] [@@sexp.allow_extra_fields]
1313
1414 type old_sexp = {
@@ -19,18 +19,16 @@ type old_sexp = {
1919 }
2020 [@@deriving of_sexp] [@@sexp.allow_extra_fields]
2121
22-let check_exec db stmt = Rc.check (exec db stmt)
23-
24-let check_bind stmt vals f =
22+let check_insert stmt vals =
2523 Rc.check (bind_values stmt vals);
26- Rc.check (iter stmt ~f)
24+ Rc.check (iter stmt ~f:(fun _ -> ()))
2725
2826 let upsert_game db sexp =
2927 print_string (sprintf "Title: %s Category: %s\n" sexp.title sexp.category);
3028 let stmt =
3129 prepare db "insert into \"games\" values (?) on conflict do nothing;"
3230 in
33- check_bind stmt [ Data.TEXT sexp.title ] (fun _ -> ())
31+ check_insert stmt [ Data.TEXT sexp.title ]
3432
3533 let upsert_checkpoints db splits =
3634 let stmt =
@@ -41,7 +39,7 @@ let upsert_checkpoints db splits =
4139 match l with
4240 | [] -> ()
4341 | next :: tail ->
44- check_bind stmt [ next; prev ] (fun _ -> ());
42+ check_insert stmt [ next; prev ];
4543 go tail next
4644 in
4745 go (List.map ~f:(fun s -> Data.TEXT s) splits) Data.NULL
@@ -55,31 +53,81 @@ let finish_line splits =
5553 let insert_route db sexp =
5654 upsert_game db sexp;
5755 upsert_checkpoints db sexp.split_names;
58- let stmt = prepare db "insert into \"routes\" values (?, ?, ?, ?);" in
56+ let stmt =
57+ prepare db
58+ "insert into \"routes\" values (?, ?, ?, ?) on conflict do nothing;"
59+ in
5960 let finish, penult = finish_line sexp.split_names in
60- Rc.check
61- (bind_values stmt
62- [
63- Data.TEXT sexp.title;
64- Data.TEXT sexp.category;
65- Data.TEXT finish;
66- Data.opt_text penult;
67- ]);
68- Rc.is_success (iter stmt (fun _ -> ()))
61+ check_insert stmt
62+ [
63+ Data.TEXT sexp.title;
64+ Data.TEXT sexp.category;
65+ Data.TEXT finish;
66+ Data.opt_text penult;
67+ ]
68+
69+let insert_segments db attempt game category splits =
70+ let stmt =
71+ prepare db
72+ "insert into \"segments\" values (?, ?, ?, ?, ?, ?) on conflict do \
73+ nothing;"
74+ in
75+ let rec go l (starting_at, t1) =
76+ match l with
77+ | [] -> ()
78+ | (ending_at, t2) :: tail ->
79+ check_insert stmt
80+ [
81+ Data.INT (Int64.of_int attempt);
82+ Data.TEXT game;
83+ Data.TEXT category;
84+ ending_at;
85+ starting_at;
86+ Data.INT (Int64.of_int (t2 - t1));
87+ ];
88+ go tail (ending_at, t2)
89+ in
90+ go
91+ (List.map ~f:(fun { title; time } -> (Data.TEXT title, time)) splits)
92+ (Data.NULL, 0)
93+
94+let insert_runs db sexp =
95+ let stmt =
96+ prepare db
97+ "insert into \"runs\" values (?, ?, ?, ?, ?, ?) on conflict do nothing;"
98+ in
99+ List.iter
100+ ~f:(fun { attempt; splits } ->
101+ let f, p = finish_line (List.map ~f:(fun { title } -> title) splits) in
102+ check_insert stmt
103+ [
104+ Data.INT (Int64.of_int attempt);
105+ Data.TEXT sexp.title;
106+ Data.TEXT sexp.category;
107+ Data.TEXT f;
108+ Data.opt_text p;
109+ Data.NULL;
110+ ];
111+ print_string
112+ (sprintf "Importing attempt %d (%s, %s)...\n" attempt
113+ (Option.value p ~default:"...")
114+ f);
115+ insert_segments db attempt sexp.title sexp.category splits)
116+ sexp.history
69117
70118 let () =
71- match Sys.get_argv () with
72- | [| _; path; db_path |] ->
119+ match Array.to_list (Sys.get_argv ()) with
120+ | _ :: db_path :: scms ->
73121 let db = db_open db_path in
74- print_string (sqlite_version_info ());
75- print_newline ();
76- check_exec db "pragma foreign_keys = on;";
77- let sexp = Sexp.load_sexp_conv_exn path old_sexp_of_sexp in
78- print_string
79- (if insert_route db sexp then "Successfully created route!\n"
80- else
81- "This route already appears to exist and I don't want to \
82- double-count any runs by mistake.\n");
122+ print_string (sqlite_version_info () ^ "\n");
123+ Rc.check (exec db "pragma foreign_keys = on;");
124+ List.iter
125+ ~f:(fun path ->
126+ let sexp = Sexp.load_sexp_conv_exn path old_sexp_of_sexp in
127+ insert_route db sexp;
128+ print_string "Successfully created route!\n";
129+ insert_runs db sexp)
130+ scms;
83131 let _ = db_close db in
84132 ()
85133 | _ -> print_string usage
--- a/init.sql
+++ b/init.sql
@@ -5,24 +5,28 @@ create table if not exists "games" (
55
66 create table if not exists "checkpoints" (
77 name text not null, previous text, primary key (name, previous));
8+create index if not exists "checkpoints_name" on "checkpoints" (name);
9+create index if not exists "checkpoints_previous" on "checkpoints" (previous);
810
911 create table if not exists "routes" (
1012 game text not null, category text not null,
1113 finish_line text not null, penultimate text,
12- primary key (game, category),
14+ primary key (game, category, finish_line, penultimate),
1315 foreign key (game) references "games" (title) on update cascade,
1416 foreign key (finish_line, penultimate) references "checkpoints" (name, previous));
1517
18+create table if not exists "runs" (
19+ attempt int not null,
20+ game text not null, category text not null,
21+ finish_line text not null, penultimate text,
22+ started_at datetime,
23+ primary key (attempt, game, category),
24+ foreign key (game, category, finish_line, penultimate) references "routes" (game, category, finish_line, penultimate) on update cascade);
25+
1626 create table if not exists "segments" (
17- created_at datetime not null,
27+ attempt int not null, game text not null, category text not null,
1828 ending_at text not null, starting_at text,
19- primary key (created_at),
29+ duration int not null,
30+ primary key (attempt, game, category, ending_at, starting_at),
31+ foreign key (attempt, game, category) references "runs" (attempt, game, category),
2032 foreign key (ending_at, starting_at) references "checkpoints" (name, previous));
21-
22-create table if not exists "runs" (
23- started_at datetime not null, finished_at datetime not null,
24- game text not null, category text not null,
25- primary key (started_at, finished_at),
26- foreign key (started_at) references "segments" (created_at),
27- foreign key (finished_at) references "segments" (created_at),
28- foreign key (game, category) references "routes" (game, category) on update cascade);