Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions impl/built_ins.ml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ let value_is_not_empty value =
| None -> false

let matches_value_predicate predicate value =
(* Ref values are entity ids — plain numbers upstream — so numeric
predicates and arithmetic see them as integers *)
match predicate, value with
| NumberValue, (Int _ | Float _) -> true
| IntegerValue, Int _ -> true
| NumberValue, (Int _ | Float _ | Ref _) -> true
| IntegerValue, (Int _ | Ref _) -> true
| StringValue, String _ -> true
| BooleanValue, Bool _ -> true
| KeywordValue, Keyword _ -> true
Expand All @@ -56,13 +58,18 @@ let matches_value_predicate predicate value =
let matches_numeric_predicate predicate value =
match predicate, value with
| ZeroNumber, Int value -> value = 0
| ZeroNumber, Ref value -> value = 0
| ZeroNumber, Float value -> value = 0.0
| PositiveNumber, Int value -> value > 0
| PositiveNumber, Ref value -> value > 0
| PositiveNumber, Float value -> value > 0.0
| NegativeNumber, Int value -> value < 0
| NegativeNumber, Ref value -> value < 0
| NegativeNumber, Float value -> value < 0.0
| EvenInteger, Int value -> value mod 2 = 0
| EvenInteger, Ref value -> value mod 2 = 0
| OddInteger, Int value -> value mod 2 <> 0
| OddInteger, Ref value -> value mod 2 <> 0
| (EvenInteger | OddInteger), Float _ -> false
| _, _ -> false

Expand Down Expand Up @@ -90,6 +97,7 @@ let all_values_equal = function

