Ejemplo n.º 1
0
def _rql_cache_key(cnx, rql, args, eidkeys):
    cachekey = [rql]
    type_from_eid = cnx.repo.type_from_eid
    for key in sorted(eidkeys):
        try:
            etype = type_from_eid(args[key], cnx)
        except KeyError:
            raise QueryError('bad cache key %s (no value)' % key)
        except TypeError:
            raise QueryError('bad cache key %s (value: %r)' % (key, args[key]))
        cachekey.append(etype)
        # ensure eid is correctly typed in args
        args[key] = int(args[key])
    return tuple(cachekey)
Ejemplo n.º 2
0
def _extract_const_attributes(plan, rqlst, to_build):
    """add constant values to entity def, mark variables to be selected
    """
    to_select = {}
    for relation in rqlst.main_relations:
        lhs, rhs = relation.get_variable_parts()
        rtype = relation.r_type
        if rtype in READ_ONLY_RTYPES:
            raise QueryError("can't assign to %s" % rtype)
        try:
            edef = to_build[str(lhs)]
        except KeyError:
            # lhs var is not to build, should be selected and added as an
            # object relation
            edef = to_build[str(rhs)]
            to_select.setdefault(edef, []).append((rtype, lhs, 1))
        else:
            if isinstance(rhs, Constant) and not rhs.uid:
                # add constant values to entity def
                value = rhs.eval(plan.args)
                eschema = edef.entity.e_schema
                attrtype = eschema.subjrels[rtype].objects(eschema)[0]
                if attrtype == 'Password' and isinstance(value, str):
                    value = value.encode('UTF8')
                edef.edited_attribute(rtype, value)
            elif str(rhs) in to_build:
                # create a relation between two newly created variables
                plan.add_relation_def((edef, rtype, to_build[rhs.name]))
            else:
                to_select.setdefault(edef, []).append((rtype, rhs, 0))
    return to_select
Ejemplo n.º 3
0
 def add_relation_def(self, rdef):
     """add an relation definition to build"""
     edef, rtype, value = rdef
     if self.schema[rtype].rule:
         raise QueryError("'%s' is a computed relation" % rtype)
     self.r_defs.add(rdef)
     if not isinstance(edef, int):
         self._r_subj_index.setdefault(edef, []).append(rdef)
     if not isinstance(value, int):
         self._r_obj_index.setdefault(value, []).append(rdef)
Ejemplo n.º 4
0
 def build_set_plan(self, plan, rqlst):
     """get an execution plan from an SET RQL query"""
     getrschema = self.schema.rschema
     select = Select()  # potential substep query
     selectedidx = {}  # local state
     updatedefs = []  # definition of update attributes/relations
     selidx = residx = 0  # substep selection / resulting rset indexes
     # search for eid const in the WHERE clause
     eidconsts = _extract_eid_consts(plan, rqlst)
     # build `updatedefs` describing things to update and add necessary
     # variables to the substep selection
     for i, relation in enumerate(rqlst.main_relations):
         if relation.r_type in VIRTUAL_RTYPES:
             raise QueryError('can not assign to %r relation' %
                              relation.r_type)
         lhs, rhs = relation.get_variable_parts()
         lhskey = lhs.as_string()
         if lhskey not in selectedidx:
             if lhs.variable in eidconsts:
                 eid = eidconsts[lhs.variable]
                 lhsinfo = (_CONSTANT, eid, residx)
             else:
                 select.append_selected(lhs.copy(select))
                 lhsinfo = (_FROM_SUBSTEP, selidx, residx)
                 selidx += 1
             residx += 1
             selectedidx[lhskey] = lhsinfo
         else:
             lhsinfo = selectedidx[lhskey][:-1] + (None, )
         rhskey = rhs.as_string()
         if rhskey not in selectedidx:
             if isinstance(rhs, Constant):
                 rhsinfo = (_CONSTANT, rhs.eval(plan.args), residx)
             elif getattr(rhs, 'variable', None) in eidconsts:
                 eid = eidconsts[rhs.variable]
                 rhsinfo = (_CONSTANT, eid, residx)
             else:
                 select.append_selected(rhs.copy(select))
                 rhsinfo = (_FROM_SUBSTEP, selidx, residx)
                 selidx += 1
             residx += 1
             selectedidx[rhskey] = rhsinfo
         else:
             rhsinfo = selectedidx[rhskey][:-1] + (None, )
         rschema = getrschema(relation.r_type)
         updatedefs.append((lhsinfo, rhsinfo, rschema))
     # the update step
     step = UpdateStep(plan, updatedefs)
     # when necessary add substep to fetch yet unknown values
     select = _build_substep_query(select, rqlst)
     if select is not None:
         # set distinct to avoid potential duplicate key error
         select.distinct = True
         step.children += self._select_plan(plan, select, rqlst.solutions)
     return (step, )
