cubicweb/server/rqlannotation.py
author Nicolas Chauvat <nicolas.chauvat@logilab.fr>
Wed, 13 Mar 2019 10:26:15 +0100
changeset 12885 194e9ae964ed
parent 12567 26744ad37953
permissions -rw-r--r--
[server.rqlannotation] rename SQLGenAnnotator to RQLAnnotator This class is in charge of annotating the RQL syntax tree with various bits of information, like (in)variance, use of full-text-index, etc. It is a needed step before the generation of SQL, but does not touch SQL directly. Hence RQLAnnotator seems a better name.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
     1
# copyright 2003-2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
5421
8167de96c523 proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
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: 4721
diff changeset
    17
# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    18
"""Functions to add additional annotations on a rql syntax tree to ease later
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    19
code generation.
6297
23c1e50ff97b [rql] fix bug with query like 'Any 1 WHERE NOT X in_group G': tables should be kept in EXISTS() even when there are no restriction
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5582
diff changeset
    20
"""
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    21
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    22
from rql import BadRQLQuery
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    23
from rql.nodes import Relation, VariableRef, Constant, Variable, Or
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    24
from rql.utils import common_parent
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    25
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    26
4721
8f63691ccb7f pylint style fixes
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4719
diff changeset
    27
class CantSelectPrincipal(Exception):
8f63691ccb7f pylint style fixes
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4719
diff changeset
    28
    """raised when no 'principal' variable can be found"""
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    29
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    30
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    31
def _select_principal(scope, relations, _sort=lambda x: x):
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    32
    """given a list of rqlst relations, select one which will be used to
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    33
    represent an invariant variable (e.g. using on extremity of the relation
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    34
    instead of the variable's type table
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    35
    """
599
9ef680acd92a fix select principal so results are predictable during tests
Sylvain <syt@logilab.fr>
parents: 438
diff changeset
    36
    # _sort argument is there for test
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    37
    diffscope_rels = {}
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    38
    ored_rels = set()
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    39
    diffscope_rels = set()
7357
5ad3154a8810 [rql2sql] fix bug avoiding outer join relation to be used as a variable principal. Closes #1659395
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7193
diff changeset
    40
    for rel, role in _sort(relations):
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    41
        # note: only eid and has_text among all final relations may be there
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    42
        if rel.r_type in ('eid', 'identity'):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    43
            continue
7357
5ad3154a8810 [rql2sql] fix bug avoiding outer join relation to be used as a variable principal. Closes #1659395
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7193
diff changeset
    44
        if rel.optional is not None and len(relations) > 1:
5ad3154a8810 [rql2sql] fix bug avoiding outer join relation to be used as a variable principal. Closes #1659395
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7193
diff changeset
    45
            if role == 'subject' and rel.optional == 'right':
5ad3154a8810 [rql2sql] fix bug avoiding outer join relation to be used as a variable principal. Closes #1659395
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7193
diff changeset
    46
                continue
5ad3154a8810 [rql2sql] fix bug avoiding outer join relation to be used as a variable principal. Closes #1659395
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7193
diff changeset
    47
            if role == 'object' and rel.optional == 'left':
5ad3154a8810 [rql2sql] fix bug avoiding outer join relation to be used as a variable principal. Closes #1659395
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7193
diff changeset
    48
                continue
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    49
        if rel.ored(traverse_scope=True):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    50
            ored_rels.add(rel)
5582
3e133b29a1a4 [rql2sql] follow rql 0.26.1 changes: NOT nodes normalization, allowing simplification of sql generation, and fix #XXX
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5426
diff changeset
    51
        elif rel.scope is scope:
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    52
            return rel
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    53
        elif not rel.neged(traverse_scope=True):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    54
            diffscope_rels.add(rel)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    55
    if len(ored_rels) > 1:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    56
        ored_rels_copy = tuple(ored_rels)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    57
        for rel1 in ored_rels_copy:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    58
            for rel2 in ored_rels_copy:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    59
                if rel1 is rel2:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    60
                    continue
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    61
                if isinstance(common_parent(rel1, rel2), Or):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    62
                    ored_rels.discard(rel1)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    63
                    ored_rels.discard(rel2)
599
9ef680acd92a fix select principal so results are predictable during tests
Sylvain <syt@logilab.fr>
parents: 438
diff changeset
    64
    for rel in _sort(ored_rels):
