1 import Base 2 import ./execution_query_recipe.bend as Recipe 3 import ./execution_query_schema.bend as Schema 4 import ./execution_query_domain.bend as D 5 import ./execution_query_matches.bend as Match 6 import ./execution_query_identity.bend as Id 7 import ./execution_composer_layout.bend as L 8 import ./world.bend as W 9 import ./entity_key.bend as K 10 import ./indexed_lifecycle.bend as Indexed 11 import ./chunk_column.bend as Col 12 13 type Granted<-S:Data,-WC:Type> is Data: 14 Granted{entry:Recipe.Entry,handles:List<&2,W.Handle>} 15 type Lowering<-S:Data,-WC:Type,-full:L.Layout> is Data: 16 Lowered{grants:List<&2,Granted>,book:Schema.Book} 17 LoweringRefused{} 18 def info_matches(route:L.Route,info:Schema.Info) -> Bool: 19 match info: 20 case Schema.Info{at,_,_}:Schema.same_route(route,at) 21 def info_selected(info:Schema.Info,next:Maybe<&2,Schema.Info>,selected:Bool) -> Maybe<&2,Schema.Info>: 22 match selected: 23 case False{}:next 24 case True{}:Some{info} 25 def info_at(+route:L.Route,info:List<&2,Schema.Info>) -> Maybe<&2,Schema.Info>: 26 match info: 27 case []:None{} 28 case +head <> rest:info_selected(head,info_at(route,rest),info_matches(route,head)) 29 def present(info:Maybe<&2,Schema.Info>,offset:U32,wanted:Bool) -> Bool: 30 match info: 31 case None{}:False{} 32 case Some{Schema.Info{_,_,domain}}:Bool.not(Bool.xor(D.contains(domain,offset),wanted)) 33 def bound(+info:List<&2,Schema.Info>,selection:Recipe.Bound,+offset:U32) -> Bool: 34 match selection: 35 case Recipe.AllLive{}:True{} 36 case Recipe.Changed{_}:True{} 37 case Recipe.With{route}:present(info_at(route,info),offset,True{}) 38 case Recipe.Without{route}:present(info_at(route,info),offset,False{}) 39 case Recipe.And{left,right}:Bool.and(bound(info,left,offset),bound(info,right,offset)) 40 case Recipe.Or{left,right}:Bool.or(bound(info,left,offset),bound(info,right,offset)) 41 def selected_handle(~S:Data,handle:W.Handle,rest:List<&2,W.Handle>,selected:Bool) -> List<&2,W.Handle>: 42 match selected: 43 case False{}:rest 44 case True{}:handle <> rest 45 def candidate_offset(~S:Data,info:List<&2,Schema.Info>,selection:Recipe.Bound,handle:W.Handle,rest:List<&2,W.Handle>,offset:Maybe<&2,U32>) -> List<&2,W.Handle>: 46 match offset: 47 case None{}:rest 48 case Some{offset}:selected_handle(~S,handle,rest,bound(info,selection,offset)) 49 def candidates(~S:Data,+info:List<&2,Schema.Info>,+selection:Recipe.Bound,handles:List<&2,W.Handle>) -> List<&2,W.Handle>: 50 match handles: 51 case []:[] 52 case +handle <> rest:candidate_offset(~S,info,selection,handle,candidates(~S,info,selection,rest),Id.physical_index(W.handle_index(~S,handle))) 53 def capacity(info:Maybe<&2,Schema.Info>) -> U32: 54 match info: 55 case None{}:0 56 case Some{Schema.Info{_,capacity,_}}:capacity 57 def physical_cons(offset:U32,rest:List<&2,U32>,fits:Bool) -> List<&2,U32>: 58 match fits: 59 case False{}:rest 60 case True{}:offset <> rest 61 # Beyond-storage optional absence remains in Granted.handles. Only physical 62 # owners that actually exist contribute physical-domain validation metadata. 63 def physical_checked(capacity:U32,rest:List<&2,U32>,offset:Maybe<&2,U32>) -> List<&2,U32>: 64 match offset: 65 case None{}:rest 66 case Some{+offset}:physical_cons(offset,rest,U32.is_lt(offset,capacity)) 67 def physical(~S:Data,+capacity:U32,handles:List<&2,W.Handle>) -> List<&2,U32>: 68 match handles: 69 case []:[] 70 case +handle <> rest:physical_checked(capacity,physical(~S,capacity,rest),Id.physical_index(W.handle_index(~S,handle))) 71 def physical_recipe(kind:Recipe.QueryKind,domain:D.Domain) -> Match.Recipe: 72 match kind: 73 case Recipe.ReadRows{}:Match.Read{domain} 74 case Recipe.WriteRows{}:Match.Write{domain} 75 def handles_valid(~S:Data,+namespace:U32,handles:List<&2,W.Handle>) -> Bool: 76 match handles: 77 case []:True{} 78 case W.Handle{hn,key} <> rest:Bool.and(U32.is_eq(namespace,hn),Bool.and(Indexed.normal(K.index(key)),handles_valid(~S,namespace,rest))) 79 def public_indexes(~S:Data,handles:List<&2,W.Handle>) -> List<&2,U32>: 80 match handles: 81 case []:[] 82 case handle <> rest:W.handle_index(~S,handle) <> public_indexes(~S,rest) 83 def recipe_valid(~S:Data,~WC:Type,~full:L.Layout,recipe:Recipe.Recipe) -> Bool: 84 match recipe: 85 case Recipe.Recipe{Recipe.Entry{route,_,selection,_}}:Bool.and(L.valid(full,route),Recipe.bound_valid(~full,selection)) 86 def recipes_valid(~S:Data,~WC:Type,~full:L.Layout,recipes:List<&2,Recipe.Recipe>) -> Bool: 87 match recipes: 88 case []:True{} 89 case recipe <> rest:Bool.and(recipe_valid(~S,~WC,~full,recipe),recipes_valid(~S,~WC,~full,rest)) 90 type LoweredEntries<-S:Data,-WC:Type> is Data: 91 LoweredEntries{grants:List<&2,Granted>,entries:List<&2,Schema.Entry>} 92 def bound_entries(~S:Data,+info:List<&2,Schema.Info>,+handles:List<&2,W.Handle>,selection:Recipe.Bound) -> List<&2,Schema.Entry>: 93 match selection: 94 case Recipe.AllLive{}:[] 95 case Recipe.Changed{+route}:[Schema.Entry{route,Match.Read{D.Domain{physical(~S,capacity(info_at(route,info)),handles)}}}] 96 case Recipe.With{+route}:[Schema.Entry{route,Match.Read{D.Domain{physical(~S,capacity(info_at(route,info)),handles)}}}] 97 case Recipe.Without{+route}:[Schema.Entry{route,Match.Read{D.Domain{physical(~S,capacity(info_at(route,info)),handles)}}}] 98 case Recipe.And{left,right}:List.append(&2,Schema.Entry,bound_entries(~S,info,handles,left),bound_entries(~S,info,handles,right)) 99 case Recipe.Or{left,right}:List.append(&2,Schema.Entry,bound_entries(~S,info,handles,left),bound_entries(~S,info,handles,right)) 100 def lowered_cons(~S:Data,~WC:Type,+route:L.Route,+kind:Recipe.QueryKind,selection:Recipe.Bound,presence:Match.Presence,+handles:List<&2,W.Handle>,capacity:U32,boundReads:List<&2,Schema.Entry>,rest:LoweredEntries) -> LoweredEntries: 101 match rest: 102 case LoweredEntries{grants,entries}:LoweredEntries{Granted{Recipe.Entry{route,kind,selection,presence},handles} <> grants,Schema.Entry{route,physical_recipe(kind,D.Domain{physical(~S,capacity,handles)})} <> List.append(&2,Schema.Entry,boundReads,entries)} 103 def lower_entries(~S:Data,~WC:Type,recipes:List<&2,Recipe.Recipe>,+info:List<&2,Schema.Info>,+handles:List<&2,W.Handle>) -> LoweredEntries: 104 match recipes: 105 case []:LoweredEntries{[],[]} 106 case Recipe.Recipe{Recipe.Entry{+route,kind,+selection,presence}} <> rest:lowered_cons(~S,~WC,route,kind,selection,presence,candidates(~S,info,selection,handles),capacity(info_at(route,info)),bound_entries(~S,info,handles,selection),lower_entries(~S,~WC,rest,info,handles)) 107 def lowered(~S:Data,~WC:Type,~full:L.Layout,result:LoweredEntries) -> Lowering: 108 match result: 109 case LoweredEntries{grants,entries}:Lowered{grants,Schema.Book{entries}} 110 def lower_checked(~S:Data,~WC:Type,~full:L.Layout,recipes:List<&2,Recipe.Recipe>,info:List<&2,Schema.Info>,handles:List<&2,W.Handle>,valid:Bool) -> Lowering: 111 match valid: 112 case False{}:LoweringRefused{} 113 case True{}:lowered(~S,~WC,~full,lower_entries(~S,~WC,recipes,info,handles)) 114 def lower(~S:Data,~WC:Type,~full:L.Layout,~same:{WC == L.columns(~S,full) : Type},+recipes:List<&2,Recipe.Recipe>,+info:List<&2,Schema.Info>,namespace:U32,+handles:List<&2,W.Handle>) -> Lowering: 115 lower_checked(~S,~WC,~full,recipes,info,handles,Bool.and(handles_valid(~S,namespace,handles),Bool.and(Id.unique(~S,handles),Bool.and(Match.inventory_valid(D.validate(U32.add(Col.maximum_capacity(),1),D.Domain{public_indexes(~S,handles)})),recipes_valid(~S,~WC,~full,recipes))))) 116 117 def same_kind(left:Recipe.QueryKind,right:Recipe.QueryKind) -> Bool: 118 match left right: 119 case Recipe.ReadRows{} Recipe.ReadRows{}:True{} 120 case Recipe.WriteRows{} Recipe.WriteRows{}:True{} 121 case _ _:False{} 122 def same_presence(left:Match.Presence,right:Match.Presence) -> Bool: 123 match left right: 124 case Match.Optional{} Match.Optional{}:True{} 125 case Match.Required{} Match.Required{}:True{} 126 case Match.With{} Match.With{}:True{} 127 case Match.Without{} Match.Without{}:True{} 128 case _ _:False{} 129 def same_bound(left:Recipe.Bound,right:Recipe.Bound) -> Bool: 130 match left right: 131 case Recipe.AllLive{} Recipe.AllLive{}:True{} 132 case Recipe.Changed{l} Recipe.Changed{r}:Schema.same_route(l,r) 133 case Recipe.With{l} Recipe.With{r}:Schema.same_route(l,r) 134 case Recipe.Without{l} Recipe.Without{r}:Schema.same_route(l,r) 135 case Recipe.And{ll,lr} Recipe.And{rl,rr}:Bool.and(same_bound(ll,rl),same_bound(lr,rr)) 136 case Recipe.Or{ll,lr} Recipe.Or{rl,rr}:Bool.and(same_bound(ll,rl),same_bound(lr,rr)) 137 case _ _:False{} 138 def same_entry(left:Recipe.Entry,right:Recipe.Entry) -> Bool: 139 match left right: 140 case Recipe.Entry{lr,lk,lb,lp} Recipe.Entry{rr,rk,rb,rp}:Bool.and(Schema.same_route(lr,rr),Bool.and(same_kind(lk,rk),Bool.and(same_bound(lb,rb),same_presence(lp,rp)))) 141 def grant_selected(~S:Data,handles:List<&2,W.Handle>,next:Maybe<&2,List<&2,W.Handle>>,selected:Bool) -> Maybe<&2,List<&2,W.Handle>>: 142 match selected: 143 case False{}:next 144 case True{}:Some{handles} 145 def grant_handles(~S:Data,~WC:Type,+entry:Recipe.Entry,grants:List<&2,Granted>) -> Maybe<&2,List<&2,W.Handle>>: 146 match grants: 147 case []:None{} 148 case Granted{at,handles} <> rest:grant_selected(~S,handles,grant_handles(~S,~WC,entry,rest),same_entry(entry,at))