Ejemplo n.º 5
0
 def build_delete_plan(self, plan, rqlst):
     """get an execution plan from a DELETE RQL query"""
     # build a select query to fetch entities to delete
     steps = []
     for etype, var in rqlst.main_variables:
         step = DeleteEntitiesStep(plan)
         step.children += self._sel_variable_step(plan, rqlst, etype, var)
         steps.append(step)
     for relation in rqlst.main_relations:
         rtype = relation.r_type
         if self.schema[rtype].rule:
             raise QueryError("'%s' is a computed relation" % rtype)
         step = DeleteRelationsStep(plan, rtype)
         step.children += self._sel_relation_steps(plan, rqlst, relation)
         steps.append(step)
     return steps
Ejemplo n.º 6
0
 def execute(self):
     """execute this step"""
     cnx = self.plan.cnx
     repo = cnx.repo
     edefs = {}
     relations = {}
     # insert relations
     if self.children:
         result = self.execute_child()
     else:
         result = [[]]
     for i, row in enumerate(result):
         newrow = []
         for (lhsinfo, rhsinfo, rschema) in self.updatedefs:
             if rschema.rule:
                 raise QueryError("'%s' is a computed relation" %
                                  rschema.type)
             lhsval = _handle_relterm(lhsinfo, row, newrow)
             rhsval = _handle_relterm(rhsinfo, row, newrow)
             if rschema.final or rschema.inlined:
                 eid = int(lhsval)
                 try:
                     edited = edefs[eid]
                 except KeyError:
                     edef = cnx.entity_from_eid(eid)
                     edefs[eid] = edited = EditedEntity(edef)
                 edited.edited_attribute(str(rschema), rhsval)
             else:
                 str_rschema = str(rschema)
                 if str_rschema in relations:
                     relations[str_rschema].append((lhsval, rhsval))
                 else:
                     relations[str_rschema] = [(lhsval, rhsval)]
         result[i] = newrow
     # update entities
     repo.glob_add_relations(cnx, relations)
     for eid, edited in edefs.items():
         repo.glob_update_entity(cnx, edited)
     return result
Ejemplo n.º 7
0
 def commit(self):
     """commit the current session's transaction"""
     assert self.cnxset is not None
     cstate = self.commit_state
     if cstate == 'uncommitable':
         raise QueryError('transaction must be rolled back')
     if cstate == 'precommit':
         self.warn('calling commit in precommit makes no sense; ignoring commit')
         return
     if cstate == 'postcommit':
         self.critical('postcommit phase is not allowed to write to the db; ignoring commit')
         return
     assert cstate is None
     # on rollback, an operation should have the following state
     # information:
     # - processed by the precommit/commit event or not
     # - if processed, is it the failed operation
     debug = server.DEBUG & server.DBG_OPS
     try:
         # by default, operations are executed with security turned off
         with self.security_enabled(False, False):
             processed = []
             self.commit_state = 'precommit'
             if debug:
                 print(self.commit_state, '*' * 20)
             try:
                 with self.running_hooks_ops():
                     while self.pending_operations:
                         operation = self.pending_operations.pop(0)
                         operation.processed = 'precommit'
                         processed.append(operation)
                         if debug:
                             print(operation)
                         operation.handle_event('precommit_event')
                 self.pending_operations[:] = processed
                 self.debug('precommit transaction %s done', self)
             except BaseException:
                 # if error on [pre]commit:
                 #
                 # * set .failed = True on the operation causing the failure
                 # * call revert<event>_event on processed operations
                 # * call rollback_event on *all* operations
                 #
                 # that seems more natural than not calling rollback_event
                 # for processed operations, and allow generic rollback
                 # instead of having to implements rollback, revertprecommit
                 # and revertcommit, that will be enough in mont case.
                 operation.failed = True
                 if debug:
                     print(self.commit_state, '*' * 20)
                 with self.running_hooks_ops():
                     for operation in reversed(processed):
                         if debug:
                             print(operation)
                         try:
                             operation.handle_event('revertprecommit_event')
                         except BaseException:
                             self.critical('error while reverting precommit',
                                           exc_info=True)
                 # XXX use slice notation since self.pending_operations is a
                 # read-only property.
                 self.pending_operations[:] = processed + self.pending_operations
                 self.rollback()
                 raise
             self.cnxset.commit()
             self.commit_state = 'postcommit'
             if debug:
                 print(self.commit_state, '*' * 20)
             with self.running_hooks_ops():
                 while self.pending_operations:
                     operation = self.pending_operations.pop(0)
                     if debug:
                         print(operation)
                     operation.processed = 'postcommit'
                     try:
                         operation.handle_event('postcommit_event')
                     except BaseException:
                         if self.repo.config.mode == 'test':
                             raise
                         self.critical('error while postcommit',
                                       exc_info=sys.exc_info())
             self.debug('postcommit transaction %s done', self)
             return self.transaction_uuid(set=False)
     finally:
         self.clear()