5582
3e133b29a1a4 [rql2sql] follow rql 0.26.1 changes: NOT nodes normalization, allowing simplification of sql generation, and fix #XXX
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5426
diff changeset
    65
        if rel.scope is scope:
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    66
            return rel
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    67
        diffscope_rels.add(rel)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    68
    # if DISTINCT query, can use variable from a different scope as principal
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    69
    # since introduced duplicates will be removed
5582
3e133b29a1a4 [rql2sql] follow rql 0.26.1 changes: NOT nodes normalization, allowing simplification of sql generation, and fix #XXX
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5426
diff changeset
    70
    if scope.stmt.distinct and diffscope_rels:
10669
155c29e0ed1c [py3k] use next builtin instead of next method
Rémi Cardona <remi.cardona@logilab.fr>
parents: 10663
diff changeset
    71
        return next(iter(_sort(diffscope_rels)))
7651
7c0af7ef3325 [repo, ms] fix planning of some queries where variable 'non-invarianess' should be considered, leading for instance to junk in entities table on source deletion
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7523
diff changeset
    72
    # XXX could use a relation from a different scope if it can't generate
7c0af7ef3325 [repo, ms] fix planning of some queries where variable 'non-invarianess' should be considered, leading for instance to junk in entities table on source deletion
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7523
diff changeset
    73
    # duplicates, so we should have to check cardinality
1802
d628defebc17 delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents: 1132
diff changeset
    74
    raise CantSelectPrincipal()
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    75
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    76
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    77
def _select_main_var(relations):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    78
    """given a list of rqlst relations, select one which will be used as main
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    79
    relation for the rhs variable
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    80
    """
3815
50b87f759b5d test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3694
diff changeset
    81
    principal = None
7707
936530f8d32c [sql gen] handle optional on comparison node (eg HAVING expression) and on rhs of final relation. Closes #1859609
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7651
diff changeset
    82
    others = []
3815
50b87f759b5d test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3694
diff changeset
    83
    # sort for test predictability
50b87f759b5d test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3694
diff changeset
    84
    for rel in sorted(relations, key=lambda x: (x.children[0].name, x.r_type)):
50b87f759b5d test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3694
diff changeset
    85
        # only equality relation with a variable as rhs may be principal
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    86
        if (rel.operator() not in ('=', 'IS')
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    87
                or not isinstance(rel.children[1].children[0], VariableRef)
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
    88
                or rel.neged(strict=True)):
3815
50b87f759b5d test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3694
diff changeset
    89
            continue
7707
936530f8d32c [sql gen] handle optional on comparison node (eg HAVING expression) and on rhs of final relation. Closes #1859609
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7651
diff changeset
    90
        if rel.optional:
936530f8d32c [sql gen] handle optional on comparison node (eg HAVING expression) and on rhs of final relation. Closes #1859609
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7651
diff changeset
    91
            others.append(rel)
936530f8d32c [sql gen] handle optional on comparison node (eg HAVING expression) and on rhs of final relation. Closes #1859609
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7651
diff changeset
    92
            continue
5582
3e133b29a1a4 [rql2sql] follow rql 0.26.1 changes: NOT nodes normalization, allowing simplification of sql generation, and fix #XXX
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 5426
diff changeset
    93
        if rel.scope is rel.stmt:
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    94
            return rel
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
    95
        principal = rel
3815
50b87f759b5d test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3694
diff changeset
    96
    if principal is None:
7707
936530f8d32c [sql gen] handle optional on comparison node (eg HAVING expression) and on rhs of final relation. Closes #1859609
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7651
diff changeset
    97
        if others:
936530f8d32c [sql gen] handle optional on comparison node (eg HAVING expression) and on rhs of final relation. Closes #1859609
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7651
diff changeset
    98
            return others[0]
3815
50b87f759b5d test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3694
diff changeset
    99
        raise BadRQLQuery('unable to find principal in %s' % ', '.join(
50b87f759b5d test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3694
diff changeset
   100
            r.as_string() for r in relations))
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   101
    return principal
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   102
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   103
438
69b79faefa94 need_intersect test and fixes
sylvain.thenault@logilab.fr
parents: 340
diff changeset
   104
def set_qdata(getrschema, union, noinvariant):
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   105
    """recursive function to set querier data on variables in the syntax tree
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   106
    """
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   107
    for select in union.children:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   108
        for subquery in select.with_:
438
69b79faefa94 need_intersect test and fixes
sylvain.thenault@logilab.fr
parents: 340
diff changeset
   109
            set_qdata(getrschema, subquery.query, noinvariant)
