server/ssplanner.py
branchtls-sprint
changeset 1802 d628defebc17
parent 1016 26387b836099
child 1977 606923dff11b
equal deleted inserted replaced
1801:672acc730ce5 1802:d628defebc17
    76                 # tree is not annotated yet, no scope set so add the restriction
    76                 # tree is not annotated yet, no scope set so add the restriction
    77                 # to the root
    77                 # to the root
    78                 rel = newroot.add_type_restriction(var, possibletypes)
    78                 rel = newroot.add_type_restriction(var, possibletypes)
    79             stinfo['typerels'] = frozenset((rel,))
    79             stinfo['typerels'] = frozenset((rel,))
    80             stinfo['possibletypes'] = possibletypes
    80             stinfo['possibletypes'] = possibletypes
    81         
    81 
    82 class SSPlanner(object):
    82 class SSPlanner(object):
    83     """SingleSourcePlanner: build execution plan for rql queries
    83     """SingleSourcePlanner: build execution plan for rql queries
    84 
    84 
    85     optimized for single source repositories
    85     optimized for single source repositories
    86     """
    86     """
    87     
    87 
    88     def __init__(self, schema, rqlhelper):
    88     def __init__(self, schema, rqlhelper):
    89         self.schema = schema
    89         self.schema = schema
    90         self.rqlhelper = rqlhelper
    90         self.rqlhelper = rqlhelper
    91 
    91 
    92     def build_plan(self, plan):
    92     def build_plan(self, plan):
    93         """build an execution plan from a RQL query
    93         """build an execution plan from a RQL query
    94         
    94 
    95         do nothing here, dispatch according to the statement type
    95         do nothing here, dispatch according to the statement type
    96         """
    96         """
    97         build_plan = getattr(self, 'build_%s_plan' % plan.rqlst.TYPE)
    97         build_plan = getattr(self, 'build_%s_plan' % plan.rqlst.TYPE)
    98         for step in build_plan(plan, plan.rqlst):
    98         for step in build_plan(plan, plan.rqlst):
    99             plan.add_step(step)
    99             plan.add_step(step)
   100     
   100 
   101     def build_select_plan(self, plan, rqlst):
   101     def build_select_plan(self, plan, rqlst):
   102         """build execution plan for a SELECT RQL query. Suppose only one source
   102         """build execution plan for a SELECT RQL query. Suppose only one source
   103         is available and so avoid work need for query decomposition among sources
   103         is available and so avoid work need for query decomposition among sources
   104                
   104 
   105         the rqlst should not be tagged at this point.
   105         the rqlst should not be tagged at this point.
   106         """
   106         """
   107         plan.preprocess(rqlst)
   107         plan.preprocess(rqlst)
   108         return (OneFetchStep(plan, rqlst, plan.session.repo.sources),)
   108         return (OneFetchStep(plan, rqlst, plan.session.repo.sources),)
   109             
   109 
   110     def build_insert_plan(self, plan, rqlst):
   110     def build_insert_plan(self, plan, rqlst):
   111         """get an execution plan from an INSERT RQL query"""
   111         """get an execution plan from an INSERT RQL query"""
   112         # each variable in main variables is a new entity to insert
   112         # each variable in main variables is a new entity to insert
   113         to_build = {}
   113         to_build = {}
   114         session = plan.session
   114         session = plan.session
   121         # add necessary steps to add relations and update attributes
   121         # add necessary steps to add relations and update attributes
   122         step = InsertStep(plan) # insert each entity and its relations
   122         step = InsertStep(plan) # insert each entity and its relations
   123         step.children += self._compute_relation_steps(plan, rqlst.solutions,
   123         step.children += self._compute_relation_steps(plan, rqlst.solutions,
   124                                                       rqlst.where, to_select)
   124                                                       rqlst.where, to_select)
   125         return (step,)
   125         return (step,)
   126         
   126 
   127     def _compute_relation_steps(self, plan, solutions, restriction, to_select):
   127     def _compute_relation_steps(self, plan, solutions, restriction, to_select):
   128         """handle the selection of relations for an insert query"""
   128         """handle the selection of relations for an insert query"""
   129         for edef, rdefs in to_select.items():
   129         for edef, rdefs in to_select.items():
   130             # create a select rql st to fetch needed data
   130             # create a select rql st to fetch needed data
   131             select = Select()
   131             select = Select()
   144             if restriction is not None:
   144             if restriction is not None:
   145                 select.set_where(restriction.copy(select))
   145                 select.set_where(restriction.copy(select))
   146             step = RelationsStep(plan, edef, rdefs)
   146             step = RelationsStep(plan, edef, rdefs)
   147             step.children += self._select_plan(plan, select, solutions)
   147             step.children += self._select_plan(plan, select, solutions)
   148             yield step
   148             yield step
   149     
   149 
   150     def build_delete_plan(self, plan, rqlst):
   150     def build_delete_plan(self, plan, rqlst):
   151         """get an execution plan from a DELETE RQL query"""
   151         """get an execution plan from a DELETE RQL query"""
   152         # build a select query to fetch entities to delete
   152         # build a select query to fetch entities to delete
   153         steps = []
   153         steps = []
   154         for etype, var in rqlst.main_variables:
   154         for etype, var in rqlst.main_variables:
   172         if restriction is not None:
   172         if restriction is not None:
   173             select.set_where(restriction.copy(select))
   173             select.set_where(restriction.copy(select))
   174         if etype != 'Any':
   174         if etype != 'Any':
   175             select.add_type_restriction(varref.variable, etype)
   175             select.add_type_restriction(varref.variable, etype)
   176         return self._select_plan(plan, select, solutions)
   176         return self._select_plan(plan, select, solutions)
   177         
   177 
   178     def _sel_relation_steps(self, plan, solutions, restriction, relation):
   178     def _sel_relation_steps(self, plan, solutions, restriction, relation):
   179         """handle the selection of relations for a delete query"""
   179         """handle the selection of relations for a delete query"""
   180         select = Select()
   180         select = Select()
   181         lhs, rhs = relation.get_variable_parts()
   181         lhs, rhs = relation.get_variable_parts()
   182         select.append_selected(lhs.copy(select))
   182         select.append_selected(lhs.copy(select))
   183         select.append_selected(rhs.copy(select))
   183         select.append_selected(rhs.copy(select))
   184         select.set_where(relation.copy(select))
   184         select.set_where(relation.copy(select))
   185         if restriction is not None:
   185         if restriction is not None:
   186             select.add_restriction(restriction.copy(select))
   186             select.add_restriction(restriction.copy(select))
   187         return self._select_plan(plan, select, solutions)
   187         return self._select_plan(plan, select, solutions)
   188     
   188 
   189     def build_set_plan(self, plan, rqlst):
   189     def build_set_plan(self, plan, rqlst):
   190         """get an execution plan from an SET RQL query"""
   190         """get an execution plan from an SET RQL query"""
   191         select = Select()
   191         select = Select()
   192         # extract variables to add to the selection
   192         # extract variables to add to the selection
   193         selected_index = {}
   193         selected_index = {}
   220         step = UpdateStep(plan, attrrelations, relations, selected_index)
   220         step = UpdateStep(plan, attrrelations, relations, selected_index)
   221         step.children += self._select_plan(plan, select, rqlst.solutions)
   221         step.children += self._select_plan(plan, select, rqlst.solutions)
   222         return (step,)
   222         return (step,)
   223 
   223 
   224     # internal methods ########################################################
   224     # internal methods ########################################################
   225     
   225 
   226     def _select_plan(self, plan, select, solutions):
   226     def _select_plan(self, plan, select, solutions):
   227         union = Union()
   227         union = Union()
   228         union.append(select)
   228         union.append(select)
   229         select.clean_solutions(solutions)
   229         select.clean_solutions(solutions)
   230         add_types_restriction(self.schema, select)        
   230         add_types_restriction(self.schema, select)
   231         self.rqlhelper.annotate(union)
   231         self.rqlhelper.annotate(union)
   232         return self.build_select_plan(plan, union)
   232         return self.build_select_plan(plan, union)
   233 
   233 
   234 
   234 
   235 # execution steps and helper functions ########################################
   235 # execution steps and helper functions ########################################
   258     limit = offset = None
   258     limit = offset = None
   259     def set_limit_offset(self, limit, offset):
   259     def set_limit_offset(self, limit, offset):
   260         self.limit = limit
   260         self.limit = limit
   261         self.offset = offset or None
   261         self.offset = offset or None
   262 
   262 
   263         
   263 
   264 class Step(object):
   264 class Step(object):
   265     """base abstract class for execution step"""
   265     """base abstract class for execution step"""
   266     def __init__(self, plan):
   266     def __init__(self, plan):
   267         self.plan = plan
   267         self.plan = plan
   268         self.children = []
   268         self.children = []
   269         
   269 
   270     def execute_child(self):
   270     def execute_child(self):
   271         assert len(self.children) == 1
   271         assert len(self.children) == 1
   272         return self.children[0].execute()
   272         return self.children[0].execute()
   273     
   273 
   274     def execute_children(self):
   274     def execute_children(self):
   275         for step in self.children:
   275         for step in self.children:
   276             step.execute()
   276             step.execute()
   277         
   277 
   278     def execute(self):
   278     def execute(self):
   279         """execute this step and store partial (eg this step) results"""
   279         """execute this step and store partial (eg this step) results"""
   280         raise NotImplementedError()
   280         raise NotImplementedError()
   281     
   281 
   282     def mytest_repr(self):
   282     def mytest_repr(self):
   283         """return a representation of this step suitable for test"""
   283         """return a representation of this step suitable for test"""
   284         return (self.__class__.__name__,)
   284         return (self.__class__.__name__,)
   285     
   285 
   286     def test_repr(self):
   286     def test_repr(self):
   287         """return a representation of this step suitable for test"""
   287         """return a representation of this step suitable for test"""
   288         return self.mytest_repr() + (
   288         return self.mytest_repr() + (
   289             [step.test_repr() for step in self.children],)
   289             [step.test_repr() for step in self.children],)
   290 
   290 
   291         
   291 
   292 class OneFetchStep(LimitOffsetMixIn, Step):
   292 class OneFetchStep(LimitOffsetMixIn, Step):
   293     """step consisting in fetching data from sources and directly returning
   293     """step consisting in fetching data from sources and directly returning
   294     results
   294     results
   295     """
   295     """
   296     def __init__(self, plan, union, sources, inputmap=None):
   296     def __init__(self, plan, union, sources, inputmap=None):
   303     def set_limit_offset(self, limit, offset):
   303     def set_limit_offset(self, limit, offset):
   304         LimitOffsetMixIn.set_limit_offset(self, limit, offset)
   304         LimitOffsetMixIn.set_limit_offset(self, limit, offset)
   305         for select in self.union.children:
   305         for select in self.union.children:
   306             select.limit = limit
   306             select.limit = limit
   307             select.offset = offset
   307             select.offset = offset
   308         
   308 
   309     def execute(self):
   309     def execute(self):
   310         """call .syntax_tree_search with the given syntax tree on each
   310         """call .syntax_tree_search with the given syntax tree on each
   311         source for each solution
   311         source for each solution
   312         """
   312         """
   313         self.execute_children()
   313         self.execute_children()
   373     """step consisting in adding attributes/relations to entity defs from a
   373     """step consisting in adding attributes/relations to entity defs from a
   374     previous FetchStep
   374     previous FetchStep
   375 
   375 
   376     relations values comes from the latest result, with one columns for
   376     relations values comes from the latest result, with one columns for
   377     each relation defined in self.r_defs
   377     each relation defined in self.r_defs
   378     
   378 
   379     for one entity definition, we'll construct N entity, where N is the
   379     for one entity definition, we'll construct N entity, where N is the
   380     number of the latest result
   380     number of the latest result
   381     """
   381     """
   382     
   382 
   383     FINAL = 0
   383     FINAL = 0
   384     RELATION = 1
   384     RELATION = 1
   385     REVERSE_RELATION = 2
   385     REVERSE_RELATION = 2
   386     
   386 
   387     def __init__(self, plan, e_def, r_defs):
   387     def __init__(self, plan, e_def, r_defs):
   388         Step.__init__(self, plan)
   388         Step.__init__(self, plan)
   389         # partial entity definition to expand
   389         # partial entity definition to expand
   390         self.e_def = e_def
   390         self.e_def = e_def
   391         # definition of relations to complete
   391         # definition of relations to complete
   392         self.r_defs = r_defs
   392         self.r_defs = r_defs
   393         
   393 
   394     def execute(self):
   394     def execute(self):
   395         """execute this step"""
   395         """execute this step"""
   396         base_e_def = self.e_def
   396         base_e_def = self.e_def
   397         result = []
   397         result = []
   398         for row in self.execute_child():
   398         for row in self.execute_child():
   413         self.plan.substitute_entity_def(base_e_def, result)
   413         self.plan.substitute_entity_def(base_e_def, result)
   414 
   414 
   415 
   415 
   416 class InsertStep(Step):
   416 class InsertStep(Step):
   417     """step consisting in inserting new entities / relations"""
   417     """step consisting in inserting new entities / relations"""
   418     
   418 
   419     def execute(self):
   419     def execute(self):
   420         """execute this step"""
   420         """execute this step"""
   421         for step in self.children:
   421         for step in self.children:
   422             assert isinstance(step, RelationsStep)
   422             assert isinstance(step, RelationsStep)
   423             step.plan = self.plan
   423             step.plan = self.plan
   442         pending = session.query_data('pendingeids', set(), setdefault=True)
   442         pending = session.query_data('pendingeids', set(), setdefault=True)
   443         actual = todelete - pending
   443         actual = todelete - pending
   444         pending |= actual
   444         pending |= actual
   445         for eid in actual:
   445         for eid in actual:
   446             delete(session, eid)
   446             delete(session, eid)
   447             
   447 
   448     
   448 
   449 class DeleteRelationsStep(Step):
   449 class DeleteRelationsStep(Step):
   450     """step consisting in deleting relations"""
   450     """step consisting in deleting relations"""
   451 
   451 
   452     def __init__(self, plan, rtype):
   452     def __init__(self, plan, rtype):
   453         Step.__init__(self, plan)
   453         Step.__init__(self, plan)
   454         self.rtype = rtype
   454         self.rtype = rtype
   455         
   455 
   456     def execute(self):
   456     def execute(self):
   457         """execute this step"""
   457         """execute this step"""
   458         session = self.plan.session
   458         session = self.plan.session
   459         delete = session.repo.glob_delete_relation
   459         delete = session.repo.glob_delete_relation
   460         for subj, obj in self.execute_child():
   460         for subj, obj in self.execute_child():
   461             delete(session, subj, self.rtype, obj)
   461             delete(session, subj, self.rtype, obj)
   462     
   462 
   463 
   463 
   464 class UpdateStep(Step):
   464 class UpdateStep(Step):
   465     """step consisting in updating entities / adding relations from relations
   465     """step consisting in updating entities / adding relations from relations
   466     definitions and from results fetched in previous step
   466     definitions and from results fetched in previous step
   467     """
   467     """
   468     
   468 
   469     def __init__(self, plan, attribute_relations, relations, selected_index):
   469     def __init__(self, plan, attribute_relations, relations, selected_index):
   470         Step.__init__(self, plan)
   470         Step.__init__(self, plan)
   471         self.attribute_relations = attribute_relations
   471         self.attribute_relations = attribute_relations
   472         self.relations = relations
   472         self.relations = relations
   473         self.selected_index = selected_index
   473         self.selected_index = selected_index
   474         
   474 
   475     def execute(self):
   475     def execute(self):
   476         """execute this step"""
   476         """execute this step"""
   477         plan = self.plan
   477         plan = self.plan
   478         session = self.plan.session
   478         session = self.plan.session
   479         repo = session.repo
   479         repo = session.repo