61 # the check for ?, /, & are to prevent problems when running |
61 # the check for ?, /, & are to prevent problems when running |
62 # behind Apache mod_proxy |
62 # behind Apache mod_proxy |
63 if value == u'' or u'?' in value or u'/' in value or u'&' in value: |
63 if value == u'' or u'?' in value or u'/' in value or u'&' in value: |
64 return False |
64 return False |
65 return True |
65 return True |
66 |
|
67 |
|
68 def remove_ambiguous_rels(attr_set, subjtypes, schema): |
|
69 '''remove from `attr_set` the relations of entity types `subjtypes` that have |
|
70 different entity type sets as target''' |
|
71 for attr in attr_set.copy(): |
|
72 rschema = schema.rschema(attr) |
|
73 if rschema.final: |
|
74 continue |
|
75 ttypes = None |
|
76 for subjtype in subjtypes: |
|
77 cur_ttypes = rschema.objects(subjtype) |
|
78 if ttypes is None: |
|
79 ttypes = cur_ttypes |
|
80 elif cur_ttypes != ttypes: |
|
81 attr_set.remove(attr) |
|
82 break |
|
83 |
66 |
84 |
67 |
85 class Entity(AppObject): |
68 class Entity(AppObject): |
86 """an entity instance has e_schema automagically set on |
69 """an entity instance has e_schema automagically set on |
87 the class and instances has access to their issuing cursor. |
70 the class and instances has access to their issuing cursor. |
213 fetchattrs = cls.fetch_attrs |
196 fetchattrs = cls.fetch_attrs |
214 cls._fetch_restrictions(mainvar, select, fetchattrs, user, ordermethod) |
197 cls._fetch_restrictions(mainvar, select, fetchattrs, user, ordermethod) |
215 return select |
198 return select |
216 |
199 |
217 @classmethod |
200 @classmethod |
|
201 def _fetch_ambiguous_rtypes(cls, select, var, fetchattrs, subjtypes, schema): |
|
202 """find rtypes in `fetchattrs` that relate different subject etypes |
|
203 taken from (`subjtypes`) to different target etypes; these so called |
|
204 "ambiguous" relations, are added directly to the `select` syntax tree |
|
205 selection but removed from `fetchattrs` to avoid the fetch recursion |
|
206 because we have to choose only one targettype for the recursion and |
|
207 adding its own fetch attrs to the selection -when we recurse- would |
|
208 filter out the other possible target types from the result set |
|
209 """ |
|
210 for attr in fetchattrs.copy(): |
|
211 rschema = schema.rschema(attr) |
|
212 if rschema.final: |
|
213 continue |
|
214 ttypes = None |
|
215 for subjtype in subjtypes: |
|
216 cur_ttypes = set(rschema.objects(subjtype)) |
|
217 if ttypes is None: |
|
218 ttypes = cur_ttypes |
|
219 elif cur_ttypes != ttypes: |
|
220 # we found an ambiguous relation: remove it from fetchattrs |
|
221 fetchattrs.remove(attr) |
|
222 # ... and add it to the selection |
|
223 targetvar = select.make_variable() |
|
224 select.add_selected(targetvar) |
|
225 rel = make_relation(var, attr, (targetvar,), VariableRef) |
|
226 select.add_restriction(rel) |
|
227 break |
|
228 |
|
229 @classmethod |
218 def _fetch_restrictions(cls, mainvar, select, fetchattrs, |
230 def _fetch_restrictions(cls, mainvar, select, fetchattrs, |
219 user, ordermethod='fetch_order', visited=None): |
231 user, ordermethod='fetch_order', visited=None): |
220 eschema = cls.e_schema |
232 eschema = cls.e_schema |
221 if visited is None: |
233 if visited is None: |
222 visited = set((eschema.type,)) |
234 visited = set((eschema.type,)) |
250 # (card == '?') *or if the entity is being added*, since in |
262 # (card == '?') *or if the entity is being added*, since in |
251 # that case the relation may still be missing. As we miss this |
263 # that case the relation may still be missing. As we miss this |
252 # later information here, systematically add it. |
264 # later information here, systematically add it. |
253 rel.change_optional('right') |
265 rel.change_optional('right') |
254 targettypes = rschema.objects(eschema.type) |
266 targettypes = rschema.objects(eschema.type) |
255 # XXX user._cw.vreg iiiirk |
267 vreg = user._cw.vreg # XXX user._cw.vreg iiiirk |
256 etypecls = user._cw.vreg['etypes'].etype_class(targettypes[0]) |
268 etypecls = vreg['etypes'].etype_class(targettypes[0]) |
257 if len(targettypes) > 1: |
269 if len(targettypes) > 1: |
258 # find fetch_attrs common to all destination types |
270 # find fetch_attrs common to all destination types |
259 fetchattrs = user._cw.vreg['etypes'].fetch_attrs(targettypes) |
271 fetchattrs = vreg['etypes'].fetch_attrs(targettypes) |
260 remove_ambiguous_rels(fetchattrs, targettypes, user._cw.vreg.schema) |
272 # .. and handle ambiguous relations |
|
273 cls._fetch_ambiguous_rtypes(select, var, fetchattrs, |
|
274 targettypes, vreg.schema) |
261 else: |
275 else: |
262 fetchattrs = etypecls.fetch_attrs |
276 fetchattrs = etypecls.fetch_attrs |
263 etypecls._fetch_restrictions(var, select, fetchattrs, |
277 etypecls._fetch_restrictions(var, select, fetchattrs, |
264 user, ordermethod, visited=visited) |
278 user, ordermethod, visited=visited) |
265 if ordermethod is not None: |
279 if ordermethod is not None: |
770 rset = self._cw.execute(rql, {'x': self.eid}) |
784 rset = self._cw.execute(rql, {'x': self.eid}) |
771 self.cw_set_relation_cache(rtype, role, rset) |
785 self.cw_set_relation_cache(rtype, role, rset) |
772 return self.related(rtype, role, limit, entities) |
786 return self.related(rtype, role, limit, entities) |
773 |
787 |
774 def cw_related_rql(self, rtype, role='subject', targettypes=None): |
788 def cw_related_rql(self, rtype, role='subject', targettypes=None): |
775 rschema = self._cw.vreg.schema[rtype] |
789 vreg = self._cw.vreg |
|
790 rschema = vreg.schema[rtype] |
776 select = Select() |
791 select = Select() |
777 mainvar, evar = select.get_variable('X'), select.get_variable('E') |
792 mainvar, evar = select.get_variable('X'), select.get_variable('E') |
778 select.add_selected(mainvar) |
793 select.add_selected(mainvar) |
779 select.add_eid_restriction(evar, 'x', 'Substitute') |
794 select.add_eid_restriction(evar, 'x', 'Substitute') |
780 if role == 'subject': |
795 if role == 'subject': |
793 targettypes = rschema.subjects(self.e_schema) |
808 targettypes = rschema.subjects(self.e_schema) |
794 else: |
809 else: |
795 select.add_constant_restriction(mainvar, 'is', targettypes, |
810 select.add_constant_restriction(mainvar, 'is', targettypes, |
796 'String') |
811 'String') |
797 gcard = greater_card(rschema, targettypes, (self.e_schema,), 1) |
812 gcard = greater_card(rschema, targettypes, (self.e_schema,), 1) |
798 etypecls = self._cw.vreg['etypes'].etype_class(targettypes[0]) |
813 etypecls = vreg['etypes'].etype_class(targettypes[0]) |
799 if len(targettypes) > 1: |
814 if len(targettypes) > 1: |
800 fetchattrs = self._cw.vreg['etypes'].fetch_attrs(targettypes) |
815 fetchattrs = vreg['etypes'].fetch_attrs(targettypes) |
801 # XXX we should fetch ambiguous relation objects too but not |
816 self._fetch_ambiguous_rtypes(select, mainvar, fetchattrs, |
802 # recurse on them in _fetch_restrictions; it is easier to remove |
817 targettypes, vreg.schema) |
803 # them completely for now, as it would require a deeper api rewrite |
|
804 remove_ambiguous_rels(fetchattrs, targettypes, self._cw.vreg.schema) |
|
805 else: |
818 else: |
806 fetchattrs = etypecls.fetch_attrs |
819 fetchattrs = etypecls.fetch_attrs |
807 etypecls.fetch_rqlst(self._cw.user, select, mainvar, fetchattrs, |
820 etypecls.fetch_rqlst(self._cw.user, select, mainvar, fetchattrs, |
808 settype=False) |
821 settype=False) |
809 # optimisation: remove ORDERBY if cardinality is 1 or ? (though |
822 # optimisation: remove ORDERBY if cardinality is 1 or ? (though |