10663
54b8a1f249fb [py3k] dict.itervalues → dict.values
Rémi Cardona <remi.cardona@logilab.fr>
parents: 10662
diff changeset
   110
        for var in select.defined_vars.values():
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   111
            if var.stinfo['invariant']:
11770
22b854d3e8b2 [rql2sql] Stop generating SQL query from RQL using entities.type
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11703
diff changeset
   112
                if var in noinvariant:
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   113
                    var._q_invariant = False
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   114
                else:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   115
                    var._q_invariant = True
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   116
            else:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   117
                var._q_invariant = False
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   118
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   119
12885
194e9ae964ed [server.rqlannotation] rename SQLGenAnnotator to RQLAnnotator
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12567
diff changeset
   120
class RQLAnnotator(object):
194e9ae964ed [server.rqlannotation] rename SQLGenAnnotator to RQLAnnotator
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12567
diff changeset
   121
    """Annotate the RQL abstract syntax tree to inform the SQL generation"""
12509
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   122
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   123
    def __init__(self, schema):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   124
        self.schema = schema
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   125
        self.nfdomain = frozenset(eschema.type for eschema in schema.entities()
3689
deb13e88e037 follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3249
diff changeset
   126
                                  if not eschema.final)
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   127
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   128
    def annotate(self, rqlst):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   129
        """add information to the rql syntax tree to help sources to do their
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   130
        job (read sql generation)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   131
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   132
        a variable is tagged as invariant if:
12509
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   133
        * it is a non final variable
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   134
        * it is not used as lhs in any final or inlined relation
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   135
        * there is no type restriction on this variable (either explicit in the
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   136
          syntax tree or because a solution for this variable has been removed
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   137
          due to security filtering)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   138
        """
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   139
        # assert rqlst.TYPE == 'select', rqlst
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   140
        rqlst.has_text_query = self._annotate_union(rqlst)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   141
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   142
    def _annotate_union(self, union):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   143
        has_text_query = False
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   144
        for select in union.children:
12509
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   145
            if self._annotate_select(select):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   146
                has_text_query = True
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   147
        return has_text_query
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   148
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   149
    def _annotate_select(self, rqlst):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   150
        has_text_query = False
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   151
        for subquery in rqlst.with_:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   152
            if self._annotate_union(subquery.query):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   153
                has_text_query = True
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   154
        getrschema = self.schema.rschema
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   155
        for var in rqlst.defined_vars.values():
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   156
            stinfo = var.stinfo
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   157
            if stinfo.get('ftirels'):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   158
                has_text_query = True
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   159
            if stinfo['attrvar']:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   160
                stinfo['invariant'] = False
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   161
                stinfo['principal'] = _select_main_var(stinfo['rhsrelations'])
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   162
                continue
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   163
            if stinfo['typerel'] is None:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   164
                # those particular queries should be executed using the system
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   165
                # entities table unless there is some type restriction
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   166
                if not stinfo['relations']:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   167
                    # Any X, Any MAX(X)...
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   168
                    stinfo['invariant'] = True
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   169
                    stinfo['principal'] = None
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   170
                    continue
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   171
                if (any(rel for rel in stinfo['relations']
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   172
                        if rel.r_type == 'eid' and rel.operator() != '=')
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   173
                        and not any(r for r in var.stinfo['relations'] - var.stinfo['rhsrelations']
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   174
                                    if r.r_type != 'eid'
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   175
                                    and (getrschema(r.r_type).inlined
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   176
                                         or getrschema(r.r_type).final))):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   177
                    # Any X WHERE X eid > 2
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   178
                    stinfo['invariant'] = True
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   179
                    stinfo['principal'] = None
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   180
                    continue
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   181
            if stinfo['selected'] and var.valuable_references() == 1 + bool(stinfo['constnode']):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   182
                # "Any X", "Any X, Y WHERE X attr Y"
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   183
                stinfo['invariant'] = False
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   184
                continue
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   185
            joins = set()
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   186
            invariant = False
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   187
            for ref in var.references():
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   188
                rel = ref.relation()
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   189
                if rel is None or rel.is_types_restriction():
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   190
                    continue
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   191
                lhs, rhs = rel.get_parts()
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   192
                onlhs = ref is lhs
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   193
                role = 'subject' if onlhs else 'object'
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   194
                if rel.r_type == 'eid':
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   195
                    if not (onlhs and len(stinfo['relations']) > 1):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   196
                        break
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   197
                    if not stinfo['constnode']:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   198
                        joins.add((rel, role))
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   199
                    continue
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   200
                elif rel.r_type == 'identity':
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   201
                    # identity can't be used as principal, so check other relation are used
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   202
                    # XXX explain rhs.operator == '='
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   203
                    if rhs.operator != '=' or len(stinfo['relations']) <= 1:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   204
                        break
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   205
                    joins.add((rel, role))
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   206
                    continue
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   207
                rschema = getrschema(rel.r_type)
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   208
                if rel.optional:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   209
                    if rel in stinfo.get('optrelations', ()):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   210
                        # optional variable can't be invariant if this is the lhs
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   211
                        # variable of an inlined relation
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   212
                        if rel not in stinfo['rhsrelations'] and rschema.inlined:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   213
                            break
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   214
                    # variable used as main variable of an optional relation can't
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   215
                    # be invariant, unless we can use some other relation as
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   216
                    # reference for the outer join
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   217
                    elif not stinfo['constnode']:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   218
                        break
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   219
                    elif len(stinfo['relations']) == 2:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   220
                        if onlhs:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   221
                            ostinfo = rhs.children[0].variable.stinfo
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   222
                        else:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   223
                            ostinfo = lhs.variable.stinfo
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   224
                        if not (ostinfo.get('optcomparisons')
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   225
                                or any(orel for orel in ostinfo['relations']
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   226
                                       if orel.optional and orel is not rel)):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   227
                            break
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   228
                if rschema.final or (onlhs and rschema.inlined):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   229
                    if rschema.type != 'has_text':
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   230
                        # need join anyway if the variable appears in a final or
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   231
                        # inlined relation
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   232
                        break
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   233
                    joins.add((rel, role))
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   234
                    continue
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   235
                if not stinfo['constnode']:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   236
                    if rschema.inlined and rel.neged(strict=True):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   237
                        # if relation is inlined, can't be invariant if that
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   238
                        # variable is used anywhere else.
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   239
                        # see 'Any P WHERE NOT N ecrit_par P, N eid 512':
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   240
                        # sql for 'NOT N ecrit_par P' is 'N.ecrit_par is NULL' so P
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   241
                        # can use N.ecrit_par as principal
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   242
                        if (stinfo['selected'] or len(stinfo['relations']) > 1):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   243
                            break
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   244
                joins.add((rel, role))
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   245
            else:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   246
                # if there is at least one ambigous relation and no other to
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   247
                # restrict types, can't be invariant since we need to filter out
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   248
                # other types
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   249
                if not self.is_ambiguous(var):
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   250
                    invariant = True
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   251
            stinfo['invariant'] = invariant
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   252
            if invariant and joins:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   253
                # remember rqlst/solutions analyze information
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   254
                # we have to select a kindof "main" relation which will "extrajoins"
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   255
                # the other
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   256
                # priority should be given to relation which are not in inner queries
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   257
                # (eg exists)
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   258
                try:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   259
                    stinfo['principal'] = principal = _select_principal(var.scope, joins)
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   260
                    if getrschema(principal.r_type).inlined:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   261
                        # the scope of the lhs variable must be equal or outer to the
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   262
                        # rhs variable's scope (since it's retrieved from lhs's table)
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   263
                        sstinfo = principal.children[0].variable.stinfo
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   264
                        sstinfo['scope'] = common_parent(sstinfo['scope'], stinfo['scope']).scope
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   265
                except CantSelectPrincipal:
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   266
                    stinfo['invariant'] = False
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   267
        # see unittest_rqlannotation. test_has_text_security_cache_bug
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   268
        # XXX probably more to do, but yet that work without more...
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   269
        for col_alias in rqlst.aliases.values():
db81a99e9dd1 [server.rqlannotation] refactor to make class SQLGenAnnotator consistent
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 12355
diff changeset
   270
            if col_alias.stinfo.get('ftirels'):
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   271
                has_text_query = True
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   272
        return has_text_query
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   273
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   274
    def is_ambiguous(self, var):
7523
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   275
        # ignore has_text relation when we know it will be used as principal.
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   276
        # This is expected by the rql2sql generator which will use the `entities`
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   277
        # table to filter out by type if necessary, This optimisation is very
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   278
        # interesting in multi-sources cases, as it may avoid a costly query
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   279
        # on sources to get all entities of a given type to achieve this, while
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   280
        # we have all the necessary information.
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   281
        root = var.stmt.root  # Union node
