author | Aurelien Campeas <aurelien.campeas@logilab.fr> |
Thu, 17 Jun 2010 18:50:20 +0200 | |
branch | stable |
changeset 5783 | c5ff8cd74758 |
parent 5782 | 8ff48d1a319f |
child 5793 | 1faff41593df |
permissions | -rw-r--r-- |
5421
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
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:
5280
diff
changeset
|
17 |
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
0 | 18 |
"""RQL to SQL generator for native sources. |
19 |
||
20 |
||
21 |
SQL queries optimization |
|
22 |
~~~~~~~~~~~~~~~~~~~~~~~~ |
|
1398
5fe84a5f7035
rename internal entity types to have CW prefix instead of E
sylvain.thenault@logilab.fr
parents:
1263
diff
changeset
|
23 |
1. CWUser X WHERE X in_group G, G name 'users': |
0 | 24 |
|
1398
5fe84a5f7035
rename internal entity types to have CW prefix instead of E
sylvain.thenault@logilab.fr
parents:
1263
diff
changeset
|
25 |
CWUser is the only subject entity type for the in_group relation, |
0 | 26 |
which allow us to do :: |
27 |
||
1398
5fe84a5f7035
rename internal entity types to have CW prefix instead of E
sylvain.thenault@logilab.fr
parents:
1263
diff
changeset
|
28 |
SELECT eid_from FROM in_group, CWGroup |
5fe84a5f7035
rename internal entity types to have CW prefix instead of E
sylvain.thenault@logilab.fr
parents:
1263
diff
changeset
|
29 |
WHERE in_group.eid_to = CWGroup.eid_from |
5fe84a5f7035
rename internal entity types to have CW prefix instead of E
sylvain.thenault@logilab.fr
parents:
1263
diff
changeset
|
30 |
AND CWGroup.name = 'users' |
0 | 31 |
|
32 |
||
33 |
2. Any X WHERE X nonfinal1 Y, Y nonfinal2 Z |
|
34 |
||
35 |
-> direct join between nonfinal1 and nonfinal2, whatever X,Y, Z (unless |
|
36 |
inlined...) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
37 |
|
0 | 38 |
NOT IMPLEMENTED (and quite hard to implement) |
39 |
||
40 |
Potential optimization information is collected by the querier, sql generation |
|
41 |
is done according to this information |
|
42 |
||
5280
7e13bb484a19
added note about interesting page about SQL implementation differences
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
5013
diff
changeset
|
43 |
cross RDMS note : read `Comparison of different SQL implementations`_ |
7e13bb484a19
added note about interesting page about SQL implementation differences
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
5013
diff
changeset
|
44 |
by Troels Arvin. Features SQL ISO Standard, PG, mysql, Oracle, MS SQL, DB2 |
7e13bb484a19
added note about interesting page about SQL implementation differences
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
5013
diff
changeset
|
45 |
and Informix. |
7e13bb484a19
added note about interesting page about SQL implementation differences
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
5013
diff
changeset
|
46 |
|
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
|
47 |
.. _Comparison of different SQL implementations: http://www.troels.arvin.dk/db/rdbms |
5782
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
48 |
""" |
0 | 49 |
|
50 |
__docformat__ = "restructuredtext en" |
|
51 |
||
52 |
import threading |
|
53 |
||
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
54 |
from logilab.database import FunctionDescr, SQL_FUNCTIONS_REGISTRY |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
55 |
|
0 | 56 |
from rql import BadRQLQuery, CoercionError |
57 |
from rql.stmts import Union, Select |
|
5782
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
58 |
from rql.nodes import (SortTerm, VariableRef, Constant, Function, Variable, Or, |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
59 |
Not, Comparison, ColumnAlias, Relation, SubQuery, Exists) |
0 | 60 |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
61 |
from cubicweb import QueryError |
1251
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
62 |
from cubicweb.server.sqlutils import SQL_PREFIX |
0 | 63 |
from cubicweb.server.utils import cleanup_solutions |
64 |
||
4794
81075b09ebf1
[rql2sql] fix potential crash when testing _q_invariant on a ColumnAlias instead of a Variable by always considering _q_invariant to false for them
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4719
diff
changeset
|
65 |
ColumnAlias._q_invariant = False # avoid to check for ColumnAlias / Variable |
81075b09ebf1
[rql2sql] fix potential crash when testing _q_invariant on a ColumnAlias instead of a Variable by always considering _q_invariant to false for them
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4719
diff
changeset
|
66 |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
67 |
FunctionDescr.source_execute = None |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
68 |
|
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
69 |
def default_update_cb_stack(self, stack): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
70 |
stack.append(self.source_execute) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
71 |
FunctionDescr.update_cb_stack = default_update_cb_stack |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
72 |
|
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
73 |
LENGTH = SQL_FUNCTIONS_REGISTRY.get_function('LENGTH') |
5630
40d7c7e180f1
[storage] source's callback has a new prototype, update usage
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5593
diff
changeset
|
74 |
def length_source_execute(source, session, value): |
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
75 |
return len(value.getvalue()) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
76 |
LENGTH.source_execute = length_source_execute |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
77 |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
78 |
def _new_var(select, varname): |
0 | 79 |
newvar = select.get_variable(varname) |
80 |
if not 'relations' in newvar.stinfo: |
|
81 |
# not yet initialized |
|
82 |
newvar.prepare_annotation() |
|
83 |
newvar.stinfo['scope'] = select |
|
84 |
newvar._q_invariant = False |
|
85 |
return newvar |
|
86 |
||
87 |
def _fill_to_wrap_rel(var, newselect, towrap, schema): |
|
88 |
for rel in var.stinfo['relations'] - var.stinfo['rhsrelations']: |
|
89 |
rschema = schema.rschema(rel.r_type) |
|
90 |
if rschema.inlined: |
|
91 |
towrap.add( (var, rel) ) |
|
92 |
for vref in rel.children[1].iget_nodes(VariableRef): |
|
93 |
newivar = _new_var(newselect, vref.name) |
|
94 |
newselect.selection.append(VariableRef(newivar)) |
|
95 |
_fill_to_wrap_rel(vref.variable, newselect, towrap, schema) |
|
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3245
diff
changeset
|
96 |
elif rschema.final: |
0 | 97 |
towrap.add( (var, rel) ) |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
98 |
|
0 | 99 |
def rewrite_unstable_outer_join(select, solutions, unstable, schema): |
100 |
"""if some optional variables are unstable, they should be selected in a |
|
101 |
subquery. This function check this and rewrite the rql syntax tree if |
|
102 |
necessary (in place). Return a boolean telling if the tree has been modified |
|
103 |
""" |
|
104 |
torewrite = set() |
|
105 |
modified = False |
|
106 |
for varname in tuple(unstable): |
|
107 |
var = select.defined_vars[varname] |
|
5004
4cc020ee70e2
le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
108 |
if not var.stinfo.get('optrelations'): |
0 | 109 |
continue |
110 |
modified = True |
|
111 |
unstable.remove(varname) |
|
112 |
torewrite.add(var) |
|
113 |
newselect = Select() |
|
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
|
114 |
newselect.need_distinct = False |
0 | 115 |
myunion = Union() |
116 |
myunion.append(newselect) |
|
117 |
# extract aliases / selection |
|
118 |
newvar = _new_var(newselect, var.name) |
|
119 |
newselect.selection = [VariableRef(newvar)] |
|
120 |
for avar in select.defined_vars.itervalues(): |
|
121 |
if avar.stinfo['attrvar'] is var: |
|
122 |
newavar = _new_var(newselect, avar.name) |
|
123 |
newavar.stinfo['attrvar'] = newvar |
|
124 |
newselect.selection.append(VariableRef(newavar)) |
|
125 |
towrap_rels = set() |
|
126 |
_fill_to_wrap_rel(var, newselect, towrap_rels, schema) |
|
127 |
# extract relations |
|
128 |
for var, rel in towrap_rels: |
|
129 |
newrel = rel.copy(newselect) |
|
130 |
newselect.add_restriction(newrel) |
|
131 |
select.remove_node(rel) |
|
132 |
var.stinfo['relations'].remove(rel) |
|
133 |
newvar.stinfo['relations'].add(newrel) |
|
134 |
if rel.optional in ('left', 'both'): |
|
5004
4cc020ee70e2
le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
135 |
newvar.add_optional_relation(newrel) |
0 | 136 |
for vref in newrel.children[1].iget_nodes(VariableRef): |
137 |
var = vref.variable |
|
138 |
var.stinfo['relations'].add(newrel) |
|
139 |
var.stinfo['rhsrelations'].add(newrel) |
|
140 |
if rel.optional in ('right', 'both'): |
|
5004
4cc020ee70e2
le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
141 |
var.add_optional_relation(newrel) |
0 | 142 |
# extract subquery solutions |
3736
07196bda2456
take care to already existant solutions local variable
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3689
diff
changeset
|
143 |
mysolutions = [sol.copy() for sol in solutions] |
07196bda2456
take care to already existant solutions local variable
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3689
diff
changeset
|
144 |
cleanup_solutions(newselect, mysolutions) |
0 | 145 |
newselect.set_possible_types(solutions) |
146 |
# full sub-query |
|
147 |
aliases = [VariableRef(select.get_variable(avar.name, i)) |
|
148 |
for i, avar in enumerate(newselect.selection)] |
|
149 |
select.add_subquery(SubQuery(aliases, myunion), check=False) |
|
150 |
return modified |
|
151 |
||
152 |
def _new_solutions(rqlst, solutions): |
|
153 |
"""first filter out subqueries variables from solutions""" |
|
154 |
newsolutions = [] |
|
155 |
for origsol in solutions: |
|
156 |
asol = {} |
|
157 |
for vname in rqlst.defined_vars: |
|
158 |
asol[vname] = origsol[vname] |
|
159 |
if not asol in newsolutions: |
|
160 |
newsolutions.append(asol) |
|
161 |
return newsolutions |
|
162 |
||
163 |
def remove_unused_solutions(rqlst, solutions, varmap, schema): |
|
164 |
"""cleanup solutions: remove solutions where invariant variables are taking |
|
165 |
different types |
|
166 |
""" |
|
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
167 |
newsols = _new_solutions(rqlst, solutions) |
0 | 168 |
existssols = {} |
169 |
unstable = set() |
|
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
170 |
invariants = {} |
0 | 171 |
for vname, var in rqlst.defined_vars.iteritems(): |
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
172 |
vtype = newsols[0][vname] |
0 | 173 |
if var._q_invariant or vname in varmap: |
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
174 |
# remove invariant variable from solutions to remove duplicates |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
175 |
# later, then reinserting a type for the variable even later |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
176 |
for sol in newsols: |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
177 |
invariants.setdefault(id(sol), {})[vname] = sol.pop(vname) |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
178 |
elif var.scope is not rqlst: |
0 | 179 |
# move appart variables which are in a EXISTS scope and are variating |
180 |
try: |
|
181 |
thisexistssols, thisexistsvars = existssols[var.scope] |
|
182 |
except KeyError: |
|
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
183 |
thisexistssols = [newsols[0]] |
0 | 184 |
thisexistsvars = set() |
185 |
existssols[var.scope] = thisexistssols, thisexistsvars |
|
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
186 |
for i in xrange(len(newsols)-1, 0, -1): |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
187 |
if vtype != newsols[i][vname]: |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
188 |
thisexistssols.append(newsols.pop(i)) |
0 | 189 |
thisexistsvars.add(vname) |
190 |
else: |
|
191 |
# remember unstable variables |
|
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
192 |
for i in xrange(1, len(newsols)): |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
193 |
if vtype != newsols[i][vname]: |
0 | 194 |
unstable.add(vname) |
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
195 |
if invariants: |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
196 |
# filter out duplicates |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
197 |
newsols_ = [] |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
198 |
for sol in newsols: |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
199 |
if not sol in newsols_: |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
200 |
newsols_.append(sol) |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
201 |
newsols = newsols_ |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
202 |
# reinsert solutions for invariants |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
203 |
for sol in newsols: |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
204 |
for invvar, vartype in invariants[id(sol)].iteritems(): |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
205 |
sol[invvar] = vartype |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
206 |
for sol in existssols: |
3866
2783c166ad1a
fix potential key error
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3852
diff
changeset
|
207 |
try: |
2783c166ad1a
fix potential key error
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3852
diff
changeset
|
208 |
for invvar, vartype in invariants[id(sol)].iteritems(): |
2783c166ad1a
fix potential key error
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3852
diff
changeset
|
209 |
sol[invvar] = vartype |
2783c166ad1a
fix potential key error
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3852
diff
changeset
|
210 |
except KeyError: |
2783c166ad1a
fix potential key error
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3852
diff
changeset
|
211 |
continue |
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
212 |
if len(newsols) > 1: |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
213 |
if rewrite_unstable_outer_join(rqlst, newsols, unstable, schema): |
0 | 214 |
# remove variables extracted to subqueries from solutions |
3852
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
215 |
newsols = _new_solutions(rqlst, newsols) |
03121ca1f85e
test and fix case where remove_unsused_solutions remove some solutions that should be kept
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3815
diff
changeset
|
216 |
return newsols, existssols, unstable |
0 | 217 |
|
218 |
def relation_info(relation): |
|
219 |
lhs, rhs = relation.get_variable_parts() |
|
220 |
try: |
|
221 |
lhs = lhs.variable |
|
222 |
lhsconst = lhs.stinfo['constnode'] |
|
223 |
except AttributeError: |
|
224 |
lhsconst = lhs |
|
225 |
lhs = None |
|
226 |
except KeyError: |
|
227 |
lhsconst = None # ColumnAlias |
|
228 |
try: |
|
229 |
rhs = rhs.variable |
|
230 |
rhsconst = rhs.stinfo['constnode'] |
|
231 |
except AttributeError: |
|
232 |
rhsconst = rhs |
|
233 |
rhs = None |
|
234 |
except KeyError: |
|
235 |
rhsconst = None # ColumnAlias |
|
236 |
return lhs, lhsconst, rhs, rhsconst |
|
237 |
||
238 |
def switch_relation_field(sql, table=''): |
|
239 |
switchedsql = sql.replace(table + '.eid_from', '__eid_from__') |
|
240 |
switchedsql = switchedsql.replace(table + '.eid_to', |
|
241 |
table + '.eid_from') |
|
242 |
return switchedsql.replace('__eid_from__', table + '.eid_to') |
|
243 |
||
244 |
def sort_term_selection(sorts, selectedidx, rqlst, groups): |
|
245 |
# XXX beurk |
|
246 |
if isinstance(rqlst, list): |
|
247 |
def append(term): |
|
248 |
rqlst.append(term) |
|
249 |
else: |
|
250 |
def append(term): |
|
251 |
rqlst.selection.append(term.copy(rqlst)) |
|
252 |
for sortterm in sorts: |
|
253 |
term = sortterm.term |
|
254 |
if not isinstance(term, Constant) and not str(term) in selectedidx: |
|
255 |
selectedidx.append(str(term)) |
|
256 |
append(term) |
|
257 |
if groups: |
|
258 |
for vref in term.iget_nodes(VariableRef): |
|
259 |
if not vref in groups: |
|
260 |
groups.append(vref) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
261 |
|
3752
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
262 |
def fix_selection_and_group(rqlst, selectedidx, needwrap, selectsortterms, |
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
263 |
sorts, groups, having): |
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
264 |
if selectsortterms and sorts: |
0 | 265 |
sort_term_selection(sorts, selectedidx, rqlst, not needwrap and groups) |
3752
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
266 |
if sorts and groups: |
3754 | 267 |
# when a query is grouped, ensure sort terms are grouped as well |
3752
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
268 |
for sortterm in sorts: |
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
269 |
term = sortterm.term |
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
270 |
if not isinstance(term, Constant): |
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
271 |
for vref in term.iget_nodes(VariableRef): |
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
272 |
if not vref in groups: |
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
273 |
groups.append(vref) |
0 | 274 |
if needwrap: |
275 |
if groups: |
|
276 |
for vref in groups: |
|
277 |
if not vref.name in selectedidx: |
|
278 |
selectedidx.append(vref.name) |
|
279 |
rqlst.selection.append(vref) |
|
280 |
if having: |
|
281 |
for term in having: |
|
282 |
for vref in term.iget_nodes(VariableRef): |
|
283 |
if not vref.name in selectedidx: |
|
284 |
selectedidx.append(vref.name) |
|
285 |
rqlst.selection.append(vref) |
|
286 |
||
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
287 |
def iter_mapped_var_sels(stmt, variable): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
288 |
# variable is a Variable or ColumnAlias node mapped to a source side |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
289 |
# callback |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
290 |
if not (len(variable.stinfo['rhsrelations']) <= 1 and # < 1 on column alias |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
291 |
variable.stinfo['selected']): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
292 |
raise QueryError("can't use %s as a restriction variable" |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
293 |
% variable.name) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
294 |
for selectidx in variable.stinfo['selected']: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
295 |
vrefs = stmt.selection[selectidx].get_nodes(VariableRef) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
296 |
if len(vrefs) != 1: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
297 |
raise QueryError() |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
298 |
yield selectidx, vrefs[0] |
0 | 299 |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
300 |
def update_source_cb_stack(state, stmt, node, stack): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
301 |
while True: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
302 |
node = node.parent |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
303 |
if node is stmt: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
304 |
break |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
305 |
if not isinstance(node, Function): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
306 |
raise QueryError() |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
307 |
func = SQL_FUNCTIONS_REGISTRY.get_function(node.name) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
308 |
if func.source_execute is None: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
309 |
raise QueryError('%s can not be called on mapped attribute' |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
310 |
% node.name) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
311 |
state.source_cb_funcs.add(node) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
312 |
func.update_cb_stack(stack) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
313 |
|
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
314 |
|
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
315 |
# IGenerator implementation for RQL->SQL ####################################### |
0 | 316 |
|
317 |
class StateInfo(object): |
|
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
|
318 |
def __init__(self, select, existssols, unstablevars): |
0 | 319 |
self.existssols = existssols |
320 |
self.unstablevars = unstablevars |
|
321 |
self.subtables = {} |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
322 |
self.needs_source_cb = None |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
323 |
self.subquery_source_cb = None |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
324 |
self.source_cb_funcs = set() |
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
|
325 |
self.scopes = {select: 0} |
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
|
326 |
self.scope_nodes = [] |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
327 |
|
0 | 328 |
def reset(self, solution): |
329 |
"""reset some visit variables""" |
|
330 |
self.solution = solution |
|
331 |
self.count = 0 |
|
332 |
self.done = set() |
|
333 |
self.tables = self.subtables.copy() |
|
334 |
self.actual_tables = [[]] |
|
335 |
for _, tsql in self.tables.itervalues(): |
|
336 |
self.actual_tables[-1].append(tsql) |
|
337 |
self.outer_tables = {} |
|
338 |
self.duplicate_switches = [] |
|
339 |
self.aliases = {} |
|
340 |
self.restrictions = [] |
|
341 |
self._restr_stack = [] |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
342 |
self.ignore_varmap = False |
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
343 |
self._needs_source_cb = {} |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
344 |
|
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
345 |
def merge_source_cbs(self, needs_source_cb): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
346 |
if self.needs_source_cb is None: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
347 |
self.needs_source_cb = needs_source_cb |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
348 |
elif needs_source_cb != self.needs_source_cb: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
349 |
raise QueryError('query fetch some source mapped attribute, some not') |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
350 |
|
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
351 |
def finalize_source_cbs(self): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
352 |
if self.subquery_source_cb is not None: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
353 |
self.needs_source_cb.update(self.subquery_source_cb) |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
354 |
|
0 | 355 |
def add_restriction(self, restr): |
356 |
if restr: |
|
357 |
self.restrictions.append(restr) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
358 |
|
0 | 359 |
def iter_exists_sols(self, exists): |
360 |
if not exists in self.existssols: |
|
361 |
yield 1 |
|
362 |
return |
|
363 |
thisexistssols, thisexistsvars = self.existssols[exists] |
|
364 |
origsol = self.solution |
|
365 |
origtables = self.tables |
|
366 |
done = self.done |
|
367 |
for thisexistssol in thisexistssols: |
|
368 |
for vname in self.unstablevars: |
|
369 |
if thisexistssol[vname] != origsol[vname] and vname in thisexistsvars: |
|
370 |
break |
|
371 |
else: |
|
372 |
self.tables = origtables.copy() |
|
373 |
self.solution = thisexistssol |
|
374 |
yield 1 |
|
375 |
# cleanup self.done from stuff specific to exists |
|
376 |
for var in thisexistsvars: |
|
377 |
if var in done: |
|
378 |
done.remove(var) |
|
379 |
for rel in exists.iget_nodes(Relation): |
|
380 |
if rel in done: |
|
381 |
done.remove(rel) |
|
382 |
self.solution = origsol |
|
383 |
self.tables = origtables |
|
384 |
||
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
|
385 |
def push_scope(self, scope_node): |
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
|
386 |
self.scope_nodes.append(scope_node) |
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
|
387 |
self.scopes[scope_node] = len(self.actual_tables) |
0 | 388 |
self.actual_tables.append([]) |
389 |
self._restr_stack.append(self.restrictions) |
|
390 |
self.restrictions = [] |
|
391 |
||
392 |
def pop_scope(self): |
|
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
|
393 |
del self.scopes[self.scope_nodes[-1]] |
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
|
394 |
self.scope_nodes.pop() |
0 | 395 |
restrictions = self.restrictions |
396 |
self.restrictions = self._restr_stack.pop() |
|
397 |
return restrictions, self.actual_tables.pop() |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
398 |
|
5782
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
399 |
def extract_fake_having_terms(having): |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
400 |
"""RQL's HAVING may be used to contains stuff that should go in the WHERE |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
401 |
clause of the SQL query, due to RQL grammar limitation. Split them... |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
402 |
|
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
403 |
Return a list nodes that can be ANDed with query's WHERE clause. Having |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
404 |
subtrees updated in place. |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
405 |
""" |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
406 |
fakehaving = [] |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
407 |
for subtree in having: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
408 |
ors, tocheck = set(), [] |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
409 |
for compnode in subtree.get_nodes(Comparison): |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
410 |
for fnode in compnode.get_nodes(Function): |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
411 |
if fnode.descr().aggregat: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
412 |
p = compnode.parent |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
413 |
oor = None |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
414 |
while not isinstance(p, Select): |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
415 |
if isinstance(p, Or): |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
416 |
oor = p |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
417 |
p = p.parent |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
418 |
if oor is not None: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
419 |
ors.add(oor) |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
420 |
break |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
421 |
else: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
422 |
tocheck.append(compnode) |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
423 |
# tocheck hold a set of comparison not implying an aggregat function |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
424 |
# put them in fakehaving if the don't share an Or node as ancestor |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
425 |
# with another comparison containing an aggregat function |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
426 |
for compnode in tocheck: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
427 |
parents = set() |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
428 |
p = compnode.parent |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
429 |
oor = None |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
430 |
while not isinstance(p, Select): |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
431 |
if p in ors: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
432 |
break |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
433 |
if isinstance(p, Or): |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
434 |
oor = p |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
435 |
p = p.parent |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
436 |
else: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
437 |
node = oor or compnode |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
438 |
if not node in fakehaving: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
439 |
fakehaving.append(node) |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
440 |
compnode.parent.remove(node) |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
441 |
return fakehaving |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
442 |
|
0 | 443 |
class SQLGenerator(object): |
444 |
""" |
|
445 |
generation of SQL from the fully expanded RQL syntax tree |
|
446 |
SQL is designed to be used with a CubicWeb SQL schema |
|
447 |
||
448 |
Groups and sort are not handled here since they should not be handled at |
|
449 |
this level (see cubicweb.server.querier) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
450 |
|
0 | 451 |
we should not have errors here ! |
452 |
||
453 |
WARNING: a CubicWebSQLGenerator instance is not thread safe, but generate is |
|
454 |
protected by a lock |
|
455 |
""" |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
456 |
|
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
457 |
def __init__(self, schema, dbhelper, attrmap=None): |
0 | 458 |
self.schema = schema |
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
459 |
self.dbhelper = dbhelper |
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
460 |
self.dbencoding = dbhelper.dbencoding |
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
461 |
self.keyword_map = {'NOW' : self.dbhelper.sql_current_timestamp, |
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
462 |
'TODAY': self.dbhelper.sql_current_date, |
0 | 463 |
} |
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
464 |
if not self.dbhelper.union_parentheses_support: |
0 | 465 |
self.union_sql = self.noparen_union_sql |
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
466 |
if self.dbhelper.fti_need_distinct: |
4836
3e3c4917e94e
[sql generation] fti_need_distinct quere was not anymore used, potentially causing duplicates on has_text queries with sqlite backend
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4794
diff
changeset
|
467 |
self.__union_sql = self.union_sql |
3e3c4917e94e
[sql generation] fti_need_distinct quere was not anymore used, potentially causing duplicates on has_text queries with sqlite backend
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4794
diff
changeset
|
468 |
self.union_sql = self.has_text_need_distinct_union_sql |
0 | 469 |
self._lock = threading.Lock() |
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
470 |
if attrmap is None: |
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
471 |
attrmap = {} |
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
472 |
self.attr_map = attrmap |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
473 |
|
0 | 474 |
def generate(self, union, args=None, varmap=None): |
475 |
"""return SQL queries and a variable dictionnary from a RQL syntax tree |
|
476 |
||
477 |
:partrqls: a list of couple (rqlst, solutions) |
|
478 |
:args: optional dictionary with values of substitutions used in the query |
|
479 |
:varmap: optional dictionary mapping variable name to a special table |
|
480 |
name, in case the query as to fetch data from temporary tables |
|
481 |
||
482 |
return an sql string and a dictionary with substitutions values |
|
483 |
""" |
|
484 |
if args is None: |
|
485 |
args = {} |
|
486 |
if varmap is None: |
|
487 |
varmap = {} |
|
488 |
self._lock.acquire() |
|
489 |
self._args = args |
|
490 |
self._varmap = varmap |
|
491 |
self._query_attrs = {} |
|
492 |
self._state = None |
|
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
|
493 |
# self._not_scope_offset = 0 |
0 | 494 |
try: |
495 |
# union query for each rqlst / solution |
|
496 |
sql = self.union_sql(union) |
|
497 |
# we are done |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
498 |
return sql, self._query_attrs, self._state.needs_source_cb |
0 | 499 |
finally: |
500 |
self._lock.release() |
|
501 |
||
4836
3e3c4917e94e
[sql generation] fti_need_distinct quere was not anymore used, potentially causing duplicates on has_text queries with sqlite backend
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4794
diff
changeset
|
502 |
def has_text_need_distinct_union_sql(self, union, needalias=False): |
3e3c4917e94e
[sql generation] fti_need_distinct quere was not anymore used, potentially causing duplicates on has_text queries with sqlite backend
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4794
diff
changeset
|
503 |
if getattr(union, 'has_text_query', False): |
3e3c4917e94e
[sql generation] fti_need_distinct quere was not anymore used, potentially causing duplicates on has_text queries with sqlite backend
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4794
diff
changeset
|
504 |
for select in union.children: |
3e3c4917e94e
[sql generation] fti_need_distinct quere was not anymore used, potentially causing duplicates on has_text queries with sqlite backend
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4794
diff
changeset
|
505 |
select.need_distinct = True |
3e3c4917e94e
[sql generation] fti_need_distinct quere was not anymore used, potentially causing duplicates on has_text queries with sqlite backend
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4794
diff
changeset
|
506 |
return self.__union_sql(union, needalias) |
3e3c4917e94e
[sql generation] fti_need_distinct quere was not anymore used, potentially causing duplicates on has_text queries with sqlite backend
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4794
diff
changeset
|
507 |
|
0 | 508 |
def union_sql(self, union, needalias=False): # pylint: disable-msg=E0202 |
509 |
if len(union.children) == 1: |
|
510 |
return self.select_sql(union.children[0], needalias) |
|
511 |
sqls = ('(%s)' % self.select_sql(select, needalias) |
|
512 |
for select in union.children) |
|
513 |
return '\nUNION ALL\n'.join(sqls) |
|
514 |
||
515 |
def noparen_union_sql(self, union, needalias=False): |
|
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
516 |
# needed for sqlite backend which doesn't like parentheses around union |
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
517 |
# query. This may cause bug in some condition (sort in one of the |
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
518 |
# subquery) but will work in most case |
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
519 |
# |
0 | 520 |
# see http://www.sqlite.org/cvstrac/tktview?tn=3074 |
521 |
sqls = (self.select_sql(select, needalias) |
|
522 |
for i, select in enumerate(union.children)) |
|
523 |
return '\nUNION ALL\n'.join(sqls) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
524 |
|
0 | 525 |
def select_sql(self, select, needalias=False): |
526 |
"""return SQL queries and a variable dictionnary from a RQL syntax tree |
|
527 |
||
528 |
:select: a selection statement of the syntax tree (`rql.stmts.Select`) |
|
529 |
:solution: a dictionnary containing variables binding. |
|
530 |
A solution's dictionnary has variable's names as key and variable's |
|
531 |
types as values |
|
532 |
:needwrap: boolean telling if the query will be wrapped in an outer |
|
533 |
query (to deal with aggregat and/or grouping) |
|
534 |
""" |
|
535 |
distinct = selectsortterms = select.need_distinct |
|
536 |
sorts = select.orderby |
|
537 |
groups = select.groupby |
|
538 |
having = select.having |
|
5782
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
539 |
morerestr = extract_fake_having_terms(having) |
0 | 540 |
# remember selection, it may be changed and have to be restored |
541 |
origselection = select.selection[:] |
|
542 |
# check if the query will have union subquery, if it need sort term |
|
543 |
# selection (union or distinct query) and wrapping (union with groups) |
|
544 |
needwrap = False |
|
545 |
sols = select.solutions |
|
546 |
if len(sols) > 1: |
|
547 |
# remove invariant from solutions |
|
548 |
sols, existssols, unstable = remove_unused_solutions( |
|
549 |
select, sols, self._varmap, self.schema) |
|
550 |
if len(sols) > 1: |
|
551 |
# if there is still more than one solution, a UNION will be |
|
552 |
# generated and so sort terms have to be selected |
|
553 |
selectsortterms = True |
|
554 |
# and if select is using group by or aggregat, a wrapping |
|
555 |
# query will be necessary |
|
556 |
if groups or select.has_aggregat: |
|
557 |
select.select_only_variables() |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
558 |
needwrap = True |
0 | 559 |
else: |
560 |
existssols, unstable = {}, () |
|
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
|
561 |
state = StateInfo(select, existssols, unstable) |
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
562 |
if self._state is not None: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
563 |
# state from a previous unioned select |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
564 |
state.merge_source_cbs(self._state.needs_source_cb) |
0 | 565 |
# treat subqueries |
566 |
self._subqueries_sql(select, state) |
|
567 |
# generate sql for this select node |
|
568 |
selectidx = [str(term) for term in select.selection] |
|
569 |
if needwrap: |
|
570 |
outerselection = origselection[:] |
|
571 |
if sorts and selectsortterms: |
|
572 |
outerselectidx = [str(term) for term in outerselection] |
|
573 |
if distinct: |
|
574 |
sort_term_selection(sorts, outerselectidx, |
|
575 |
outerselection, groups) |
|
576 |
else: |
|
577 |
outerselectidx = selectidx[:] |
|
3752
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
578 |
fix_selection_and_group(select, selectidx, needwrap, |
4c77a1653374
when a query is grouped, ensure sort terms are grouped as well
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3736
diff
changeset
|
579 |
selectsortterms, sorts, groups, having) |
0 | 580 |
if needwrap: |
581 |
fselectidx = outerselectidx |
|
582 |
fneedwrap = len(outerselection) != len(origselection) |
|
583 |
else: |
|
584 |
fselectidx = selectidx |
|
585 |
fneedwrap = len(select.selection) != len(origselection) |
|
586 |
if fneedwrap: |
|
587 |
needalias = True |
|
588 |
self._in_wrapping_query = False |
|
589 |
self._state = state |
|
590 |
try: |
|
5782
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
591 |
sql = self._solutions_sql(select, morerestr, sols, distinct, |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
592 |
needalias or needwrap) |
0 | 593 |
# generate groups / having before wrapping query selection to |
594 |
# get correct column aliases |
|
595 |
self._in_wrapping_query = needwrap |
|
596 |
if groups: |
|
597 |
# no constant should be inserted in GROUP BY else the backend will |
|
598 |
# interpret it as a positional index in the selection |
|
599 |
groups = ','.join(vref.accept(self) for vref in groups |
|
600 |
if not isinstance(vref, Constant)) |
|
601 |
if having: |
|
602 |
# filter out constants as for GROUP BY |
|
5662
785837baabba
HAVING sql clause don't know about comma...
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5635
diff
changeset
|
603 |
having = ' AND '.join(term.accept(self) for term in having |
785837baabba
HAVING sql clause don't know about comma...
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5635
diff
changeset
|
604 |
if not isinstance(term, Constant)) |
0 | 605 |
if needwrap: |
606 |
sql = '%s FROM (%s) AS T1' % (self._selection_sql(outerselection, distinct, |
|
607 |
needalias), |
|
608 |
sql) |
|
609 |
if groups: |
|
610 |
sql += '\nGROUP BY %s' % groups |
|
611 |
if having: |
|
612 |
sql += '\nHAVING %s' % having |
|
613 |
# sort |
|
614 |
if sorts: |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
615 |
sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm, |
0 | 616 |
fselectidx) |
617 |
for sortterm in sorts) |
|
618 |
if fneedwrap: |
|
619 |
selection = ['T1.C%s' % i for i in xrange(len(origselection))] |
|
620 |
sql = 'SELECT %s FROM (%s) AS T1' % (','.join(selection), sql) |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
621 |
state.finalize_source_cbs() |
0 | 622 |
finally: |
623 |
select.selection = origselection |
|
624 |
# limit / offset |
|
625 |
limit = select.limit |
|
626 |
if limit: |
|
627 |
sql += '\nLIMIT %s' % limit |
|
628 |
offset = select.offset |
|
629 |
if offset: |
|
630 |
sql += '\nOFFSET %s' % offset |
|
631 |
return sql |
|
632 |
||
633 |
def _subqueries_sql(self, select, state): |
|
634 |
for i, subquery in enumerate(select.with_): |
|
635 |
sql = self.union_sql(subquery.query, needalias=True) |
|
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
636 |
tablealias = '_T%s' % i # XXX nested subqueries |
0 | 637 |
sql = '(%s) AS %s' % (sql, tablealias) |
638 |
state.subtables[tablealias] = (0, sql) |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
639 |
latest_state = self._state |
0 | 640 |
for vref in subquery.aliases: |
641 |
alias = vref.variable |
|
642 |
alias._q_sqltable = tablealias |
|
643 |
alias._q_sql = '%s.C%s' % (tablealias, alias.colnum) |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
644 |
try: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
645 |
stack = latest_state.needs_source_cb[alias.colnum] |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
646 |
if state.subquery_source_cb is None: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
647 |
state.subquery_source_cb = {} |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
648 |
for selectidx, vref in iter_mapped_var_sels(select, alias): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
649 |
stack = stack[:] |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
650 |
update_source_cb_stack(state, select, vref, stack) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
651 |
state.subquery_source_cb[selectidx] = stack |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
652 |
except KeyError: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
653 |
continue |
0 | 654 |
|
5782
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
655 |
def _solutions_sql(self, select, morerestr, solutions, distinct, needalias): |
0 | 656 |
sqls = [] |
657 |
for solution in solutions: |
|
658 |
self._state.reset(solution) |
|
659 |
# visit restriction subtree |
|
660 |
if select.where is not None: |
|
661 |
self._state.add_restriction(select.where.accept(self)) |
|
5782
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
662 |
for restriction in morerestr: |
8ff48d1a319f
[rql2sql] when using HAVING to by-pass rql limitation (not to filter on result of an aggregat function), we should emit SQL that doesn't use HAVING to avoid potential backend error because variables are not grouped. Closes #1061603.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5706
diff
changeset
|
663 |
self._state.add_restriction(restriction.accept(self)) |
0 | 664 |
sql = [self._selection_sql(select.selection, distinct, needalias)] |
665 |
if self._state.restrictions: |
|
666 |
sql.append('WHERE %s' % ' AND '.join(self._state.restrictions)) |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
667 |
self._state.merge_source_cbs(self._state._needs_source_cb) |
0 | 668 |
# add required tables |
669 |
assert len(self._state.actual_tables) == 1, self._state.actual_tables |
|
670 |
tables = self._state.actual_tables[-1] |
|
671 |
if tables: |
|
672 |
# sort for test predictability |
|
673 |
sql.insert(1, 'FROM %s' % ', '.join(sorted(tables))) |
|
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
674 |
elif self._state.restrictions and self.dbhelper.needs_from_clause: |
0 | 675 |
sql.insert(1, 'FROM (SELECT 1) AS _T') |
676 |
sqls.append('\n'.join(sql)) |
|
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
|
677 |
if distinct: |
0 | 678 |
return '\nUNION\n'.join(sqls) |
679 |
else: |
|
680 |
return '\nUNION ALL\n'.join(sqls) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
681 |
|
0 | 682 |
def _selection_sql(self, selected, distinct, needaliasing=False): |
683 |
clause = [] |
|
684 |
for term in selected: |
|
685 |
sql = term.accept(self) |
|
686 |
if needaliasing: |
|
687 |
colalias = 'C%s' % len(clause) |
|
688 |
clause.append('%s AS %s' % (sql, colalias)) |
|
689 |
if isinstance(term, VariableRef): |
|
690 |
self._state.aliases[term.name] = colalias |
|
691 |
else: |
|
692 |
clause.append(sql) |
|
693 |
if distinct: |
|
694 |
return 'SELECT DISTINCT %s' % ', '.join(clause) |
|
695 |
return 'SELECT %s' % ', '.join(clause) |
|
696 |
||
697 |
def _sortterm_sql(self, sortterm, selectidx): |
|
698 |
term = sortterm.term |
|
699 |
try: |
|
700 |
sqlterm = str(selectidx.index(str(term)) + 1) |
|
701 |
except ValueError: |
|
702 |
# Constant node or non selected term |
|
703 |
sqlterm = str(term.accept(self)) |
|
704 |
if sortterm.asc: |
|
705 |
return sqlterm |
|
706 |
else: |
|
707 |
return '%s DESC' % sqlterm |
|
708 |
||
709 |
def visit_and(self, et): |
|
710 |
"""generate SQL for a AND subtree""" |
|
711 |
res = [] |
|
712 |
for c in et.children: |
|
713 |
part = c.accept(self) |
|
714 |
if part: |
|
715 |
res.append(part) |
|
716 |
return ' AND '.join(res) |
|
717 |
||
718 |
def visit_or(self, ou): |
|
719 |
"""generate SQL for a OR subtree""" |
|
720 |
res = [] |
|
721 |
for c in ou.children: |
|
722 |
part = c.accept(self) |
|
723 |
if part: |
|
724 |
res.append('(%s)' % part) |
|
725 |
if res: |
|
726 |
if len(res) > 1: |
|
727 |
return '(%s)' % ' OR '.join(res) |
|
728 |
return res[0] |
|
729 |
return '' |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
730 |
|
0 | 731 |
def visit_not(self, node): |
732 |
csql = node.children[0].accept(self) |
|
733 |
if node in self._state.done or not csql: |
|
734 |
# already processed or no sql generated by children |
|
735 |
return csql |
|
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
|
736 |
return 'NOT (%s)' % csql |
0 | 737 |
|
738 |
def visit_exists(self, exists): |
|
739 |
"""generate SQL name for a exists subquery""" |
|
740 |
sqls = [] |
|
741 |
for dummy in self._state.iter_exists_sols(exists): |
|
742 |
sql = self._visit_exists(exists) |
|
743 |
if sql: |
|
744 |
sqls.append(sql) |
|
745 |
if not sqls: |
|
746 |
return '' |
|
747 |
return 'EXISTS(%s)' % ' UNION '.join(sqls) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
748 |
|
0 | 749 |
def _visit_exists(self, exists): |
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
|
750 |
self._state.push_scope(exists) |
0 | 751 |
restriction = exists.children[0].accept(self) |
752 |
restrictions, tables = self._state.pop_scope() |
|
753 |
if restriction: |
|
754 |
restrictions.append(restriction) |
|
755 |
restriction = ' AND '.join(restrictions) |
|
756 |
if not restriction: |
|
757 |
return '' |
|
758 |
if not tables: |
|
759 |
# XXX could leave surrounding EXISTS() in this case no? |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
760 |
sql = 'SELECT 1 WHERE %s' % restriction |
0 | 761 |
else: |
762 |
sql = 'SELECT 1 FROM %s WHERE %s' % (', '.join(tables), restriction) |
|
763 |
return sql |
|
764 |
||
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
765 |
|
0 | 766 |
def visit_relation(self, relation): |
767 |
"""generate SQL for a relation""" |
|
768 |
rtype = relation.r_type |
|
769 |
# don't care of type constraint statement (i.e. relation_type = 'is') |
|
770 |
if relation.is_types_restriction(): |
|
771 |
return '' |
|
772 |
lhs, rhs = relation.get_parts() |
|
773 |
rschema = self.schema.rschema(rtype) |
|
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3245
diff
changeset
|
774 |
if rschema.final: |
0 | 775 |
if rtype == 'eid' and lhs.variable._q_invariant and \ |
776 |
lhs.variable.stinfo['constnode']: |
|
777 |
# special case where this restriction is already generated by |
|
778 |
# some other relation |
|
779 |
return '' |
|
780 |
# attribute relation |
|
781 |
if rtype == 'has_text': |
|
782 |
sql = self._visit_has_text_relation(relation) |
|
783 |
else: |
|
784 |
rhs_vars = rhs.get_nodes(VariableRef) |
|
785 |
if rhs_vars: |
|
786 |
# if variable(s) in the RHS |
|
787 |
sql = self._visit_var_attr_relation(relation, rhs_vars) |
|
788 |
else: |
|
789 |
# no variables in the RHS |
|
790 |
sql = self._visit_attribute_relation(relation) |
|
5706
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
791 |
elif (rtype == 'is' and isinstance(rhs.children[0], Constant) |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
792 |
and rhs.children[0].eval(self._args) is None): |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
793 |
# special case "C is NULL" |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
794 |
if lhs.name in self._varmap: |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
795 |
lhssql = self._varmap[lhs.name] |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
796 |
else: |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
797 |
lhssql = lhs.accept(self) |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
798 |
return '%s%s' % (lhssql, rhs.accept(self)) |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
799 |
elif '%s.%s' % (lhs, relation.r_type) in self._varmap: |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
800 |
# relation has already been processed by a previous step |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
801 |
return '' |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
802 |
elif relation.optional: |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
803 |
# check it has not already been treaten (to get necessary |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
804 |
# information to add an outer join condition) |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
805 |
if relation in self._state.done: |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
806 |
return '' |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
807 |
# OPTIONAL relation, generate a left|right outer join |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
808 |
sql = self._visit_outer_join_relation(relation, rschema) |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
809 |
elif rschema.inlined: |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
810 |
sql = self._visit_inlined_relation(relation) |
0 | 811 |
else: |
5706
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
812 |
# regular (non final) relation |
c2e8290bc7b7
[rql2sql] fix special 'X relation NULL' case (used by security insertion), broken by recent removal of IS operator in RQL
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5702
diff
changeset
|
813 |
sql = self._visit_relation(relation, rschema) |
0 | 814 |
return sql |
815 |
||
816 |
def _visit_inlined_relation(self, relation): |
|
817 |
lhsvar, _, rhsvar, rhsconst = relation_info(relation) |
|
818 |
# we are sure here to have a lhsvar |
|
819 |
assert lhsvar is not None |
|
3989
8b471739fb33
héhé, get the same result with a slightly enhanced condition and all this code removed
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3988
diff
changeset
|
820 |
if isinstance(relation.parent, Not) \ |
8b471739fb33
héhé, get the same result with a slightly enhanced condition and all this code removed
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3988
diff
changeset
|
821 |
and len(lhsvar.stinfo['relations']) > 1 \ |
8b471739fb33
héhé, get the same result with a slightly enhanced condition and all this code removed
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3988
diff
changeset
|
822 |
and (rhsvar is None or rhsvar._q_invariant): |
0 | 823 |
self._state.done.add(relation.parent) |
3989
8b471739fb33
héhé, get the same result with a slightly enhanced condition and all this code removed
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3988
diff
changeset
|
824 |
return '%s IS NULL' % self._inlined_var_sql(lhsvar, relation.r_type) |
2199
bd0a0f219751
fix sql generated on NOT inlined_relation queries. Use exists, so no more needs for extra DISTINCT
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2073
diff
changeset
|
825 |
lhssql = self._inlined_var_sql(lhsvar, relation.r_type) |
0 | 826 |
if rhsconst is not None: |
827 |
return '%s=%s' % (lhssql, rhsconst.accept(self)) |
|
828 |
if isinstance(rhsvar, Variable) and not rhsvar.name in self._varmap: |
|
829 |
# if the rhs variable is only linked to this relation, this mean we |
|
830 |
# only want the relation to exists, eg NOT NULL in case of inlined |
|
831 |
# relation |
|
832 |
if len(rhsvar.stinfo['relations']) == 1 and rhsvar._q_invariant: |
|
833 |
return '%s IS NOT NULL' % lhssql |
|
834 |
if rhsvar._q_invariant: |
|
835 |
return self._extra_join_sql(relation, lhssql, rhsvar) |
|
836 |
return '%s=%s' % (lhssql, rhsvar.accept(self)) |
|
837 |
||
838 |
def _process_relation_term(self, relation, rid, termvar, termconst, relfield): |
|
4794
81075b09ebf1
[rql2sql] fix potential crash when testing _q_invariant on a ColumnAlias instead of a Variable by always considering _q_invariant to false for them
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4719
diff
changeset
|
839 |
if termconst or not termvar._q_invariant: |
0 | 840 |
termsql = termconst and termconst.accept(self) or termvar.accept(self) |
841 |
yield '%s.%s=%s' % (rid, relfield, termsql) |
|
842 |
elif termvar._q_invariant: |
|
843 |
# if the variable is mapped, generate restriction anyway |
|
844 |
if termvar.name in self._varmap: |
|
845 |
termsql = termvar.accept(self) |
|
846 |
yield '%s.%s=%s' % (rid, relfield, termsql) |
|
847 |
extrajoin = self._extra_join_sql(relation, '%s.%s' % (rid, relfield), termvar) |
|
848 |
if extrajoin: |
|
849 |
yield extrajoin |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
850 |
|
0 | 851 |
def _visit_relation(self, relation, rschema): |
852 |
"""generate SQL for a relation |
|
853 |
||
854 |
implements optimization 1. |
|
855 |
""" |
|
856 |
if relation.r_type == 'identity': |
|
857 |
# special case "X identity Y" |
|
858 |
lhs, rhs = relation.get_parts() |
|
859 |
return '%s%s' % (lhs.accept(self), rhs.accept(self)) |
|
860 |
lhsvar, lhsconst, rhsvar, rhsconst = relation_info(relation) |
|
861 |
rid = self._relation_table(relation) |
|
862 |
sqls = [] |
|
863 |
sqls += self._process_relation_term(relation, rid, lhsvar, lhsconst, 'eid_from') |
|
864 |
sqls += self._process_relation_term(relation, rid, rhsvar, rhsconst, 'eid_to') |
|
865 |
sql = ' AND '.join(sqls) |
|
4467
0e73d299730a
fix long-waiting symetric typo: should be spelled symmetric. Add auto database migration on schema deserialization
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4286
diff
changeset
|
866 |
if rschema.symmetric: |
0 | 867 |
sql = '(%s OR %s)' % (sql, switch_relation_field(sql)) |
868 |
return sql |
|
869 |
||
870 |
def _visit_outer_join_relation(self, relation, rschema): |
|
871 |
""" |
|
872 |
left outer join syntax (optional=='right'): |
|
873 |
X relation Y? |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
874 |
|
0 | 875 |
right outer join syntax (optional=='left'): |
876 |
X? relation Y |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
877 |
|
0 | 878 |
full outer join syntaxes (optional=='both'): |
879 |
X? relation Y? |
|
880 |
||
881 |
if relation is inlined: |
|
882 |
if it's a left outer join: |
|
883 |
-> X LEFT OUTER JOIN Y ON (X.relation=Y.eid) |
|
884 |
elif it's a right outer join: |
|
885 |
-> Y LEFT OUTER JOIN X ON (X.relation=Y.eid) |
|
886 |
elif it's a full outer join: |
|
887 |
-> X FULL OUTER JOIN Y ON (X.relation=Y.eid) |
|
888 |
else: |
|
889 |
if it's a left outer join: |
|
890 |
-> X LEFT OUTER JOIN relation ON (relation.eid_from=X.eid) |
|
891 |
LEFT OUTER JOIN Y ON (relation.eid_to=Y.eid) |
|
892 |
elif it's a right outer join: |
|
893 |
-> Y LEFT OUTER JOIN relation ON (relation.eid_to=Y.eid) |
|
894 |
LEFT OUTER JOIN X ON (relation.eid_from=X.eid) |
|
895 |
elif it's a full outer join: |
|
896 |
-> X FULL OUTER JOIN Y ON (X.relation=Y.eid) |
|
897 |
""" |
|
898 |
lhsvar, lhsconst, rhsvar, rhsconst = relation_info(relation) |
|
899 |
if relation.optional == 'right': |
|
900 |
joinattr, restrattr = 'eid_from', 'eid_to' |
|
901 |
else: |
|
902 |
lhsvar, rhsvar = rhsvar, lhsvar |
|
903 |
lhsconst, rhsconst = rhsconst, lhsconst |
|
904 |
joinattr, restrattr = 'eid_to', 'eid_from' |
|
905 |
if relation.optional == 'both': |
|
906 |
outertype = 'FULL' |
|
907 |
else: |
|
908 |
outertype = 'LEFT' |
|
909 |
if rschema.inlined or relation.r_type == 'identity': |
|
910 |
self._state.done.add(relation) |
|
911 |
t1 = self._var_table(lhsvar) |
|
912 |
if relation.r_type == 'identity': |
|
913 |
attr = 'eid' |
|
914 |
else: |
|
915 |
attr = relation.r_type |
|
916 |
# reset lhs/rhs, we need the initial order now |
|
917 |
lhs, rhs = relation.get_variable_parts() |
|
918 |
if '%s.%s' % (lhs.name, attr) in self._varmap: |
|
919 |
lhssql = self._varmap['%s.%s' % (lhs.name, attr)] |
|
920 |
else: |
|
1251
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
921 |
lhssql = '%s.%s%s' % (self._var_table(lhs.variable), SQL_PREFIX, attr) |
0 | 922 |
if not rhsvar is None: |
923 |
t2 = self._var_table(rhsvar) |
|
924 |
if t2 is None: |
|
925 |
if rhsconst is not None: |
|
926 |
# inlined relation with invariant as rhs |
|
927 |
condition = '%s=%s' % (lhssql, rhsconst.accept(self)) |
|
928 |
if relation.r_type != 'identity': |
|
929 |
condition = '(%s OR %s IS NULL)' % (condition, lhssql) |
|
5004
4cc020ee70e2
le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
930 |
if not lhsvar.stinfo.get('optrelations'): |
0 | 931 |
return condition |
932 |
self.add_outer_join_condition(lhsvar, t1, condition) |
|
933 |
return |
|
934 |
else: |
|
935 |
condition = '%s=%s' % (lhssql, rhsconst.accept(self)) |
|
936 |
self.add_outer_join_condition(lhsvar, t1, condition) |
|
937 |
join = '%s OUTER JOIN %s ON (%s=%s)' % ( |
|
938 |
outertype, self._state.tables[t2][1], lhssql, rhs.accept(self)) |
|
939 |
self.replace_tables_by_outer_join(join, t1, t2) |
|
940 |
return '' |
|
941 |
lhssql = lhsconst and lhsconst.accept(self) or lhsvar.accept(self) |
|
942 |
rhssql = rhsconst and rhsconst.accept(self) or rhsvar.accept(self) |
|
943 |
rid = self._relation_table(relation) |
|
944 |
if not lhsvar: |
|
945 |
join = '' |
|
946 |
toreplace = [] |
|
947 |
maintable = rid |
|
948 |
else: |
|
949 |
join = '%s OUTER JOIN %s ON (%s.%s=%s' % ( |
|
950 |
outertype, self._state.tables[rid][1], rid, joinattr, lhssql) |
|
951 |
toreplace = [rid] |
|
952 |
maintable = self._var_table(lhsvar) |
|
953 |
if rhsconst: |
|
954 |
join += ' AND %s.%s=%s)' % (rid, restrattr, rhssql) |
|
955 |
else: |
|
956 |
join += ')' |
|
957 |
if not rhsconst: |
|
1122
9f37de24251f
fix rql2sq w/ outer join on subquery result
sylvain.thenault@logilab.fr
parents:
438
diff
changeset
|
958 |
rhstable = rhsvar._q_sqltable |
0 | 959 |
if rhstable: |
960 |
assert rhstable is not None, rhsvar |
|
961 |
join += ' %s OUTER JOIN %s ON (%s.%s=%s)' % ( |
|
1251
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
962 |
outertype, self._state.tables[rhstable][1], rid, restrattr, |
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
963 |
rhssql) |
0 | 964 |
toreplace.append(rhstable) |
965 |
self.replace_tables_by_outer_join(join, maintable, *toreplace) |
|
966 |
return '' |
|
967 |
||
968 |
def _visit_var_attr_relation(self, relation, rhs_vars): |
|
969 |
"""visit an attribute relation with variable(s) in the RHS |
|
970 |
||
971 |
attribute variables are used either in the selection or for |
|
972 |
unification (eg X attr1 A, Y attr2 A). In case of selection, |
|
973 |
nothing to do here. |
|
974 |
""" |
|
975 |
contextrels = {} |
|
976 |
for var in rhs_vars: |
|
2073
173c646981a7
fix missing from close when using a var map
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
977 |
if var.name in self._varmap: |
173c646981a7
fix missing from close when using a var map
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
978 |
# ensure table is added |
173c646981a7
fix missing from close when using a var map
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
979 |
self._var_info(var.variable) |
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
980 |
principal = var.variable.stinfo.get('principal') |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
981 |
if principal is not None and principal is not relation: |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
982 |
contextrels[var.name] = relation |
0 | 983 |
if not contextrels: |
984 |
return '' |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
985 |
# we have to generate unification expression |
0 | 986 |
lhssql = self._inlined_var_sql(relation.children[0].variable, |
987 |
relation.r_type) |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
988 |
try: |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
989 |
self._state.ignore_varmap = True |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
990 |
return '%s%s' % (lhssql, relation.children[1].accept(self)) |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
991 |
finally: |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
992 |
self._state.ignore_varmap = False |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
993 |
|
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
994 |
def _visit_attribute_relation(self, rel): |
0 | 995 |
"""generate SQL for an attribute relation""" |
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
996 |
lhs, rhs = rel.get_parts() |
0 | 997 |
rhssql = rhs.accept(self) |
998 |
table = self._var_table(lhs.variable) |
|
999 |
if table is None: |
|
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1000 |
assert rel.r_type == 'eid' |
0 | 1001 |
lhssql = lhs.accept(self) |
1002 |
else: |
|
1003 |
try: |
|
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1004 |
lhssql = self._varmap['%s.%s' % (lhs.name, rel.r_type)] |
0 | 1005 |
except KeyError: |
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1006 |
mapkey = '%s.%s' % (self._state.solution[lhs.name], rel.r_type) |
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1007 |
if mapkey in self.attr_map: |
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1008 |
cb, sourcecb = self.attr_map[mapkey] |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1009 |
if sourcecb: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1010 |
# callback is a source callback, we can't use this |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1011 |
# attribute in restriction |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1012 |
raise QueryError("can't use %s (%s) in restriction" |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1013 |
% (mapkey, rel.as_string())) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1014 |
lhssql = cb(self, lhs.variable, rel) |
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1015 |
elif rel.r_type == 'eid': |
1251
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
1016 |
lhssql = lhs.variable._q_sql |
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
1017 |
else: |
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1018 |
lhssql = '%s.%s%s' % (table, SQL_PREFIX, rel.r_type) |
0 | 1019 |
try: |
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1020 |
if rel._q_needcast == 'TODAY': |
0 | 1021 |
sql = 'DATE(%s)%s' % (lhssql, rhssql) |
1022 |
# XXX which cast function should be used |
|
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1023 |
#elif rel._q_needcast == 'NOW': |
0 | 1024 |
# sql = 'TIMESTAMP(%s)%s' % (lhssql, rhssql) |
1025 |
else: |
|
1026 |
sql = '%s%s' % (lhssql, rhssql) |
|
1027 |
except AttributeError: |
|
1028 |
sql = '%s%s' % (lhssql, rhssql) |
|
5004
4cc020ee70e2
le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1029 |
if lhs.variable.stinfo.get('optrelations'): |
0 | 1030 |
self.add_outer_join_condition(lhs.variable, table, sql) |
1031 |
else: |
|
1032 |
return sql |
|
1033 |
||
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1034 |
def _visit_has_text_relation(self, rel): |
0 | 1035 |
"""generate SQL for a has_text relation""" |
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1036 |
lhs, rhs = rel.get_parts() |
0 | 1037 |
const = rhs.children[0] |
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1038 |
alias = self._fti_table(rel) |
0 | 1039 |
jointo = lhs.accept(self) |
1040 |
restriction = '' |
|
1041 |
lhsvar = lhs.variable |
|
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1042 |
me_is_principal = lhsvar.stinfo.get('principal') is rel |
0 | 1043 |
if me_is_principal: |
5004
4cc020ee70e2
le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1044 |
if lhsvar.stinfo['typerel'] is None: |
0 | 1045 |
# the variable is using the fti table, no join needed |
1046 |
jointo = None |
|
1047 |
elif not lhsvar.name in self._varmap: |
|
1048 |
# join on entities instead of etype's table to get result for |
|
1049 |
# external entities on multisources configurations |
|
3762
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1050 |
ealias = lhsvar._q_sqltable = '_' + lhsvar.name |
0 | 1051 |
jointo = lhsvar._q_sql = '%s.eid' % ealias |
1052 |
self.add_table('entities AS %s' % ealias, ealias) |
|
1053 |
if not lhsvar._q_invariant or len(lhsvar.stinfo['possibletypes']) == 1: |
|
1054 |
restriction = " AND %s.type='%s'" % (ealias, self._state.solution[lhs.name]) |
|
1055 |
else: |
|
1056 |
etypes = ','.join("'%s'" % etype for etype in lhsvar.stinfo['possibletypes']) |
|
1057 |
restriction = " AND %s.type IN (%s)" % (ealias, etypes) |
|
2354
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1058 |
if isinstance(rel.parent, Not): |
9b4bac626977
ability to map attributes to something else than usual cw mapping on sql generation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2199
diff
changeset
|
1059 |
self._state.done.add(rel.parent) |
0 | 1060 |
not_ = True |
1061 |
else: |
|
1062 |
not_ = False |
|
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1063 |
return self.dbhelper.fti_restriction_sql(alias, const.eval(self._args), |
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
|
1064 |
jointo, not_) + restriction |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1065 |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1066 |
def visit_comparison(self, cmp): |
3787
82bb2c7f083b
C - fix typo. make `lgp check` happy.
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
3762
diff
changeset
|
1067 |
"""generate SQL for a comparison""" |
0 | 1068 |
if len(cmp.children) == 2: |
1862
94dc8ccd320b
#343322: should generate IS NULL in sql w/ None values in substitution
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1802
diff
changeset
|
1069 |
# XXX occurs ? |
0 | 1070 |
lhs, rhs = cmp.children |
1071 |
else: |
|
1072 |
lhs = None |
|
1073 |
rhs = cmp.children[0] |
|
1074 |
operator = cmp.operator |
|
5702
9fb240cf0f61
[rql] more update to 0.26.2 api
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5662
diff
changeset
|
1075 |
if operator in ('LIKE', 'ILIKE'): |
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1076 |
if operator == 'ILIKE' and not self.dbhelper.ilike_support: |
0 | 1077 |
operator = ' LIKE ' |
1078 |
else: |
|
1079 |
operator = ' %s ' % operator |
|
1862
94dc8ccd320b
#343322: should generate IS NULL in sql w/ None values in substitution
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1802
diff
changeset
|
1080 |
elif (operator == '=' and isinstance(rhs, Constant) |
94dc8ccd320b
#343322: should generate IS NULL in sql w/ None values in substitution
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1802
diff
changeset
|
1081 |
and rhs.eval(self._args) is None): |
94dc8ccd320b
#343322: should generate IS NULL in sql w/ None values in substitution
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1802
diff
changeset
|
1082 |
if lhs is None: |
94dc8ccd320b
#343322: should generate IS NULL in sql w/ None values in substitution
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1802
diff
changeset
|
1083 |
return ' IS NULL' |
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1084 |
return '%s IS NULL' % lhs.accept(self) |
0 | 1085 |
elif isinstance(rhs, Function) and rhs.name == 'IN': |
1086 |
assert operator == '=' |
|
1087 |
operator = ' ' |
|
1088 |
if lhs is None: |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1089 |
return '%s%s'% (operator, rhs.accept(self)) |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1090 |
return '%s%s%s'% (lhs.accept(self), operator, rhs.accept(self)) |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1091 |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1092 |
def visit_mathexpression(self, mexpr): |
0 | 1093 |
"""generate SQL for a mathematic expression""" |
1094 |
lhs, rhs = mexpr.get_parts() |
|
1095 |
# check for string concatenation |
|
1096 |
operator = mexpr.operator |
|
1097 |
try: |
|
1098 |
if mexpr.operator == '+' and mexpr.get_type(self._state.solution, self._args) == 'String': |
|
1099 |
operator = '||' |
|
1100 |
except CoercionError: |
|
1101 |
pass |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1102 |
return '(%s %s %s)'% (lhs.accept(self), operator, rhs.accept(self)) |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1103 |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1104 |
def visit_function(self, func): |
0 | 1105 |
"""generate SQL name for a function""" |
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1106 |
args = [c.accept(self) for c in func.children] |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1107 |
if func in self._state.source_cb_funcs: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1108 |
# function executed as a callback on the source |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1109 |
assert len(args) == 1 |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1110 |
return args[0] |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1111 |
# func_as_sql will check function is supported by the backend |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1112 |
return self.dbhelper.func_as_sql(func.name, args) |
0 | 1113 |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1114 |
def visit_constant(self, constant): |
0 | 1115 |
"""generate SQL name for a constant""" |
1116 |
value = constant.value |
|
1117 |
if constant.type is None: |
|
1118 |
return 'NULL' |
|
1119 |
if constant.type == 'Int' and isinstance(constant.parent, SortTerm): |
|
1120 |
return constant.value |
|
1121 |
if constant.type in ('Date', 'Datetime'): |
|
1122 |
rel = constant.relation() |
|
1123 |
if rel is not None: |
|
1124 |
rel._q_needcast = value |
|
1125 |
return self.keyword_map[value]() |
|
1497
54fc5cc52210
use dbmshelper to generate correct boolean value in rql2sql
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1382
diff
changeset
|
1126 |
if constant.type == 'Boolean': |
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1127 |
value = self.dbhelper.boolean_value(value) |
4124
8f2f5f0a89e7
fix backout
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4121
diff
changeset
|
1128 |
if constant.type == 'Substitute': |
0 | 1129 |
_id = constant.value |
1130 |
if isinstance(_id, unicode): |
|
1131 |
_id = _id.encode() |
|
1132 |
else: |
|
1133 |
_id = str(id(constant)).replace('-', '', 1) |
|
1134 |
if isinstance(value, unicode): |
|
1135 |
value = value.encode(self.dbencoding) |
|
1136 |
self._query_attrs[_id] = value |
|
1137 |
return '%%(%s)s' % _id |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1138 |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1139 |
def visit_variableref(self, variableref): |
0 | 1140 |
"""get the sql name for a variable reference""" |
1141 |
# use accept, .variable may be a variable or a columnalias |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1142 |
return variableref.variable.accept(self) |
0 | 1143 |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1144 |
def visit_columnalias(self, colalias): |
0 | 1145 |
"""get the sql name for a subquery column alias""" |
1146 |
if colalias.name in self._varmap: |
|
1147 |
sql = self._varmap[colalias.name] |
|
1122
9f37de24251f
fix rql2sq w/ outer join on subquery result
sylvain.thenault@logilab.fr
parents:
438
diff
changeset
|
1148 |
table = sql.split('.', 1)[0] |
9f37de24251f
fix rql2sq w/ outer join on subquery result
sylvain.thenault@logilab.fr
parents:
438
diff
changeset
|
1149 |
colalias._q_sqltable = table |
9f37de24251f
fix rql2sq w/ outer join on subquery result
sylvain.thenault@logilab.fr
parents:
438
diff
changeset
|
1150 |
colalias._q_sql = sql |
9f37de24251f
fix rql2sq w/ outer join on subquery result
sylvain.thenault@logilab.fr
parents:
438
diff
changeset
|
1151 |
self.add_table(table) |
0 | 1152 |
return sql |
1153 |
return colalias._q_sql |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1154 |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1155 |
def visit_variable(self, variable): |
0 | 1156 |
"""get the table name and sql string for a variable""" |
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1157 |
#if contextrels is None and variable.name in self._state.done: |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1158 |
if variable.name in self._state.done: |
0 | 1159 |
if self._in_wrapping_query: |
1160 |
return 'T1.%s' % self._state.aliases[variable.name] |
|
1161 |
return variable._q_sql |
|
1162 |
self._state.done.add(variable.name) |
|
1163 |
vtablename = None |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1164 |
if not self._state.ignore_varmap and variable.name in self._varmap: |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1165 |
sql, vtablename = self._var_info(variable) |
0 | 1166 |
elif variable.stinfo['attrvar']: |
1167 |
# attribute variable (systematically used in rhs of final |
|
1168 |
# relation(s)), get table name and sql from any rhs relation |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1169 |
sql = self._linked_var_sql(variable) |
0 | 1170 |
elif variable._q_invariant: |
1171 |
# since variable is invariant, we know we won't found final relation |
|
1172 |
principal = variable.stinfo['principal'] |
|
1173 |
if principal is None: |
|
3762
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1174 |
vtablename = '_' + variable.name |
1251
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
1175 |
self.add_table('entities AS %s' % vtablename, vtablename) |
0 | 1176 |
sql = '%s.eid' % vtablename |
5004
4cc020ee70e2
le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1177 |
if variable.stinfo['typerel'] is not None: |
0 | 1178 |
# add additional restriction on entities.type column |
1179 |
pts = variable.stinfo['possibletypes'] |
|
1180 |
if len(pts) == 1: |
|
1181 |
etype = iter(variable.stinfo['possibletypes']).next() |
|
1182 |
restr = "%s.type='%s'" % (vtablename, etype) |
|
1183 |
else: |
|
1184 |
etypes = ','.join("'%s'" % et for et in pts) |
|
1185 |
restr = '%s.type IN (%s)' % (vtablename, etypes) |
|
1186 |
self._state.add_restriction(restr) |
|
1187 |
elif principal.r_type == 'has_text': |
|
1188 |
sql = '%s.%s' % (self._fti_table(principal), |
|
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1189 |
self.dbhelper.fti_uid_attr) |
0 | 1190 |
elif principal in variable.stinfo['rhsrelations']: |
1191 |
if self.schema.rschema(principal.r_type).inlined: |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1192 |
sql = self._linked_var_sql(variable) |
0 | 1193 |
else: |
1194 |
sql = '%s.eid_to' % self._relation_table(principal) |
|
1195 |
else: |
|
1196 |
sql = '%s.eid_from' % self._relation_table(principal) |
|
1197 |
else: |
|
1198 |
# standard variable: get table name according to etype and use .eid |
|
1199 |
# attribute |
|
1200 |
sql, vtablename = self._var_info(variable) |
|
1201 |
variable._q_sqltable = vtablename |
|
1202 |
variable._q_sql = sql |
|
1203 |
return sql |
|
1204 |
||
1205 |
# various utilities ####################################################### |
|
1206 |
||
1207 |
def _extra_join_sql(self, relation, sql, var): |
|
1208 |
# if rhs var is invariant, and this relation is not its principal, |
|
1209 |
# generate extra join |
|
1210 |
try: |
|
1211 |
if not var.stinfo['principal'] is relation: |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1212 |
op = relation.operator() |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1213 |
if op == '=': |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1214 |
# need a predicable result for tests |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1215 |
args = sorted( (sql, var.accept(self)) ) |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1216 |
args.insert(1, op) |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1217 |
else: |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1218 |
args = (sql, op, var.accept(self)) |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1219 |
return '%s%s%s' % tuple(args) |
0 | 1220 |
except KeyError: |
1221 |
# no principal defined, relation is necessarily the principal and |
|
1222 |
# so nothing to return here |
|
1223 |
pass |
|
1224 |
return '' |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1225 |
|
5593
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1226 |
def _temp_table_scope(self, select, table): |
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1227 |
scope = 9999 |
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1228 |
for var, sql in self._varmap.iteritems(): |
5635
56784e46509f
[rql2sql] fix scope computation of variable from temporary table
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5630
diff
changeset
|
1229 |
# skip "attribute variable" in varmap (such 'T.login') |
56784e46509f
[rql2sql] fix scope computation of variable from temporary table
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5630
diff
changeset
|
1230 |
if not '.' in var and table == sql.split('.', 1)[0]: |
5593
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1231 |
try: |
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1232 |
scope = min(scope, self._state.scopes[select.defined_vars[var].scope]) |
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1233 |
except KeyError: |
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1234 |
scope = 0 # XXX |
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1235 |
if scope == 0: |
5635
56784e46509f
[rql2sql] fix scope computation of variable from temporary table
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5630
diff
changeset
|
1236 |
break |
56784e46509f
[rql2sql] fix scope computation of variable from temporary table
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5630
diff
changeset
|
1237 |
return scope |
5593
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1238 |
|
0 | 1239 |
def _var_info(self, var): |
1240 |
try: |
|
1241 |
sql = self._varmap[var.name] |
|
3762
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1242 |
tablealias = sql.split('.', 1)[0] |
5593
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1243 |
scope = self._temp_table_scope(var.stmt, tablealias) |
3762
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1244 |
self.add_table(tablealias, scope=scope) |
0 | 1245 |
except KeyError: |
5593
f6c55bec9326
[rql2sql] properly compute scope for variable from temporary tables
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5582
diff
changeset
|
1246 |
scope = self._state.scopes[var.scope] |
0 | 1247 |
etype = self._state.solution[var.name] |
1248 |
# XXX this check should be moved in rql.stcheck |
|
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3245
diff
changeset
|
1249 |
if self.schema.eschema(etype).final: |
0 | 1250 |
raise BadRQLQuery(var.stmt.root) |
3762
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1251 |
tablealias = '_' + var.name |
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1252 |
sql = '%s.%seid' % (tablealias, SQL_PREFIX) |
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1253 |
self.add_table('%s%s AS %s' % (SQL_PREFIX, etype, tablealias), |
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1254 |
tablealias, scope=scope) |
e416186fb91c
prefix sql aliases for entity table by '_' to avoid pb with variable such as 'AS' (eg a keyword in SQL)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3754
diff
changeset
|
1255 |
return sql, tablealias |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1256 |
|
0 | 1257 |
def _inlined_var_sql(self, var, rtype): |
1258 |
try: |
|
1259 |
sql = self._varmap['%s.%s' % (var.name, rtype)] |
|
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
|
1260 |
scope = self._state.scopes[var.scope] |
0 | 1261 |
self.add_table(sql.split('.', 1)[0], scope=scope) |
1262 |
except KeyError: |
|
1251
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
1263 |
sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype) |
0 | 1264 |
#self._state.done.add(var.name) |
1265 |
return sql |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1266 |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1267 |
def _linked_var_sql(self, variable): |
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1268 |
if not self._state.ignore_varmap: |
0 | 1269 |
try: |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1270 |
return self._varmap[variable.name] |
0 | 1271 |
except KeyError: |
1272 |
pass |
|
3815
50b87f759b5d
test and fix http://www.logilab.org/ticket/499838
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3787
diff
changeset
|
1273 |
rel = (variable.stinfo.get('principal') or |
0 | 1274 |
iter(variable.stinfo['rhsrelations']).next()) |
1275 |
linkedvar = rel.children[0].variable |
|
1276 |
if rel.r_type == 'eid': |
|
1277 |
return linkedvar.accept(self) |
|
1278 |
if isinstance(linkedvar, ColumnAlias): |
|
1279 |
raise BadRQLQuery('variable %s should be selected by the subquery' |
|
1280 |
% variable.name) |
|
1281 |
try: |
|
1282 |
sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)] |
|
1283 |
except KeyError: |
|
5013
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1284 |
mapkey = '%s.%s' % (self._state.solution[linkedvar.name], rel.r_type) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1285 |
if mapkey in self.attr_map: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1286 |
cb, sourcecb = self.attr_map[mapkey] |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1287 |
if not sourcecb: |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1288 |
return cb(self, linkedvar, rel) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1289 |
# attribute mapped at the source level (bfss for instance) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1290 |
stmt = rel.stmt |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1291 |
for selectidx, vref in iter_mapped_var_sels(stmt, variable): |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1292 |
stack = [cb] |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1293 |
update_source_cb_stack(self._state, stmt, vref, stack) |
ad91f93bbb93
[source storage] refactor source sql generation and results handling to allow repository side callbacks
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5010
diff
changeset
|
1294 |
self._state._needs_source_cb[selectidx] = stack |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1295 |
linkedvar.accept(self) |
1251
af40e615dc89
introduce a 'cw_' prefix on entity table and column names so we don't conflict with sql or DBMS specific keywords
sylvain.thenault@logilab.fr
parents:
1124
diff
changeset
|
1296 |
sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type) |
0 | 1297 |
return sql |
1298 |
||
1299 |
# tables handling ######################################################### |
|
1300 |
||
1301 |
def alias_and_add_table(self, tablename): |
|
1302 |
alias = '%s%s' % (tablename, self._state.count) |
|
1303 |
self._state.count += 1 |
|
1304 |
self.add_table('%s AS %s' % (tablename, alias), alias) |
|
1305 |
return alias |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1306 |
|
0 | 1307 |
def add_table(self, table, key=None, scope=-1): |
1308 |
if key is None: |
|
1309 |
key = table |
|
1310 |
if key in self._state.tables: |
|
1311 |
return |
|
3238
988a72e59b2b
[querier] fix sql generated w/ NOT relation and shared variable: ensure variable's table is in parent select'scope
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2915
diff
changeset
|
1312 |
if scope < 0: |
988a72e59b2b
[querier] fix sql generated w/ NOT relation and shared variable: ensure variable's table is in parent select'scope
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2915
diff
changeset
|
1313 |
scope = len(self._state.actual_tables) + scope |
2915
651bbe1526b6
[rql2sql] test and fix a bug triggered when editing a ticket in jpl
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2354
diff
changeset
|
1314 |
self._state.tables[key] = (scope, table) |
0 | 1315 |
self._state.actual_tables[scope].append(table) |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1316 |
|
0 | 1317 |
def replace_tables_by_outer_join(self, substitute, lefttable, *tables): |
1318 |
for table in tables: |
|
1319 |
try: |
|
1320 |
scope, alias = self._state.tables[table] |
|
1321 |
self._state.actual_tables[scope].remove(alias) |
|
1322 |
except ValueError: # huum, not sure about what should be done here |
|
1323 |
msg = "%s already used in an outer join, don't know what to do!" |
|
1324 |
raise Exception(msg % table) |
|
1325 |
try: |
|
1326 |
tablealias = self._state.outer_tables[lefttable] |
|
1327 |
actualtables = self._state.actual_tables[-1] |
|
1328 |
except KeyError: |
|
1329 |
tablescope, tablealias = self._state.tables[lefttable] |
|
1330 |
actualtables = self._state.actual_tables[tablescope] |
|
1331 |
outerjoin = '%s %s' % (tablealias, substitute) |
|
1332 |
self._update_outer_tables(lefttable, actualtables, tablealias, outerjoin) |
|
1333 |
for table in tables: |
|
1334 |
self._state.outer_tables[table] = outerjoin |
|
1335 |
||
1336 |
def add_outer_join_condition(self, var, table, condition): |
|
1337 |
try: |
|
1338 |
tablealias = self._state.outer_tables[table] |
|
1339 |
actualtables = self._state.actual_tables[-1] |
|
1340 |
except KeyError: |
|
5004
4cc020ee70e2
le patch rql26 a été importé
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1341 |
for rel in var.stinfo.get('optrelations'): |
0 | 1342 |
self.visit_relation(rel) |
1343 |
assert self._state.outer_tables |
|
1344 |
self.add_outer_join_condition(var, table, condition) |
|
1345 |
return |
|
1346 |
before, after = tablealias.split(' AS %s ' % table, 1) |
|
1347 |
beforep, afterp = after.split(')', 1) |
|
1348 |
outerjoin = '%s AS %s %s AND %s) %s' % (before, table, beforep, |
|
1349 |
condition, afterp) |
|
1350 |
self._update_outer_tables(table, actualtables, tablealias, outerjoin) |
|
1351 |
||
1352 |
def _update_outer_tables(self, table, actualtables, oldalias, newalias): |
|
1353 |
actualtables.remove(oldalias) |
|
1354 |
actualtables.append(newalias) |
|
4286
6801093af29c
fix bug: the 'table' argument is overwritten by the loop variable, causing latter crash due to this missing information
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4212
diff
changeset
|
1355 |
self._state.outer_tables[table] = newalias |
0 | 1356 |
# some tables which have already been used as outer table and replaced |
1357 |
# by <oldalias> may not be reused here, though their associated value |
|
1358 |
# in the outer_tables dict has to be updated as well |
|
1359 |
for table, outerexpr in self._state.outer_tables.iteritems(): |
|
1360 |
if outerexpr == oldalias: |
|
1361 |
self._state.outer_tables[table] = newalias |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1362 |
|
0 | 1363 |
def _var_table(self, var): |
1364 |
var.accept(self)#.visit_variable(var) |
|
1365 |
return var._q_sqltable |
|
1366 |
||
1367 |
def _relation_table(self, relation): |
|
1368 |
"""return the table alias used by the given relation""" |
|
1369 |
if relation in self._state.done: |
|
1370 |
return relation._q_sqltable |
|
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3245
diff
changeset
|
1371 |
assert not self.schema.rschema(relation.r_type).final, relation.r_type |
0 | 1372 |
rid = 'rel_%s%s' % (relation.r_type, self._state.count) |
1373 |
# relation's table is belonging to the root scope if it is the principal |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1374 |
# table of one of it's variable and if that variable belong's to parent |
0 | 1375 |
# scope |
1376 |
for varref in relation.iget_nodes(VariableRef): |
|
1377 |
var = varref.variable |
|
1378 |
if isinstance(var, ColumnAlias): |
|
1379 |
scope = 0 |
|
1380 |
break |
|
1381 |
# XXX may have a principal without being invariant for this generation, |
|
1382 |
# not sure this is a pb or not |
|
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
|
1383 |
if var.stinfo.get('principal') is relation and var.scope is var.stmt: |
0 | 1384 |
scope = 0 |
1385 |
break |
|
1386 |
else: |
|
1387 |
scope = -1 |
|
1388 |
self._state.count += 1 |
|
1389 |
self.add_table('%s_relation AS %s' % (relation.r_type, rid), rid, scope=scope) |
|
1390 |
relation._q_sqltable = rid |
|
1391 |
self._state.done.add(relation) |
|
1392 |
return rid |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1522
diff
changeset
|
1393 |
|
0 | 1394 |
def _fti_table(self, relation): |
1395 |
if relation in self._state.done: |
|
1396 |
try: |
|
1397 |
return relation._q_sqltable |
|
1398 |
except AttributeError: |
|
1399 |
pass |
|
1400 |
self._state.done.add(relation) |
|
5010
b2c5aee8ca3f
[cleanup] rename dbms_helper to dbhelper for consistency
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4845
diff
changeset
|
1401 |
alias = self.alias_and_add_table(self.dbhelper.fti_table) |
0 | 1402 |
relation._q_sqltable = alias |
1403 |
return alias |