spa2rql.py
author Nicolas Chauvat <nicolas.chauvat@logilab.fr>
Tue, 01 Jun 2010 15:19:06 +0200
branchstable
changeset 5621 6f32f08d91f3
parent 5613 bc0ebfbf5c5d
child 10662 10942ed172de
permissions -rw-r--r--
[sparql] improve error reporting and fix one bug
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
5421
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     1
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     2
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     3
#
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     4
# This file is part of CubicWeb.
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     5
#
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     6
# CubicWeb is free software: you can redistribute it and/or modify it under the
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     7
# terms of the GNU Lesser General Public License as published by the Free
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     8
# Software Foundation, either version 2.1 of the License, or (at your option)
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
     9
# any later version.
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
    10
#
5424
8ecbcbff9777 replace logilab-common by CubicWeb in disclaimer
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5421
diff changeset
    11
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
5421
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
    12
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
    13
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
    14
# details.
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
    15
#
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
    16
# You should have received a copy of the GNU Lesser General Public License along
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
    17
# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
5613
bc0ebfbf5c5d [sparql] fix type inference (test_dctitle_both_project_cwuser) and support for <http://www.w3.org/1999/02/22-rdf-syntax-ns#type tag (test_base_rdftype)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5424
diff changeset
    18
"""SPARQL -> RQL translator"""
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    19
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    20
from logilab.common import make_domains
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    21
from rql import TypeResolverException
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    22
from fyzz.yappsparser import parse
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    23
from fyzz import ast
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    24
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    25
from cubicweb.xy import xy
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    26
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    27
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    28
class UnsupportedQuery(Exception): pass
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    29
2431
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    30
def order_limit_offset(sparqlst):
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    31
    addons = ''
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    32
    if sparqlst.orderby:
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    33
        sortterms = ', '.join('%s %s' % (var.name.upper(), ascdesc.upper())
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    34
                              for var, ascdesc in sparqlst.orderby)
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    35
        addons += ' ORDERBY %s' % sortterms
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    36
    if sparqlst.limit:
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    37
        addons += ' LIMIT %s' % sparqlst.limit
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    38
    if sparqlst.offset:
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    39
        addons += ' OFFSET %s' % sparqlst.offset
93c061eac647 sparql support for limit/offset/orderby
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2430
diff changeset
    40
    return addons
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    41
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
    42
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    43
class QueryInfo(object):
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    44
    """wrapper class containing necessary information to generate a RQL query
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    45
    from a sparql syntax tree
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    46
    """
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    47
    def __init__(self, sparqlst):
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    48
        self.sparqlst = sparqlst
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    49
        if sparqlst.selected == ['*']:
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    50
            self.selection = [var.upper() for var in sparqlst.variables]
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    51
        else:
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    52
            self.selection = [var.name.upper() for var in sparqlst.selected]
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    53
        self.possible_types = {}
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    54
        self.infer_types_info = []
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    55
        self.union_params = []
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    56
        self.restrictions = []
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
    57
        self.literals = {}
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
    58
        self._litcount = 0
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    59
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
    60
    def add_literal(self, value):
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
    61
        key = chr(ord('a') + self._litcount)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
    62
        self._litcount += 1
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
    63
        self.literals[key] = value
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
    64
        return key
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    65
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    66
    def set_possible_types(self, var, varpossibletypes):
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    67
        """set/restrict possible types for the given variable.
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    68
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    69
        :return: True if something changed, else false.
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    70
        :raise: TypeResolverException if no more type allowed
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    71
        """
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    72
        varpossibletypes = set(varpossibletypes)
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    73
        try:
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    74
            ctypes = self.possible_types[var]
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    75
            nbctypes = len(ctypes)
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    76
            ctypes &= varpossibletypes
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    77
            if not ctypes:
