[py3k] fix validateform controller
We need to return bytes not str.
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""plan execution of rql queries on a single source"""__docformat__="restructuredtext en"fromsiximporttext_typefromrql.stmtsimportUnion,Selectfromrql.nodesimportConstant,RelationfromcubicwebimportQueryErrorfromcubicweb.schemaimportVIRTUAL_RTYPESfromcubicweb.rqlrewriteimportadd_types_restrictionfromcubicweb.server.editionimportEditedEntityREAD_ONLY_RTYPES=set(('eid','has_text','is','is_instance_of','identity'))_CONSTANT=object()_FROM_SUBSTEP=object()def_extract_const_attributes(plan,rqlst,to_build):"""add constant values to entity def, mark variables to be selected """to_select={}forrelationinrqlst.main_relations:lhs,rhs=relation.get_variable_parts()rtype=relation.r_typeifrtypeinREAD_ONLY_RTYPES:raiseQueryError("can't assign to %s"%rtype)try:edef=to_build[str(lhs)]exceptKeyError:# lhs var is not to build, should be selected and added as an# object relationedef=to_build[str(rhs)]to_select.setdefault(edef,[]).append((rtype,lhs,1))else:ifisinstance(rhs,Constant)andnotrhs.uid:# add constant values to entity defvalue=rhs.eval(plan.args)eschema=edef.entity.e_schemaattrtype=eschema.subjrels[rtype].objects(eschema)[0]ifattrtype=='Password'andisinstance(value,text_type):value=value.encode('UTF8')edef.edited_attribute(rtype,value)elifstr(rhs)into_build:# create a relation between two newly created variablesplan.add_relation_def((edef,rtype,to_build[rhs.name]))else:to_select.setdefault(edef,[]).append((rtype,rhs,0))returnto_selectdef_extract_eid_consts(plan,rqlst):"""return a dict mapping rqlst variable object to their eid if specified in the syntax tree """cnx=plan.cnxifrqlst.whereisNone:return{}eidconsts={}neweids=cnx.transaction_data.get('neweids',())checkread=cnx.read_securityeschema=cnx.vreg.schema.eschemaforrelinrqlst.where.get_nodes(Relation):# only care for 'eid' relations ...if(rel.r_type=='eid'# ... that are not part of a NOT clause ...andnotrel.neged(strict=True)# ... and where eid is specified by '=' operator.andrel.children[1].operator=='='):lhs,rhs=rel.get_variable_parts()ifisinstance(rhs,Constant):eid=int(rhs.eval(plan.args))# check read permission here since it may not be done by# the generated select substep if not emited (eg nothing# to be selected)ifcheckreadandeidnotinneweids:withcnx.security_enabled(read=False):eschema(cnx.entity_metas(eid)['type']).check_perm(cnx,'read',eid=eid)eidconsts[lhs.variable]=eidreturneidconstsdef_build_substep_query(select,origrqlst):"""Finalize substep select query that should be executed to get proper selection of stuff to insert/update. Return None when no query actually needed, else the given select node that will be used as substep query. """iforigrqlst.whereisnotNoneandnotselect.selection:# no selection, append one randomly by searching for a relation which is# not neged neither a type restriction (is/is_instance_of)forrelinorigrqlst.where.iget_nodes(Relation):ifnot(rel.neged(traverse_scope=True)orrel.is_types_restriction()):select.append_selected(rel.children[0].copy(select))breakelse:returnNoneifselect.selection:iforigrqlst.whereisnotNone:select.set_where(origrqlst.where.copy(select))ifgetattr(origrqlst,'having',None):select.set_having([sq.copy(select)forsqinorigrqlst.having])returnselectreturnNoneclassSSPlanner(object):"""SingleSourcePlanner: build execution plan for rql queries optimized for single source repositories """def__init__(self,schema,rqlhelper):self.schema=schemaself.rqlhelper=rqlhelperdefbuild_plan(self,plan):"""build an execution plan from a RQL query do nothing here, dispatch according to the statement type """build_plan=getattr(self,'build_%s_plan'%plan.rqlst.TYPE)forstepinbuild_plan(plan,plan.rqlst):plan.add_step(step)defbuild_select_plan(self,plan,rqlst):"""build execution plan for a SELECT RQL query. Suppose only one source is available and so avoid work need for query decomposition among sources the rqlst should not be tagged at this point. """plan.preprocess(rqlst)return(OneFetchStep(plan,rqlst),)defbuild_insert_plan(self,plan,rqlst):"""get an execution plan from an INSERT RQL query"""# each variable in main variables is a new entity to insertto_build={}cnx=plan.cnxetype_class=cnx.vreg['etypes'].etype_classforetype,varinrqlst.main_variables:# need to do this since entity class is shared w. web client code !to_build[var.name]=EditedEntity(etype_class(etype)(cnx))plan.add_entity_def(to_build[var.name])# add constant values to entity def, mark variables to be selectedto_select=_extract_const_attributes(plan,rqlst,to_build)# add necessary steps to add relations and update attributesstep=InsertStep(plan)# insert each entity and its relationsstep.children+=self._compute_relation_steps(plan,rqlst,to_select)return(step,)def_compute_relation_steps(self,plan,rqlst,to_select):"""handle the selection of relations for an insert query"""eidconsts=_extract_eid_consts(plan,rqlst)foredef,rdefsinto_select.items():# create a select rql st to fetch needed dataselect=Select()eschema=edef.entity.e_schemafori,(rtype,term,reverse)inenumerate(rdefs):ifgetattr(term,'variable',None)ineidconsts:value=eidconsts[term.variable]else:select.append_selected(term.copy(select))value=_FROM_SUBSTEPifreverse:rdefs[i]=(rtype,InsertRelationsStep.REVERSE_RELATION,value)else:rschema=eschema.subjrels[rtype]ifrschema.finalorrschema.inlined:rdefs[i]=(rtype,InsertRelationsStep.FINAL,value)else:rdefs[i]=(rtype,InsertRelationsStep.RELATION,value)step=InsertRelationsStep(plan,edef,rdefs)select=_build_substep_query(select,rqlst)ifselectisnotNone:step.children+=self._select_plan(plan,select,rqlst.solutions)yieldstepdefbuild_delete_plan(self,plan,rqlst):"""get an execution plan from a DELETE RQL query"""# build a select query to fetch entities to deletesteps=[]foretype,varinrqlst.main_variables:step=DeleteEntitiesStep(plan)step.children+=self._sel_variable_step(plan,rqlst,etype,var)steps.append(step)forrelationinrqlst.main_relations:step=DeleteRelationsStep(plan,relation.r_type)step.children+=self._sel_relation_steps(plan,rqlst,relation)steps.append(step)returnstepsdef_sel_variable_step(self,plan,rqlst,etype,varref):"""handle the selection of variables for a delete query"""select=Select()varref=varref.copy(select)select.defined_vars={varref.name:varref.variable}select.append_selected(varref)ifrqlst.whereisnotNone:select.set_where(rqlst.where.copy(select))ifgetattr(rqlst,'having',None):select.set_having([x.copy(select)forxinrqlst.having])ifetype!='Any':select.add_type_restriction(varref.variable,etype)returnself._select_plan(plan,select,rqlst.solutions)def_sel_relation_steps(self,plan,rqlst,relation):"""handle the selection of relations for a delete query"""select=Select()lhs,rhs=relation.get_variable_parts()select.append_selected(lhs.copy(select))select.append_selected(rhs.copy(select))select.set_where(relation.copy(select))ifrqlst.whereisnotNone:select.add_restriction(rqlst.where.copy(select))ifgetattr(rqlst,'having',None):select.set_having([x.copy(select)forxinrqlst.having])returnself._select_plan(plan,select,rqlst.solutions)defbuild_set_plan(self,plan,rqlst):"""get an execution plan from an SET RQL query"""getrschema=self.schema.rschemaselect=Select()# potential substep queryselectedidx={}# local stateattributes=set()# edited attributesupdatedefs=[]# definition of update attributes/relationsselidx=residx=0# substep selection / resulting rset indexes# search for eid const in the WHERE clauseeidconsts=_extract_eid_consts(plan,rqlst)# build `updatedefs` describing things to update and add necessary# variables to the substep selectionfori,relationinenumerate(rqlst.main_relations):ifrelation.r_typeinVIRTUAL_RTYPES:raiseQueryError('can not assign to %r relation'%relation.r_type)lhs,rhs=relation.get_variable_parts()lhskey=lhs.as_string()ifnotlhskeyinselectedidx:iflhs.variableineidconsts:eid=eidconsts[lhs.variable]lhsinfo=(_CONSTANT,eid,residx)else:select.append_selected(lhs.copy(select))lhsinfo=(_FROM_SUBSTEP,selidx,residx)selidx+=1residx+=1selectedidx[lhskey]=lhsinfoelse:lhsinfo=selectedidx[lhskey][:-1]+(None,)rhskey=rhs.as_string()ifnotrhskeyinselectedidx:ifisinstance(rhs,Constant):rhsinfo=(_CONSTANT,rhs.eval(plan.args),residx)elifgetattr(rhs,'variable',None)ineidconsts:eid=eidconsts[rhs.variable]rhsinfo=(_CONSTANT,eid,residx)else:select.append_selected(rhs.copy(select))rhsinfo=(_FROM_SUBSTEP,selidx,residx)selidx+=1residx+=1selectedidx[rhskey]=rhsinfoelse:rhsinfo=selectedidx[rhskey][:-1]+(None,)rschema=getrschema(relation.r_type)updatedefs.append((lhsinfo,rhsinfo,rschema))# the update stepstep=UpdateStep(plan,updatedefs)# when necessary add substep to fetch yet unknown valuesselect=_build_substep_query(select,rqlst)ifselectisnotNone:# set distinct to avoid potential duplicate key errorselect.distinct=Truestep.children+=self._select_plan(plan,select,rqlst.solutions)return(step,)# internal methods ########################################################def_select_plan(self,plan,select,solutions):union=Union()union.append(select)select.clean_solutions(solutions)add_types_restriction(self.schema,select)self.rqlhelper.annotate(union)returnself.build_select_plan(plan,union)# execution steps and helper functions ########################################defvarmap_test_repr(varmap,tablesinorder):ifvarmapisNone:returnvarmapmaprepr={}forvar,sqlinvarmap.items():table,col=sql.split('.')maprepr[var]='%s.%s'%(tablesinorder[table],col)returnmapreprclassStep(object):"""base abstract class for execution step"""def__init__(self,plan):self.plan=planself.children=[]defexecute_child(self):assertlen(self.children)==1returnself.children[0].execute()defexecute_children(self):forstepinself.children:step.execute()defexecute(self):"""execute this step and store partial (eg this step) results"""raiseNotImplementedError()defmytest_repr(self):"""return a representation of this step suitable for test"""return(self.__class__.__name__,)deftest_repr(self):"""return a representation of this step suitable for test"""returnself.mytest_repr()+([step.test_repr()forstepinself.children],)classOneFetchStep(Step):"""step consisting in fetching data from sources and directly returning results """def__init__(self,plan,union,inputmap=None):Step.__init__(self,plan)self.union=unionself.inputmap=inputmapdefexecute(self):"""call .syntax_tree_search with the given syntax tree on each source for each solution """self.execute_children()cnx=self.plan.cnxargs=self.plan.argsinputmap=self.inputmapunion=self.union# do we have to use a inputmap from a previous step ? If so disable# cachekeyifinputmaporself.plan.cache_keyisNone:cachekey=None# union may have been splited into subqueries, in which case we can't# use plan.cache_key, rebuild a cache keyelifisinstance(self.plan.cache_key,tuple):cachekey=list(self.plan.cache_key)cachekey[0]=union.as_string()cachekey=tuple(cachekey)else:cachekey=union.as_string()# get results for querysource=cnx.repo.system_sourceresult=source.syntax_tree_search(cnx,union,args,cachekey,inputmap)#print 'ONEFETCH RESULT %s' % (result)returnresultdefmytest_repr(self):"""return a representation of this step suitable for test"""try:inputmap=varmap_test_repr(self.inputmap,self.plan.tablesinorder)exceptAttributeError:inputmap=self.inputmapreturn(self.__class__.__name__,sorted((r.as_string(kwargs=self.plan.args),r.solutions)forrinself.union.children),inputmap)# UPDATE/INSERT/DELETE steps ##################################################classInsertRelationsStep(Step):"""step consisting in adding attributes/relations to entity defs from a previous FetchStep relations values comes from the latest result, with one columns for each relation defined in self.rdefs for one entity definition, we'll construct N entity, where N is the number of the latest result """FINAL=0RELATION=1REVERSE_RELATION=2def__init__(self,plan,edef,rdefs):Step.__init__(self,plan)# partial entity definition to expandself.edef=edef# definition of relations to completeself.rdefs=rdefsdefexecute(self):"""execute this step"""base_edef=self.edefedefs=[]ifself.children:result=self.execute_child()else:result=[[]]forrowinresult:# get a new entity definition for this rowedef=base_edef.clone()# complete this entity def using row valuesindex=0forrtype,rorder,valueinself.rdefs:ifvalueis_FROM_SUBSTEP:value=row[index]index+=1ifrorder==InsertRelationsStep.FINAL:edef.edited_attribute(rtype,value)elifrorder==InsertRelationsStep.RELATION:self.plan.add_relation_def((edef,rtype,value))edef.querier_pending_relations[(rtype,'subject')]=valueelse:self.plan.add_relation_def((value,rtype,edef))edef.querier_pending_relations[(rtype,'object')]=valueedefs.append(edef)self.plan.substitute_entity_def(base_edef,edefs)returnresultclassInsertStep(Step):"""step consisting in inserting new entities / relations"""defexecute(self):"""execute this step"""forstepinself.children:assertisinstance(step,InsertRelationsStep)step.plan=self.planstep.execute()# insert entities firstresult=self.plan.insert_entity_defs()# then relationself.plan.insert_relation_defs()# return eids of inserted entitiesreturnresultclassDeleteEntitiesStep(Step):"""step consisting in deleting entities"""defexecute(self):"""execute this step"""results=self.execute_child()ifresults:todelete=frozenset(int(eid)foreid,inresults)cnx=self.plan.cnxcnx.repo.glob_delete_entities(cnx,todelete)returnresultsclassDeleteRelationsStep(Step):"""step consisting in deleting relations"""def__init__(self,plan,rtype):Step.__init__(self,plan)self.rtype=rtypedefexecute(self):"""execute this step"""cnx=self.plan.cnxdelete=cnx.repo.glob_delete_relationforsubj,objinself.execute_child():delete(cnx,subj,self.rtype,obj)classUpdateStep(Step):"""step consisting in updating entities / adding relations from relations definitions and from results fetched in previous step """def__init__(self,plan,updatedefs):Step.__init__(self,plan)self.updatedefs=updatedefsdefexecute(self):"""execute this step"""cnx=self.plan.cnxrepo=cnx.repoedefs={}relations={}# insert relationsifself.children:result=self.execute_child()else:result=[[]]fori,rowinenumerate(result):newrow=[]for(lhsinfo,rhsinfo,rschema)inself.updatedefs:lhsval=_handle_relterm(lhsinfo,row,newrow)rhsval=_handle_relterm(rhsinfo,row,newrow)ifrschema.finalorrschema.inlined:eid=int(lhsval)try:edited=edefs[eid]exceptKeyError:edef=cnx.entity_from_eid(eid)edefs[eid]=edited=EditedEntity(edef)edited.edited_attribute(str(rschema),rhsval)else:str_rschema=str(rschema)ifstr_rschemainrelations:relations[str_rschema].append((lhsval,rhsval))else:relations[str_rschema]=[(lhsval,rhsval)]result[i]=newrow# update entitiesrepo.glob_add_relations(cnx,relations)foreid,editedinedefs.items():repo.glob_update_entity(cnx,edited)returnresultdef_handle_relterm(info,row,newrow):ifinfo[0]is_CONSTANT:val=info[1]else:# _FROM_SUBSTEPval=row[info[1]]ifinfo[-1]isnotNone:newrow.append(val)returnval