-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatascript_sqlite_codec.ml
More file actions
342 lines (297 loc) · 13.8 KB
/
Copy pathdatascript_sqlite_codec.ml
File metadata and controls
342 lines (297 loc) · 13.8 KB
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
module Ds = Datascript
module PSet = Persistent_sorted_set
module Transit = Transit_native.Transit.Json
open Ds
let schema_attr_default : Ds.schema_attr =
{
cardinality = One;
unique = None;
indexed = false;
is_component = false;
no_history = false;
doc = None;
value_type = None;
tuple_attrs = None;
tuple_types = None;
}
let string_of_transit_key = function
| Transit.Keyword value | Transit.String value -> Some value
| _ -> None
let keyword_of_transit = function
| Transit.Keyword value -> Some value
| _ -> None
let bool_of_transit = function Transit.Bool value -> Some value | _ -> None
let string_of_transit = function
| Transit.String value -> Some value
| _ -> None
let int_of_transit_value = function
| Transit.Int value -> Some value
| Transit.Int64 value ->
if
Int64.compare value (Int64.of_int min_int) >= 0
&& Int64.compare value (Int64.of_int max_int) <= 0
then Some (Int64.to_int value)
else None
| _ -> None
let lookup_transit_key key entries =
List.find_map
(fun (entry_key, value) ->
match string_of_transit_key entry_key with
| Some entry_key when String.equal entry_key key -> Some value
| _ -> None)
entries
let transit_of_cardinality = function
| One -> Transit.Keyword "db.cardinality/one"
| Many -> Transit.Keyword "db.cardinality/many"
let cardinality_of_transit = function
| Transit.Keyword "db.cardinality/many" -> Many
| Transit.Keyword "db.cardinality/one" -> One
| _ -> One
let transit_of_unique = function
| Value -> Transit.Keyword "db.unique/value"
| Identity -> Transit.Keyword "db.unique/identity"
let unique_of_transit = function
| Transit.Keyword "db.unique/value" -> Some Value
| Transit.Keyword "db.unique/identity" -> Some Identity
| _ -> None
let transit_of_value_type = function
| RefType -> Transit.Keyword "db.type/ref"
| StringType -> Transit.Keyword "db.type/string"
| KeywordType -> Transit.Keyword "db.type/keyword"
| NumberType -> Transit.Keyword "db.type/number"
| UuidType -> Transit.Keyword "db.type/uuid"
| InstantType -> Transit.Keyword "db.type/instant"
| TupleType -> Transit.Keyword "db.type/tuple"
let value_type_of_transit = function
| Transit.Keyword "db.type/ref" -> Some RefType
| Transit.Keyword "db.type/string" -> Some StringType
| Transit.Keyword "db.type/keyword" -> Some KeywordType
| Transit.Keyword "db.type/number" -> Some NumberType
| Transit.Keyword "db.type/uuid" -> Some UuidType
| Transit.Keyword "db.type/instant" -> Some InstantType
| Transit.Keyword "db.type/tuple" -> Some TupleType
| _ -> None
let transit_of_ref_type = function
| PSet.Strong -> Transit.Keyword "strong"
| PSet.Weak -> Transit.Keyword "weak"
let ref_type_of_transit = function
| Transit.Keyword "soft" -> PSet.Weak
| Transit.Keyword "weak" -> PSet.Weak
| Transit.Keyword "strong" | _ -> PSet.Strong
let address_to_transit address = Transit.String address
let address_of_transit label = function
| Transit.String address -> address
| Transit.Int address -> string_of_int address
| Transit.Int64 address -> Int64.to_string address
| _ -> invalid_arg (label ^ " must be a storage address")
let transit_of_tuple_attrs attrs =
Transit.Array (List.map (fun attr -> Transit.Keyword attr) attrs)
let transit_of_tuple_types types =
Transit.Array (List.map transit_of_value_type types)
let schema_attr_to_transit attr =
let entries = ref [] in
let add key value = entries := (Transit.Keyword key, value) :: !entries in
(match attr.cardinality with
| One -> ()
| Many -> add "db/cardinality" (transit_of_cardinality attr.cardinality));
Option.iter (fun unique -> add "db/unique" (transit_of_unique unique)) attr.unique;
if attr.indexed then add "db/index" (Transit.Bool true);
if attr.is_component then add "db/isComponent" (Transit.Bool true);
if attr.no_history then add "db/noHistory" (Transit.Bool true);
Option.iter (fun doc -> add "db/doc" (Transit.String doc)) attr.doc;
Option.iter
(fun value_type -> add "db/valueType" (transit_of_value_type value_type))
attr.value_type;
Option.iter (fun attrs -> add "db/tupleAttrs" (transit_of_tuple_attrs attrs)) attr.tuple_attrs;
Option.iter (fun types -> add "db/tupleTypes" (transit_of_tuple_types types)) attr.tuple_types;
Transit.Map (List.rev !entries)
let schema_to_transit schema =
Transit.Map
(List.map
(fun (attr, schema_attr) -> (Transit.Keyword attr, schema_attr_to_transit schema_attr))
schema)
let tuple_attrs_of_transit = function
| Transit.Array values | Transit.List values -> Some (List.filter_map keyword_of_transit values)
| _ -> None
let tuple_types_of_transit = function
| Transit.Array values | Transit.List values ->
let types = List.filter_map value_type_of_transit values in
if List.length types = List.length values then Some types else None
| _ -> None
let schema_attr_of_transit = function
| Transit.Map props ->
List.fold_left
(fun schema (key, value) ->
match keyword_of_transit key with
| Some "db/cardinality" -> { schema with cardinality = cardinality_of_transit value }
| Some "db/unique" -> { schema with unique = unique_of_transit value }
| Some "db/index" ->
{ schema with indexed = Option.value (bool_of_transit value) ~default:false }
| Some "db/isComponent" ->
{ schema with is_component = Option.value (bool_of_transit value) ~default:false }
| Some "db/noHistory" ->
{ schema with no_history = Option.value (bool_of_transit value) ~default:false }
| Some "db/doc" -> { schema with doc = string_of_transit value }
| Some "db/valueType" -> { schema with value_type = value_type_of_transit value }
| Some "db/tupleAttrs" -> { schema with tuple_attrs = tuple_attrs_of_transit value }
| Some "db/tupleTypes" -> { schema with tuple_types = tuple_types_of_transit value }
| Some _ | None -> schema)
schema_attr_default props
| _ -> schema_attr_default
let schema_of_transit = function
| Transit.Map entries ->
List.filter_map
(fun (attr, schema_attr) ->
match keyword_of_transit attr with
| Some attr -> Some (attr, schema_attr_of_transit schema_attr)
| None -> None)
entries
| _ -> []
let rec value_to_transit = function
| Ds.Nil -> Transit.Null
| Int value -> Transit.Int value
| Float value -> Transit.Float value
| String value -> Transit.String value
| Symbol value -> Transit.Symbol value
| Bool value -> Transit.Bool value
| Keyword value -> Transit.Keyword value
| Uuid value -> Transit.Tagged ("u", Transit.String value)
| Instant value -> Transit.Tagged ("m", Transit.Int value)
| Regex value -> Transit.Tagged ("regex", Transit.String value)
| Ref entity_id -> Transit.Int entity_id
| List values -> Transit.List (List.map value_to_transit values)
| Vector values -> Transit.Array (List.map value_to_transit values)
| Map entries ->
Transit.Map (List.map (fun (key, value) -> (value_to_transit key, value_to_transit value)) entries)
| Set values -> Transit.Set (List.map value_to_transit values)
| Tuple values ->
Transit.Array
(List.map
(function
| None -> Transit.Null
| Some value -> value_to_transit value)
values)
| TxRef -> Transit.Keyword "db/current-tx"
| Ref_to _ -> invalid_arg "storage payload cannot contain unresolved refs"
let rec value_of_transit = function
| Transit.Null -> Ds.Nil
| Bool value -> Bool value
| String value -> String value
| Int value -> Int value
| Int64 value ->
if
Int64.compare value (Int64.of_int min_int) >= 0
&& Int64.compare value (Int64.of_int max_int) <= 0
then Int (Int64.to_int value)
else Instant (Int64.to_int value)
| Float value -> Float value
| Binary value -> String value
| Big_decimal value -> Float (float_of_string value)
| Big_int value -> Transit.Int64 (Int64.of_string value) |> value_of_transit
| Date value -> Instant (Int64.to_int value)
| Uuid value -> Uuid value
| Uri value -> String value
| Keyword value -> Keyword value
| Symbol value -> Symbol value
| Array values -> Vector (List.map value_of_transit values)
| Map entries -> Map (List.map (fun (key, value) -> (value_of_transit key, value_of_transit value)) entries)
| Set values -> Set (List.map value_of_transit values)
| List values -> List (List.map value_of_transit values)
| Tagged ("u", Transit.String value) -> Uuid value
| Tagged ("m", Transit.Int value) -> Instant value
| Tagged ("m", Transit.Int64 value) -> Instant (Int64.to_int value)
| Tagged ("regex", Transit.String value) -> Regex value
| Tagged (tag, value) -> Vector [ String tag; value_of_transit value ]
let datom_to_transit datom =
let tx = if datom.Ds.added then datom.tx else -datom.tx in
Transit.Array [ Transit.Int datom.e; Transit.Keyword datom.a; value_to_transit datom.v; Transit.Int tx ]
let int_of_transit label value =
match int_of_transit_value value with
| Some value -> value
| None -> invalid_arg (label ^ " must be a Transit integer")
let datom_of_transit = function
| Transit.Array [ entity; attr; value; tx ] ->
let e = int_of_transit "datom entity" entity in
let a =
match keyword_of_transit attr with
| Some attr -> attr
| None -> invalid_arg "datom attr must be a Transit keyword"
in
let tx = int_of_transit "datom tx" tx in
{ Ds.e; a; v = value_of_transit value; tx = abs tx; added = tx >= 0 }
| _ -> invalid_arg "storage datom must be [e a v tx]"
let datoms_to_transit datoms = Transit.Array (List.map datom_to_transit datoms)
let datoms_of_transit = function
| Transit.Array datoms | Transit.List datoms -> List.map datom_of_transit datoms
| _ -> invalid_arg "storage datoms must be a Transit array"
let storage_root_to_transit root =
Transit.Map
[
(Transit.Keyword "schema", schema_to_transit root.storage_schema);
(Transit.Keyword "max-eid", Transit.Int root.storage_max_eid);
(Transit.Keyword "max-tx", Transit.Int root.storage_max_tx);
(Transit.Keyword "eavt", address_to_transit root.storage_eavt);
(Transit.Keyword "aevt", address_to_transit root.storage_aevt);
(Transit.Keyword "avet", address_to_transit root.storage_avet);
(Transit.Keyword "duplicate-datoms", datoms_to_transit root.storage_duplicate_datoms);
(Transit.Keyword "max-addr", Transit.Int root.storage_max_addr);
(Transit.Keyword "branching-factor", Transit.Int root.storage_branching_factor);
(Transit.Keyword "ref-type", transit_of_ref_type root.storage_ref_type);
]
let storage_node_to_transit = function
| PSet.Leaf datoms -> Transit.Map [ (Transit.Keyword "keys", datoms_to_transit datoms) ]
| PSet.Branch (keys, child_addresses) ->
Transit.Map
[
(Transit.Keyword "keys", datoms_to_transit keys);
(Transit.Keyword "children", Transit.Array (List.map address_to_transit child_addresses));
]
let storage_tail_to_transit groups =
Transit.Array (List.map (fun group -> datoms_to_transit group) groups)
let payload_to_transit = function
| Ds.Storage_root root -> storage_root_to_transit root
| Storage_node node -> storage_node_to_transit node
| Storage_tail groups -> storage_tail_to_transit groups
let require_key key entries =
match lookup_transit_key key entries with
| Some value -> value
| None -> invalid_arg ("storage payload is missing :" ^ key)
let optional_datoms key entries =
match lookup_transit_key key entries with
| None -> []
| Some value -> datoms_of_transit value
let storage_root_of_transit entries =
{
Ds.storage_schema = schema_of_transit (require_key "schema" entries);
storage_max_eid = int_of_transit "storage root :max-eid" (require_key "max-eid" entries);
storage_max_tx = int_of_transit "storage root :max-tx" (require_key "max-tx" entries);
storage_eavt = address_of_transit "storage root :eavt" (require_key "eavt" entries);
storage_aevt = address_of_transit "storage root :aevt" (require_key "aevt" entries);
storage_avet = address_of_transit "storage root :avet" (require_key "avet" entries);
storage_duplicate_datoms = optional_datoms "duplicate-datoms" entries;
storage_max_addr = int_of_transit "storage root :max-addr" (require_key "max-addr" entries);
storage_branching_factor =
int_of_transit "storage root :branching-factor" (require_key "branching-factor" entries);
storage_ref_type = ref_type_of_transit (require_key "ref-type" entries);
}
let child_addresses_of_transit = function
| Transit.Array values | Transit.List values ->
List.map (address_of_transit "storage node :children") values
| _ -> invalid_arg "storage node :children must be a Transit array"
let storage_node_of_transit entries =
let keys = datoms_of_transit (require_key "keys" entries) in
match lookup_transit_key "children" entries with
| None -> PSet.Leaf keys
| Some children -> PSet.Branch (keys, child_addresses_of_transit children)
let storage_tail_of_transit = function
| Transit.Array groups | Transit.List groups -> List.map datoms_of_transit groups
| _ -> invalid_arg "storage tail must be a Transit array"
let payload_of_transit = function
| Transit.Map entries ->
if Option.is_some (lookup_transit_key "schema" entries) then Storage_root (storage_root_of_transit entries)
else if Option.is_some (lookup_transit_key "keys" entries) then Storage_node (storage_node_of_transit entries)
else invalid_arg "unknown storage payload map"
| (Transit.Array _ | Transit.List _) as tail -> Storage_tail (storage_tail_of_transit tail)
| _ -> invalid_arg "unknown storage payload"
let encode payload = payload |> payload_to_transit |> Transit.to_string ~mode:Transit.Verbose
let decode content = content |> Transit.of_string |> payload_of_transit