7523
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   282
        # rel.scope -> Select or Exists node, so add .parent to get Union from
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   283
        # Select node
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   284
        rels = [rel for rel in var.stinfo['relations'] if rel.scope.parent is root]
f6856231cc51 [rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 7357
diff changeset
   285
        if len(rels) == 1 and rels[0].r_type == 'has_text':
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   286
            return False
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   287
        try:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   288
            data = var.stmt._deamb_data
1802
d628defebc17 delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents: 1132
diff changeset
   289
        except AttributeError:
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   290
            data = var.stmt._deamb_data = IsAmbData(self.schema, self.nfdomain)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   291
            data.compute(var.stmt)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   292
        return data.is_ambiguous(var)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   293
1802
d628defebc17 delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents: 1132
diff changeset
   294
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   295
class IsAmbData(object):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   296
    def __init__(self, schema, nfdomain):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   297
        self.schema = schema
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   298
        # shortcuts
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   299
        self.rschema = schema.rschema
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   300
        self.eschema = schema.eschema
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   301
        # domain for non final variables
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   302
        self.nfdomain = nfdomain
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   303
        # {var: possible solutions set}
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   304
        self.varsols = {}
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   305
        # set of ambiguous variables
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   306
        self.ambiguousvars = set()
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   307
        # remember if a variable has been deambiguified by another to avoid
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   308
        # doing the opposite
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   309
        self.deambification_map = {}
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   310
        # not invariant variables (access to final.inlined relation)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   311
        self.not_invariants = set()
1802
d628defebc17 delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents: 1132
diff changeset
   312
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   313
    def is_ambiguous(self, var):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   314
        return var in self.ambiguousvars
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   315
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   316
    def restrict(self, var, restricted_domain):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   317
        self.varsols[var] &= restricted_domain
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   318
        if var in self.ambiguousvars and self.varsols[var] == var.stinfo['possibletypes']:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   319
            self.ambiguousvars.remove(var)
1802
d628defebc17 delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents: 1132
diff changeset
   320
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   321
    def compute(self, rqlst):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   322
        # set domains for each variable
10662
10942ed172de [py3k] dict.iteritems → dict.items
Rémi Cardona <remi.cardona@logilab.fr>
parents: 10589
diff changeset
   323
        for varname, var in rqlst.defined_vars.items():
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   324
            if (var.stinfo['uidrel'] is not None
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   325
                    or self.eschema(rqlst.solutions[0][varname]).final):
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   326
                ptypes = var.stinfo['possibletypes']
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   327
            else:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   328
                ptypes = set(self.nfdomain)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   329
                self.ambiguousvars.add(var)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   330
            self.varsols[var] = ptypes
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   331
        if not self.ambiguousvars:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   332
            return
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   333
        # apply relation restriction
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   334
        self.maydeambrels = maydeambrels = {}
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   335
        for rel in rqlst.iget_nodes(Relation):
4287
15499a46c009 cleanup
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4285
diff changeset
   336
            if rel.r_type == 'eid' or rel.is_types_restriction():
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   337
                continue
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   338
            lhs, rhs = rel.get_variable_parts()
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   339
            if isinstance(lhs, VariableRef) or isinstance(rhs, VariableRef):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   340
                rschema = self.rschema(rel.r_type)
3689
deb13e88e037 follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3249
diff changeset
   341
                if rschema.inlined or rschema.final:
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   342
                    self.not_invariants.add(lhs.variable)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   343
                self.set_rel_constraint(lhs, rel, rschema.subjects)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   344
                self.set_rel_constraint(rhs, rel, rschema.objects)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   345
        # try to deambiguify more variables by considering other variables'type
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   346
        modified = True
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   347
        while modified and self.ambiguousvars:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   348
            modified = False
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   349
            for var in self.ambiguousvars.copy():
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   350
                try:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   351
                    for rel in (var.stinfo['relations'] & maydeambrels[var]):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   352
                        if self.deambiguifying_relation(var, rel):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   353
                            modified = True
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   354
                            break
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   355
                except KeyError:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   356
                    # no relation to deambiguify
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   357
                    continue
1802
d628defebc17 delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents: 1132
diff changeset
   358
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   359
    def _debug_print(self):
10589
7c23b7de2b8d [py3k] print function
Samuel Trégouët <samuel.tregouet@logilab.fr>
parents: 9892
diff changeset
   360
        print('varsols', dict((x, sorted(str(v) for v in values))
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   361
                              for x, values in self.varsols.items()))
10589
7c23b7de2b8d [py3k] print function
Samuel Trégouët <samuel.tregouet@logilab.fr>
parents: 9892
diff changeset
   362
        print('ambiguous vars', sorted(self.ambiguousvars))
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   363
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   364
    def set_rel_constraint(self, term, rel, etypes_func):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   365
        if isinstance(term, VariableRef) and self.is_ambiguous(term.variable):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   366
            var = term.variable
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   367
            if (len(var.stinfo['relations']) == 1
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   368
                    or rel.scope is var.scope
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   369
                    or rel.r_type == 'identity'):
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   370
                self.restrict(var, frozenset(etypes_func()))
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   371
                try:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   372
                    self.maydeambrels[var].add(rel)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   373
                except KeyError:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   374
                    self.maydeambrels[var] = set((rel,))
1802
d628defebc17 delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents: 1132
diff changeset
   375
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   376
    def deambiguifying_relation(self, var, rel):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   377
        lhs, rhs = rel.get_variable_parts()
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   378
        onlhs = var is getattr(lhs, 'variable', None)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   379
        other = onlhs and rhs or lhs
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   380
        otheretypes = None
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   381
        # XXX isinstance(other.variable, Variable) to skip column alias
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   382
        if isinstance(other, VariableRef) and isinstance(other.variable, Variable):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   383
            deambiguifier = other.variable
11703
670aa9bf0b6c [flake8] cubicweb/server/rqlannotation.py is now flake8 friendly
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 11057
diff changeset
   384
            if var is not self.deambification_map.get(deambiguifier):
5004
4cc020ee70e2 le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4721
diff changeset
   385
                if var.stinfo['typerel'] is None:
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   386
                    otheretypes = deambiguifier.stinfo['possibletypes']
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   387
                elif not self.is_ambiguous(deambiguifier):
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   388
                    otheretypes = self.varsols[deambiguifier]
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   389
                elif deambiguifier in self.not_invariants:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   390
                    # we know variable won't be invariant, try to use
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   391
                    # it to deambguify the current variable
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   392
                    otheretypes = self.varsols[deambiguifier]
5004
4cc020ee70e2 le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4721
diff changeset
   393
            if deambiguifier.stinfo['typerel'] is None:
4285
ea590101691c don't record deambiguifier when it has no type restriction using 'is', so we don't miss later some available constraints
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
   394
                # if deambiguifier has no type restriction using 'is',
ea590101691c don't record deambiguifier when it has no type restriction using 'is', so we don't miss later some available constraints
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
   395
                # don't record it
ea590101691c don't record deambiguifier when it has no type restriction using 'is', so we don't miss later some available constraints
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4212
diff changeset
   396
                deambiguifier = None
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   397
        elif isinstance(other, Constant) and other.uidtype:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   398
            otheretypes = (other.uidtype,)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   399
            deambiguifier = None
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   400
        if otheretypes is not None:
967
aeeec5447eb0 fix #171339
sylvain.thenault@logilab.fr
parents: 599
diff changeset
   401
            # to restrict, we must check that for all type in othertypes,
aeeec5447eb0 fix #171339
sylvain.thenault@logilab.fr
parents: 599
diff changeset
   402
            # possible types on the other end of the relation are matching
aeeec5447eb0 fix #171339
sylvain.thenault@logilab.fr
parents: 599
diff changeset
   403
            # variable's possible types
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   404
            rschema = self.rschema(rel.r_type)
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   405
            if onlhs:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   406
                rtypefunc = rschema.subjects
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   407
            else:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   408
                rtypefunc = rschema.objects
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   409
            for otheretype in otheretypes:
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   410
                reltypes = frozenset(rtypefunc(otheretype))
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   411
                if var.stinfo['possibletypes'] != reltypes:
4287
15499a46c009 cleanup
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4285
diff changeset
   412
                    return False
15499a46c009 cleanup
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4285
diff changeset
   413
            self.restrict(var, var.stinfo['possibletypes'])
15499a46c009 cleanup
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4285
diff changeset
   414
            self.deambification_map[var] = deambiguifier
15499a46c009 cleanup
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 4285
diff changeset
   415
            return True
0
b97547f5f1fa Showtime !
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
diff changeset
   416
        return False