let numeric_value = function
| Int value -> Some (`Int value)
| Ref value -> Some (`Int value)
| Float value -> Some (`Float value)
| _ -> None

Expand Down
9 changes: 8 additions & 1 deletion impl/parser.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1230,7 +1230,14 @@ let parse_complement_predicate_clause context symbol args =
| _ -> invalid_arg (one_arg_message symbol)
in
let unary_value_predicate predicate =
unary_result_predicate (function Result_value value -> predicate value | _ -> false)
(* Bound entity ids arrive as Result_entity but are plain numbers
upstream: convert through value_of_query_result so numeric/value
predicates (even?, integer?, number?, ...) apply to them *)
unary_result_predicate
(fun result ->
match context.value_of_query_result result with
| Some value -> predicate value
| None -> false)
in
let binary_string_predicate predicate =
match args with
Expand Down
10 changes: 8 additions & 2 deletions impl/query_eval.ml
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,15 @@ let value_has_count = Built_ins.value_has_count
let value_is_not_empty = Built_ins.value_is_not_empty

let eval_value_predicate_clause context db bindings term predicate =
(* Bound entity ids arrive as Result_entity but are plain numbers
upstream: convert through value_of_query_result so value/numeric
predicates (number?, integer?, even?, ...) apply to them *)
match eval_query_term (context.match_context db) bindings term with
| Some (Result_value value) when predicate value -> [ bindings ]
| Some _ | None -> []
| Some result ->
(match value_of_query_result result with
| Some value when predicate value -> [ bindings ]
| Some _ | None -> [])
| None -> []

let matches_value_predicate = Built_ins.matches_value_predicate

Expand Down
66 changes: 54 additions & 12 deletions impl/transact.ml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ let remember_current_tx_alias tempids tx alias =
in
insert_after_current_tx_aliases [] tempids

(* Raised when a lookup ref cannot be resolved yet: the target may be defined
by a later op in the same transaction. apply_ops defers such ops and retries
them as the tx datoms accumulate; if they still cannot resolve at tx end the
original "Nothing found" error is raised (matching upstream's strict
sequential behavior for genuinely missing targets). *)
exception Unresolved_lookup_ref of attr * value

let rec resolve_entity_ref context db datoms tx max_eid tempids = function
| Entity_id e ->
let e = context.validate_entity_id e in
Expand All @@ -65,9 +72,9 @@ let rec resolve_entity_ref context db datoms tx max_eid tempids = function
e, context.max_eid_with_entity_id max_eid e, remember_tempid tempids tempid e)
| Lookup_ref (attr, value) ->
let value, max_eid, tempids = resolve_value context db datoms tx max_eid tempids value in
(match context.lookup_ref_entity_id ~strict_missing:true datoms attr value with
(match context.lookup_ref_entity_id ~strict_missing:false datoms attr value with
| Some e -> e, context.max_eid_with_entity_id max_eid e, tempids
| None -> invalid_arg (context.unresolved_lookup_ref_message attr value))
| None -> raise (Unresolved_lookup_ref (attr, value)))

and resolve_value context db datoms tx max_eid tempids = function
| TxRef -> Ref tx, max_eid, remember_current_tx tempids tx
Expand Down Expand Up @@ -420,6 +427,7 @@ let apply_tx context tx_ops db =
in
let initial_max_eid = List.fold_left max_explicit_tx_op db.max_eid tx_ops in
let max_tx_seen = ref tx in
let deferred_ops = ref [] in
let mark_entity_tempid entity_tempids = function
| Temp_id tempid -> tempid :: entity_tempids
| _ -> entity_tempids
Expand Down Expand Up @@ -746,7 +754,7 @@ let apply_tx context tx_ops db =
max_eid
tempids
tx_value)
with Invalid_argument _ -> None)
with Invalid_argument _ | Unresolved_lookup_ref _ -> None)
with
| Some (tx_value, max_eid, tempids) ->
(attr, tx_value) :: probe_attrs, max_eid, tempids
Expand Down Expand Up @@ -1160,7 +1168,7 @@ let apply_tx context tx_ops db =
in
(match context.existing_unique_entity db lookup_attr lookup_value with
| Some entity_id -> Ref entity_id, context.resolve_context.max_eid_with_entity_id max_eid entity_id
| None -> invalid_arg (context.resolve_context.unresolved_lookup_ref_message lookup_attr lookup_value))
| None -> raise (Unresolved_lookup_ref (lookup_attr, lookup_value)))
| _ ->
let value, max_eid, _ =
resolve_value_for_attr context.resolve_context db attr db tx max_eid [] value
Expand Down Expand Up @@ -1512,21 +1520,55 @@ let apply_tx context tx_ops db =
and apply_ops state tx_ops =
List.fold_left
(fun state tx_op ->
let state = apply_op state tx_op in
let datoms, _, _, _, tx_data = state in
if tx_op_affects_schema tx_op then refresh_schema datoms (List.rev tx_data);
state)
try
let state = apply_op state tx_op in
let datoms, _, _, _, tx_data = state in
if tx_op_affects_schema tx_op then refresh_schema datoms (List.rev tx_data);
state
with Unresolved_lookup_ref (attr, value) ->
(* The lookup ref target may be defined by a later op in this tx:
queue the op and retry it once more datoms accumulate. *)
deferred_ops := (tx_op, attr, value) :: !deferred_ops;
state)
state
tx_ops
in
let rec drain_deferred_ops state =
let pending = List.rev !deferred_ops in
match pending with
| [] -> state
| (_, first_attr, first_value) :: _ ->
deferred_ops := [];
let progressed = ref false in
let state =
List.fold_left
(fun state (tx_op, _, _) ->
try
let state = apply_op state tx_op in
let datoms, _, _, _, tx_data = state in
if tx_op_affects_schema tx_op then refresh_schema datoms (List.rev tx_data);
progressed := true;
state
with Unresolved_lookup_ref (attr', value') ->
deferred_ops := (tx_op, attr', value') :: !deferred_ops;
state)
state
pending
in
(* No progress means the remaining targets never resolve: raise the same
error upstream raises for a lookup ref that points nowhere. *)
if not !progressed then
invalid_arg (context.resolve_context.unresolved_lookup_ref_message first_attr first_value)
else
drain_deferred_ops state
in
let datoms, max_eid, tempids, entity_tempids, tx_data, fast_tx_data =
match try_apply_bulk_explicit_entities () with
match (try try_apply_bulk_explicit_entities () with Unresolved_lookup_ref _ -> None) with
| Some (datoms, max_eid, tempids, entity_tempids, tx_data) ->
datoms, max_eid, tempids, entity_tempids, tx_data, Some tx_data
| None ->
let datoms, max_eid, tempids, entity_tempids, tx_data =
apply_ops (db, initial_max_eid, [], [], []) tx_ops
in
let state = apply_ops (db, initial_max_eid, [], [], []) tx_ops in
let datoms, max_eid, tempids, entity_tempids, tx_data = drain_deferred_ops state in
datoms, max_eid, tempids, entity_tempids, tx_data, None
in
let tx_data =
Expand Down
Loading
Loading