5621
6f32f08d91f3 [sparql] improve error reporting and fix one bug
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 5613
diff changeset
    78
                raise TypeResolverException('No possible type')
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    79
            return len(ctypes) != nbctypes
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    80
        except KeyError:
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    81
            self.possible_types[var] = varpossibletypes
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    82
            return True
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    83
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    84
    def infer_types(self):
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    85
        # XXX should use something similar to rql.analyze for proper type inference
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    86
        modified = True
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    87
        # loop to infer types until nothing changed
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    88
        while modified:
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    89
            modified = False
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    90
            for yams_predicates, subjvar, obj in self.infer_types_info:
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    91
                nbchoices = len(yams_predicates)
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    92
                # get possible types for the subject variable, according to the
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    93
                # current predicate
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    94
                svptypes = set(s for s, r, o in yams_predicates)
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    95
                if not '*' in svptypes:
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    96
                    if self.set_possible_types(subjvar, svptypes):
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    97
                        modified = True
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    98
                # restrict predicates according to allowed subject var types
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
    99
                if subjvar in self.possible_types:
5613
bc0ebfbf5c5d [sparql] fix type inference (test_dctitle_both_project_cwuser) and support for <http://www.w3.org/1999/02/22-rdf-syntax-ns#type tag (test_base_rdftype)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5424
diff changeset
   100
                    yams_predicates[:] = [(s, r, o) for s, r, o in yams_predicates
bc0ebfbf5c5d [sparql] fix type inference (test_dctitle_both_project_cwuser) and support for <http://www.w3.org/1999/02/22-rdf-syntax-ns#type tag (test_base_rdftype)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5424
diff changeset
   101
                                          if s == '*' or s in self.possible_types[subjvar]]
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   102
                if isinstance(obj, ast.SparqlVar):
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   103
                    # make a valid rql var name
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   104
                    objvar = obj.name.upper()
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   105
                    # get possible types for the object variable, according to
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   106
                    # the current predicate
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   107
                    ovptypes = set(o for s, r, o in yams_predicates)
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   108
                    if not '*' in ovptypes:
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   109
                        if self.set_possible_types(objvar, ovptypes):
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   110
                            modified = True
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   111
                    # restrict predicates according to allowed object var types
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   112
                    if objvar in self.possible_types:
5613
bc0ebfbf5c5d [sparql] fix type inference (test_dctitle_both_project_cwuser) and support for <http://www.w3.org/1999/02/22-rdf-syntax-ns#type tag (test_base_rdftype)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5424
diff changeset
   113
                        yams_predicates[:] = [(s, r, o) for s, r, o in yams_predicates
bc0ebfbf5c5d [sparql] fix type inference (test_dctitle_both_project_cwuser) and support for <http://www.w3.org/1999/02/22-rdf-syntax-ns#type tag (test_base_rdftype)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5424
diff changeset
   114
                                              if o == '*' or o in self.possible_types[objvar]]
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   115
                # ensure this still make sense
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   116
                if not yams_predicates:
5621
6f32f08d91f3 [sparql] improve error reporting and fix one bug
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 5613
diff changeset
   117
                    raise TypeResolverException('No yams predicate')
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   118
                if len(yams_predicates) != nbchoices:
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   119
                    modified = True
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   120
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   121
    def build_restrictions(self):
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   122
        # now, for each predicate
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   123
        for yams_predicates, subjvar, obj in self.infer_types_info:
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   124
            rel = yams_predicates[0]
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   125
            # if there are several yams relation type equivalences, we will have
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   126
            # to generate several unioned rql queries
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   127
            for s, r, o in yams_predicates[1:]:
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   128
                if r != rel[1]:
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   129
                    self.union_params.append((yams_predicates, subjvar, obj))
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   130
                    break
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   131
            # else we can simply add it to base rql restrictions
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   132
            else:
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   133
                restr = self.build_restriction(subjvar, rel[1], obj)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   134
                self.restrictions.append(restr)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   135
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   136
    def build_restriction(self, subjvar, rtype, obj):
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   137
        if isinstance(obj, ast.SparqlLiteral):
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   138
            key = self.add_literal(obj.value)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   139
            objvar = '%%(%s)s' % key
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   140
        else:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   141
            assert isinstance(obj, ast.SparqlVar)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   142
            # make a valid rql var name
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   143
            objvar = obj.name.upper()
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   144
        # else we can simply add it to base rql restrictions
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   145
        return '%s %s %s' % (subjvar, rtype, objvar)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   146
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   147
    def finalize(self):
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   148
        """return corresponding rql query (string) / args (dict)"""
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   149
        for varname, ptypes in self.possible_types.iteritems():
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   150
            if len(ptypes) == 1:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   151
                self.restrictions.append('%s is %s' % (varname, iter(ptypes).next()))
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   152
        unions = []
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   153
        for releq, subjvar, obj in self.union_params:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   154
            thisunions = []
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   155
            for st, rt, ot in releq:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   156
                thisunions.append([self.build_restriction(subjvar, rt, obj)])
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   157
                if st != '*':
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   158
                    thisunions[-1].append('%s is %s' % (subjvar, st))
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   159
                if isinstance(obj, ast.SparqlVar) and ot != '*':
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   160
                    objvar = obj.name.upper()
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   161
                    thisunions[-1].append('%s is %s' % (objvar, objvar))
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   162
            if not unions:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   163
                unions = thisunions
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   164
            else:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   165
                unions = zip(*make_domains([unions, thisunions]))
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   166
        selection = 'Any ' + ', '.join(self.selection)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   167
        sparqlst = self.sparqlst
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   168
        if sparqlst.distinct:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   169
            selection = 'DISTINCT ' + selection
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   170
        if unions:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   171
            baserql = '%s WHERE %s' % (selection, ', '.join(self.restrictions))
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   172
            rqls = ['(%s, %s)' % (baserql, ', '.join(unionrestrs))
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   173
                    for unionrestrs in unions]
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   174
            rql = ' UNION '.join(rqls)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   175
            if sparqlst.orderby or sparqlst.limit or sparqlst.offset:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   176
                rql = '%s%s WITH %s BEING (%s)' % (
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   177
                    selection, order_limit_offset(sparqlst),
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   178
                    ', '.join(self.selection), rql)
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   179
        else:
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   180
            rql = '%s%s WHERE %s' % (selection, order_limit_offset(sparqlst),
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   181
                                      ', '.join(self.restrictions))
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   182
        return rql, self.literals
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   183
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   184
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   185
class Sparql2rqlTranslator(object):
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   186
    def __init__(self, yschema):
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   187
        self.yschema = yschema
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   188
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   189
    def translate(self, sparql):
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   190
        sparqlst = parse(sparql)
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   191
        if sparqlst.type != 'select':
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   192
            raise UnsupportedQuery()
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   193
        qi = QueryInfo(sparqlst)
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   194
        for subj, predicate, obj in sparqlst.where:
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   195
            if not isinstance(subj, ast.SparqlVar):
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   196
                raise UnsupportedQuery()
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   197
            # make a valid rql var name
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   198
            subjvar = subj.name.upper()
5613
bc0ebfbf5c5d [sparql] fix type inference (test_dctitle_both_project_cwuser) and support for <http://www.w3.org/1999/02/22-rdf-syntax-ns#type tag (test_base_rdftype)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5424
diff changeset
   199
            if predicate in [('', 'a'),
bc0ebfbf5c5d [sparql] fix type inference (test_dctitle_both_project_cwuser) and support for <http://www.w3.org/1999/02/22-rdf-syntax-ns#type tag (test_base_rdftype)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5424
diff changeset
   200
                             ('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'type')]:
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   201
                # special 'is' relation
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   202
                if not isinstance(obj, tuple):
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   203
                    raise UnsupportedQuery()
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   204
                # restrict possible types for the subject variable
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   205
                qi.set_possible_types(
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   206
                    subjvar, xy.yeq(':'.join(obj), isentity=True))
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   207
            else:
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   208
                # 'regular' relation (eg not 'is')
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   209
                if not isinstance(predicate, tuple):
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   210
                    raise UnsupportedQuery()
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   211
                # list of 3-uple
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   212
                #   (yams etype (subject), yams rtype, yams etype (object))
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   213
                # where subject / object entity type may '*' if not specified
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   214
                yams_predicates = xy.yeq(':'.join(predicate))
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   215
                qi.infer_types_info.append((yams_predicates, subjvar, obj))
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   216
                if not isinstance(obj, (ast.SparqlLiteral, ast.SparqlVar)):
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   217
                    raise UnsupportedQuery()
2430
7d9ed6c740ec cleanup/document/refactor
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2427
diff changeset
   218
        qi.infer_types()
2435
85be7a811afe sparql: support for literal restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2431
diff changeset
   219
        qi.build_restrictions()
2422
96da7dc42eb5 quick and dirty support from simple sparql queries + base ui
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   220
        return qi