def initialize_tables(self, tablenames, formulas, target=None): """Event handler for (re)initializing a collection of tables.""" # translate FORMULAS into list of formula objects actual_formulas = [] formula_tables = set() if isinstance(formulas, basestring): formulas = self.parse(formulas) for formula in formulas: if isinstance(formula, basestring): formula = self.parse1(formula) elif isinstance(formula, tuple): formula = compile.Literal.create_from_iter(formula) assert formula.is_atom() actual_formulas.append(formula) formula_tables.add(formula.table) tablenames = set(tablenames) | formula_tables self.table_log(None, "Initializing tables %s with %s", iterstr(tablenames), iterstr(actual_formulas)) # implement initialization by computing the requisite # update. theory = self.get_target(target) old = set(theory.content(tablenames=tablenames)) new = set(actual_formulas) to_add = new - old to_rem = old - new to_add = [Event(formula_, insert=True) for formula_ in to_add] to_rem = [Event(formula_, insert=False) for formula_ in to_rem] self.table_log(None, "Initialize converted to update with %s and %s", iterstr(to_add), iterstr(to_rem)) return self.update(to_add + to_rem, target=target)
def process_new_bindings(self, bindings, atom, insert, original_rule): """Process new bindings. For each of BINDINGS, apply to ATOM, and enqueue it as an insert if INSERT is True and as a delete otherwise. """ # for each binding, compute generated tuple and group bindings # by the tuple they generated new_atoms = {} for binding in bindings: new_atom = atom.plug(binding) if new_atom not in new_atoms: new_atoms[new_atom] = [] new_atoms[new_atom].append(Database.Proof(binding, original_rule)) self.log(atom.table, "new tuples generated: %s", iterstr(new_atoms)) # enqueue each distinct generated tuple, recording appropriate bindings for new_atom in new_atoms: # self.log(event.table, "new_tuple %s: %s", new_tuple, # new_tuples[new_tuple]) # Only enqueue if new data. # Putting the check here is necessary to support recursion. self.enqueue( Event(formula=new_atom, proofs=new_atoms[new_atom], insert=insert))
def enqueue_any(self, event): """Enqueue event. Processing rules is a bit different than processing atoms in that they generate additional events that we want to process either before the rule is deleted or after it is inserted. PROCESS_QUEUE is similar but assumes that only the data will cause propagations (and ignores included theories). """ # Note: all included theories must define MODIFY formula = event.formula if formula.is_atom(): self.log(formula.tablename(), "compute/enq: atom %s", formula) assert not self.is_view( formula.table), ("Cannot directly modify tables" + " computed from other tables") # self.log(formula.table, "%s: %s", text, formula) self.enqueue(event) return [] else: # rules do not need to talk to included theories because they # only generate events for views # need to eliminate self-joins here so that we fill all # the tables introduced by self-join elimination. for rule in DeltaRuleTheory.eliminate_self_joins([formula]): new_event = Event(formula=rule, insert=event.insert, target=event.target) self.enqueue(new_event) return []
def load_file(self, filename, target=None): """Load content from file. Compile the given FILENAME and insert each of the statements into the runtime. Assumes that FILENAME includes no modals. """ formulas = compile.parse_file( filename, theories=self.theory) try: self.policy_object(target) except KeyError: self.create_policy(target) return self.update( [Event(formula=x, insert=True) for x in formulas], target)
def project_updates(self, delta, theory): """Project atom/delta rule insertion/deletion. Takes an atom/rule DELTA with update head table (i.e. ending in + or -) and inserts/deletes, respectively, that atom/rule into THEORY after stripping the +/-. Returns None if DELTA had no effect on the current state. """ theory = delta.theory_name() or theory self.table_log(None, "Applying update %s to %s", delta, theory) th_obj = self.theory[theory] insert = delta.tablename().endswith('+') newdelta = delta.drop_update().drop_theory() changed = th_obj.update([Event(formula=newdelta, insert=insert)]) if changed: return delta.invert_update() else: return None
def delete(self, formula): return self.update([Event(formula=formula, insert=False)])
def insert(self, formula): return self.update([Event(formula=formula, insert=True)])
def delete(self, rule): changes = self.update([Event(formula=rule, insert=False)]) return [event.formula for event in changes]
def insert(self, rule): changes = self.update([Event(formula=rule, insert=True)]) return [event.formula for event in changes]
def define(self, rules): """Empties and then inserts RULES.""" self.empty() return self.update( [Event(formula=rule, insert=True) for rule in rules])
def delete(self, atom, proofs=None): """Deletes ATOM from the DB. Returns changes.""" return self.modify(Event(formula=atom, insert=False, proofs=proofs))
def insert(self, atom, proofs=None): """Inserts ATOM into the DB. Returns changes.""" return self.modify(Event(formula=atom, insert=True, proofs=proofs))
def delete_obj(self, formula, theory_string): return self.update_obj([Event(formula=formula, insert=False, target=theory_string)])
def delete_string(self, policy_string, theory_string): policy = self.parse(policy_string) return self.update_obj( [Event(formula=x, insert=False, target=theory_string) for x in policy])
def insert_obj(self, formula, theory_string): return self.update_obj([Event(formula=formula, insert=True, target=theory_string)])