server/querier.py
changeset 5174 78438ad513ca
parent 5082 d6fd82a5a4e8
child 5176 ddd5219d7eef
equal deleted inserted replaced
5173:73760bbb66bd 5174:78438ad513ca
   513         self.set_schema(schema)
   513         self.set_schema(schema)
   514 
   514 
   515     def set_schema(self, schema):
   515     def set_schema(self, schema):
   516         self.schema = schema
   516         self.schema = schema
   517         repo = self._repo
   517         repo = self._repo
       
   518         # rql st and solution cache
       
   519         self._rql_cache = Cache(repo.config['rql-cache-size'])
       
   520         # rql cache key cache
       
   521         self._rql_ck_cache = Cache(repo.config['rql-cache-size'])
       
   522         # some cache usage stats
       
   523         self.cache_hit, self.cache_miss = 0, 0
   518         # rql parsing / analysing helper
   524         # rql parsing / analysing helper
   519         self.solutions = repo.vreg.solutions
   525         self.solutions = repo.vreg.solutions
   520         self._rql_cache = Cache(repo.config['rql-cache-size'])
   526         rqlhelper = repo.vreg.rqlhelper
   521         self.cache_hit, self.cache_miss = 0, 0
   527         self._parse = rqlhelper.parse
       
   528         self._annotate = rqlhelper.annotate
   522         # rql planner
   529         # rql planner
   523         # note: don't use repo.sources, may not be built yet, and also "admin"
   530         # note: don't use repo.sources, may not be built yet, and also "admin"
   524         #       isn't an actual source
   531         #       isn't an actual source
   525         rqlhelper = repo.vreg.rqlhelper
       
   526         self._parse = rqlhelper.parse
       
   527         self._annotate = rqlhelper.annotate
       
   528         if len([uri for uri in repo.config.sources() if uri != 'admin']) < 2:
   532         if len([uri for uri in repo.config.sources() if uri != 'admin']) < 2:
   529             from cubicweb.server.ssplanner import SSPlanner
   533             from cubicweb.server.ssplanner import SSPlanner
   530             self._planner = SSPlanner(schema, rqlhelper)
   534             self._planner = SSPlanner(schema, rqlhelper)
   531         else:
   535         else:
   532             from cubicweb.server.msplanner import MSPlanner
   536             from cubicweb.server.msplanner import MSPlanner
   545         """create an execution plan for an INSERT RQL query"""
   549         """create an execution plan for an INSERT RQL query"""
   546         if rqlst.TYPE == 'insert':
   550         if rqlst.TYPE == 'insert':
   547             return InsertPlan(self, rqlst, args, session)
   551             return InsertPlan(self, rqlst, args, session)
   548         return ExecutionPlan(self, rqlst, args, session)
   552         return ExecutionPlan(self, rqlst, args, session)
   549 
   553 
   550     def execute(self, session, rql, args=None, eid_key=None, build_descr=True):
   554     def execute(self, session, rql, args=None, build_descr=True):
   551         """execute a rql query, return resulting rows and their description in
   555         """execute a rql query, return resulting rows and their description in
   552         a `ResultSet` object
   556         a `ResultSet` object
   553 
   557 
   554         * `rql` should be an Unicode string or a plain ASCII string
   558         * `rql` should be an Unicode string or a plain ASCII string
   555         * `args` the optional parameters dictionary associated to the query
   559         * `args` the optional parameters dictionary associated to the query
   556         * `build_descr` is a boolean flag indicating if the description should
   560         * `build_descr` is a boolean flag indicating if the description should
   557           be built on select queries (if false, the description will be en empty
   561           be built on select queries (if false, the description will be en empty
   558           list)
   562           list)
   559         * `eid_key` must be both a key in args and a substitution in the rql
       
   560           query. It should be used to enhance cacheability of rql queries.
       
   561           It may be a tuple for keys in args.
       
   562           `eid_key` must be provided in cases where a eid substitution is provided
       
   563           and resolves ambiguities in the possible solutions inferred for each
       
   564           variable in the query.
       
   565 
   563 
   566         on INSERT queries, there will be one row with the eid of each inserted
   564         on INSERT queries, there will be one row with the eid of each inserted
   567         entity
   565         entity
   568 
   566 
   569         result for DELETE and SET queries is undefined yet
   567         result for DELETE and SET queries is undefined yet
   575         if server.DEBUG & (server.DBG_RQL | server.DBG_SQL):
   573         if server.DEBUG & (server.DBG_RQL | server.DBG_SQL):
   576             if server.DEBUG & (server.DBG_MORE | server.DBG_SQL):
   574             if server.DEBUG & (server.DBG_MORE | server.DBG_SQL):
   577                 print '*'*80
   575                 print '*'*80
   578             print 'querier input', rql, args
   576             print 'querier input', rql, args
   579         # parse the query and binds variables
   577         # parse the query and binds variables
   580         if eid_key is not None:
   578         try:
   581             if not isinstance(eid_key, (tuple, list)):
       
   582                 eid_key = (eid_key,)
       
   583             cachekey = [rql]
       
   584             for key in eid_key:
       
   585                 try:
       
   586                     etype = self._repo.type_from_eid(args[key], session)
       
   587                 except KeyError:
       
   588                     raise QueryError('bad cache key %s (no value)' % key)
       
   589                 except TypeError:
       
   590                     raise QueryError('bad cache key %s (value: %r)' % (
       
   591                         key, args[key]))
       
   592                 except UnknownEid:
       
   593                     # we want queries such as "Any X WHERE X eid 9999"
       
   594                     # return an empty result instead of raising UnknownEid
       
   595                     return empty_rset(rql, args)
       
   596                 cachekey.append(etype)
       
   597                 # ensure eid is correctly typed in args
       
   598                 args[key] = typed_eid(args[key])
       
   599             cachekey = tuple(cachekey)
       
   600         else:
       
   601             cachekey = rql
   579             cachekey = rql
   602         try:
   580             if args:
       
   581                 eidkeys = self._rql_ck_cache[rql]
       
   582                 if eidkeys:
       
   583                     try:
       
   584                         cachekey = self._repo.querier_cache_key(session, rql,
       
   585                                                                 args, eidkeys)
       
   586                     except UnknownEid:
       
   587                         # we want queries such as "Any X WHERE X eid 9999"
       
   588                         # return an empty result instead of raising UnknownEid
       
   589                         return empty_rset(rql, args)
   603             rqlst = self._rql_cache[cachekey]
   590             rqlst = self._rql_cache[cachekey]
   604             self.cache_hit += 1
   591             self.cache_hit += 1
   605         except KeyError:
   592         except KeyError:
   606             self.cache_miss += 1
   593             self.cache_miss += 1
   607             rqlst = self.parse(rql)
   594             rqlst = self.parse(rql)
   608             try:
   595             try:
   609                 self.solutions(session, rqlst, args)
   596                 eidkeys = self.solutions(session, rqlst, args)
   610             except UnknownEid:
   597             except UnknownEid:
   611                 # we want queries such as "Any X WHERE X eid 9999" return an
   598                 # we want queries such as "Any X WHERE X eid 9999" return an
   612                 # empty result instead of raising UnknownEid
   599                 # empty result instead of raising UnknownEid
   613                 return empty_rset(rql, args, rqlst)
   600                 return empty_rset(rql, args, rqlst)
       
   601             self._rql_ck_cache[rql] = eidkeys
       
   602             if eidkeys:
       
   603                 cachekey = self._repo.querier_cache_key(session, rql, args,
       
   604                                                         eidkeys)
   614             self._rql_cache[cachekey] = rqlst
   605             self._rql_cache[cachekey] = rqlst
   615         orig_rqlst = rqlst
   606         orig_rqlst = rqlst
   616         if rqlst.TYPE != 'select':
   607         if rqlst.TYPE != 'select':
   617             if session.read_security:
   608             if session.read_security:
   618                 check_no_password_selected(rqlst)
   609                 check_no_password_selected(rqlst)
   668                 todetermine = zip(xrange(len(plan.selected)), repeat(False))
   659                 todetermine = zip(xrange(len(plan.selected)), repeat(False))
   669                 descr = session._build_descr(results, basedescr, todetermine)
   660                 descr = session._build_descr(results, basedescr, todetermine)
   670             # FIXME: get number of affected entities / relations on non
   661             # FIXME: get number of affected entities / relations on non
   671             # selection queries ?
   662             # selection queries ?
   672         # return a result set object
   663         # return a result set object
   673         return ResultSet(results, rql, args, descr, eid_key, orig_rqlst)
   664         return ResultSet(results, rql, args, descr, orig_rqlst)
   674 
   665 
   675 from logging import getLogger
   666 from logging import getLogger
   676 from cubicweb import set_log_methods
   667 from cubicweb import set_log_methods
   677 LOGGER = getLogger('cubicweb.querier')
   668 LOGGER = getLogger('cubicweb.querier')
   678 set_log_methods(QuerierHelper, LOGGER)
   669 set_log_methods(QuerierHelper, LOGGER)