# HG changeset patch # User Nicolas Chauvat # Date 1248351223 -7200 # Node ID d7f0e551d0fac3239fa3a9af1b4e287e5b69ab0e # Parent 8e6b83e3b6b56a0f47ddb7f98b53a36ca2c1149a# Parent 85be7a811afe0bb3df3b3d4e761677f2bfe9388c merge diff -r 8e6b83e3b6b5 -r d7f0e551d0fa schemas/base.py --- a/schemas/base.py Thu Jul 23 14:12:34 2009 +0200 +++ b/schemas/base.py Thu Jul 23 14:13:43 2009 +0200 @@ -205,6 +205,29 @@ """generic relation to link one entity to another""" symetric = True +class ExternalUri(EntityType): + """a URI representing an object in external data store""" + uri = String(required=True, unique=True, maxsize=256, + description=_('the URI of the object')) + +class same_as(RelationType): + """generic relation to specify that an external entity represent the same + object as a local one: + http://www.w3.org/TR/owl-ref/#sameAs-def + + NOTE: You'll have to explicitly declare which entity types can have a + same_as relation + """ + permissions = { + 'read': ('managers', 'users', 'guests',), + 'add': ('managers', 'users'), + 'delete': ('managers', 'owners'), + } + cardinality = '*1' + symetric = True + # NOTE: the 'object = ExternalUri' declaration will still be mandatory + # in the cube's schema. + object = 'ExternalUri' class CWCache(EntityType): """a simple cache entity characterized by a name and diff -r 8e6b83e3b6b5 -r d7f0e551d0fa spa2rql.py --- a/spa2rql.py Thu Jul 23 14:12:34 2009 +0200 +++ b/spa2rql.py Thu Jul 23 14:13:43 2009 +0200 @@ -27,6 +27,7 @@ addons += ' OFFSET %s' % sparqlst.offset return addons + class QueryInfo(object): """wrapper class containing necessary information to generate a RQL query from a sparql syntax tree @@ -41,41 +42,14 @@ self.infer_types_info = [] self.union_params = [] self.restrictions = [] + self.literals = {} + self._litcount = 0 - def finalize(self): - """return corresponding rql query""" - for varname, ptypes in self.possible_types.iteritems(): - if len(ptypes) == 1: - self.restrictions.append('%s is %s' % (varname, iter(ptypes).next())) - unions = [] - for releq, subjvar, objvar in self.union_params: - thisunions = [] - for st, rt, ot in releq: - thisunions.append(['%s %s %s' % (subjvar, rt, objvar)]) - if st != '*': - thisunions[-1].append('%s is %s' % (subjvar, st)) - if ot != '*': - thisunions[-1].append('%s is %s' % (objvar, ot)) - if not unions: - unions = thisunions - else: - unions = zip(*make_domains([unions, thisunions])) - selection = 'Any ' + ', '.join(self.selection) - sparqlst = self.sparqlst - if sparqlst.distinct: - selection = 'DISTINCT ' + selection - if not unions: - return '%s%s WHERE %s' % (selection, order_limit_offset(sparqlst), - ', '.join(self.restrictions)) - baserql = '%s WHERE %s' % (selection, ', '.join(self.restrictions)) - rqls = ['(%s, %s)' % (baserql, ', '.join(unionrestrs)) - for unionrestrs in unions] - rql = ' UNION '.join(rqls) - if sparqlst.orderby or sparqlst.limit or sparqlst.offset: - rql = '%s%s WITH %s BEING (%s)' % ( - selection, order_limit_offset(sparqlst), - ', '.join(self.selection), rql) - return rql + def add_literal(self, value): + key = chr(ord('a') + self._litcount) + self._litcount += 1 + self.literals[key] = value + return key def set_possible_types(self, var, varpossibletypes): """set/restrict possible types for the given variable. @@ -131,19 +105,69 @@ raise TypeResolverException() if len(yams_predicates) != nbchoices: modified = True + + def build_restrictions(self): # now, for each predicate for yams_predicates, subjvar, obj in self.infer_types_info: rel = yams_predicates[0] - objvar = obj.name.upper() # if there are several yams relation type equivalences, we will have # to generate several unioned rql queries for s, r, o in yams_predicates[1:]: if r != rel[1]: - self.union_params.append((yams_predicates, subjvar, objvar)) + self.union_params.append((yams_predicates, subjvar, obj)) break + # else we can simply add it to base rql restrictions else: - # else we can simply add it to base rql restrictions - self.restrictions.append('%s %s %s' % (subjvar, rel[1], objvar)) + restr = self.build_restriction(subjvar, rel[1], obj) + self.restrictions.append(restr) + + def build_restriction(self, subjvar, rtype, obj): + if isinstance(obj, ast.SparqlLiteral): + key = self.add_literal(obj.value) + objvar = '%%(%s)s' % key + else: + assert isinstance(obj, ast.SparqlVar) + # make a valid rql var name + objvar = obj.name.upper() + # else we can simply add it to base rql restrictions + return '%s %s %s' % (subjvar, rtype, objvar) + + def finalize(self): + """return corresponding rql query (string) / args (dict)""" + for varname, ptypes in self.possible_types.iteritems(): + if len(ptypes) == 1: + self.restrictions.append('%s is %s' % (varname, iter(ptypes).next())) + unions = [] + for releq, subjvar, obj in self.union_params: + thisunions = [] + for st, rt, ot in releq: + thisunions.append([self.build_restriction(subjvar, rt, obj)]) + if st != '*': + thisunions[-1].append('%s is %s' % (subjvar, st)) + if isinstance(obj, ast.SparqlVar) and ot != '*': + objvar = obj.name.upper() + thisunions[-1].append('%s is %s' % (objvar, objvar)) + if not unions: + unions = thisunions + else: + unions = zip(*make_domains([unions, thisunions])) + selection = 'Any ' + ', '.join(self.selection) + sparqlst = self.sparqlst + if sparqlst.distinct: + selection = 'DISTINCT ' + selection + if unions: + baserql = '%s WHERE %s' % (selection, ', '.join(self.restrictions)) + rqls = ['(%s, %s)' % (baserql, ', '.join(unionrestrs)) + for unionrestrs in unions] + rql = ' UNION '.join(rqls) + if sparqlst.orderby or sparqlst.limit or sparqlst.offset: + rql = '%s%s WITH %s BEING (%s)' % ( + selection, order_limit_offset(sparqlst), + ', '.join(self.selection), rql) + else: + rql = '%s%s WHERE %s' % (selection, order_limit_offset(sparqlst), + ', '.join(self.restrictions)) + return rql, self.literals class Sparql2rqlTranslator(object): @@ -176,10 +200,8 @@ # where subject / object entity type may '*' if not specified yams_predicates = xy.yeq(':'.join(predicate)) qi.infer_types_info.append((yams_predicates, subjvar, obj)) - if isinstance(obj, ast.SparqlVar): - # make a valid rql var name - objvar = obj.name.upper() - else: + if not isinstance(obj, (ast.SparqlLiteral, ast.SparqlVar)): raise UnsupportedQuery() qi.infer_types() + qi.build_restrictions() return qi diff -r 8e6b83e3b6b5 -r d7f0e551d0fa test/unittest_spa2rql.py --- a/test/unittest_spa2rql.py Thu Jul 23 14:12:34 2009 +0200 +++ b/test/unittest_spa2rql.py Thu Jul 23 14:13:43 2009 +0200 @@ -17,9 +17,9 @@ def setUp(self): self.tr = Sparql2rqlTranslator(schema) - def _test(self, sparql, rql): + def _test(self, sparql, rql, args={}): qi = self.tr.translate(sparql) - self.assertEquals(qi.finalize(), rql) + self.assertEquals(qi.finalize(), (rql, args)) def XXX_test_base_01(self): self._test('SELECT * WHERE { }', 'Any X') @@ -144,6 +144,16 @@ finally: xy.remove_equivalence('Version publication_date', 'doap:Version dc:date') + + def test_restr_attr(self): + self._test(''' + PREFIX doap: + SELECT ?project + WHERE { + ?project a doap:Project; + doap:name "cubicweb". + }''', 'Any PROJECT WHERE PROJECT name %(a)s, PROJECT is Project', {'a': 'cubicweb'}) + # # Two elements in the group # PREFIX : # SELECT *