23 Potential optimization information is collected by the querier, sql generation |
23 Potential optimization information is collected by the querier, sql generation |
24 is done according to this information |
24 is done according to this information |
25 |
25 |
26 |
26 |
27 :organization: Logilab |
27 :organization: Logilab |
28 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
28 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
29 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
29 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
30 """ |
30 """ |
31 __docformat__ = "restructuredtext en" |
31 __docformat__ = "restructuredtext en" |
32 |
32 |
33 import threading |
33 import threading |
36 from rql.stmts import Union, Select |
36 from rql.stmts import Union, Select |
37 from rql.nodes import (SortTerm, VariableRef, Constant, Function, Not, |
37 from rql.nodes import (SortTerm, VariableRef, Constant, Function, Not, |
38 Variable, ColumnAlias, Relation, SubQuery, Exists) |
38 Variable, ColumnAlias, Relation, SubQuery, Exists) |
39 |
39 |
40 from cubicweb import server |
40 from cubicweb import server |
|
41 from cubicweb.server.sqlutils import SQL_PREFIX |
41 from cubicweb.server.utils import cleanup_solutions |
42 from cubicweb.server.utils import cleanup_solutions |
42 |
43 |
43 def _new_var(select, varname): |
44 def _new_var(select, varname): |
44 newvar = select.get_variable(varname) |
45 newvar = select.get_variable(varname) |
45 if not 'relations' in newvar.stinfo: |
46 if not 'relations' in newvar.stinfo: |
762 # reset lhs/rhs, we need the initial order now |
763 # reset lhs/rhs, we need the initial order now |
763 lhs, rhs = relation.get_variable_parts() |
764 lhs, rhs = relation.get_variable_parts() |
764 if '%s.%s' % (lhs.name, attr) in self._varmap: |
765 if '%s.%s' % (lhs.name, attr) in self._varmap: |
765 lhssql = self._varmap['%s.%s' % (lhs.name, attr)] |
766 lhssql = self._varmap['%s.%s' % (lhs.name, attr)] |
766 else: |
767 else: |
767 lhssql = '%s.%s' % (self._var_table(lhs.variable), attr) |
768 lhssql = '%s.%s%s' % (self._var_table(lhs.variable), SQL_PREFIX, attr) |
768 if not rhsvar is None: |
769 if not rhsvar is None: |
769 t2 = self._var_table(rhsvar) |
770 t2 = self._var_table(rhsvar) |
770 if t2 is None: |
771 if t2 is None: |
771 if rhsconst is not None: |
772 if rhsconst is not None: |
772 # inlined relation with invariant as rhs |
773 # inlined relation with invariant as rhs |
799 if rhsconst: |
800 if rhsconst: |
800 join += ' AND %s.%s=%s)' % (rid, restrattr, rhssql) |
801 join += ' AND %s.%s=%s)' % (rid, restrattr, rhssql) |
801 else: |
802 else: |
802 join += ')' |
803 join += ')' |
803 if not rhsconst: |
804 if not rhsconst: |
804 rhstable = self._var_table(rhsvar) |
805 rhstable = rhsvar._q_sqltable |
805 if rhstable: |
806 if rhstable: |
806 assert rhstable is not None, rhsvar |
807 assert rhstable is not None, rhsvar |
807 join += ' %s OUTER JOIN %s ON (%s.%s=%s)' % ( |
808 join += ' %s OUTER JOIN %s ON (%s.%s=%s)' % ( |
808 outertype, self._state.tables[rhstable][1], rid, restrattr, rhssql) |
809 outertype, self._state.tables[rhstable][1], rid, restrattr, |
|
810 rhssql) |
809 toreplace.append(rhstable) |
811 toreplace.append(rhstable) |
810 self.replace_tables_by_outer_join(join, maintable, *toreplace) |
812 self.replace_tables_by_outer_join(join, maintable, *toreplace) |
811 return '' |
813 return '' |
812 |
814 |
813 def _visit_var_attr_relation(self, relation, rhs_vars): |
815 def _visit_var_attr_relation(self, relation, rhs_vars): |
843 lhssql = lhs.accept(self) |
845 lhssql = lhs.accept(self) |
844 else: |
846 else: |
845 try: |
847 try: |
846 lhssql = self._varmap['%s.%s' % (lhs.name, relation.r_type)] |
848 lhssql = self._varmap['%s.%s' % (lhs.name, relation.r_type)] |
847 except KeyError: |
849 except KeyError: |
848 lhssql = '%s.%s' % (table, relation.r_type) |
850 if relation.r_type == 'eid': |
|
851 lhssql = lhs.variable._q_sql |
|
852 else: |
|
853 lhssql = '%s.%s%s' % (table, SQL_PREFIX, relation.r_type) |
849 try: |
854 try: |
850 if relation._q_needcast == 'TODAY': |
855 if relation._q_needcast == 'TODAY': |
851 sql = 'DATE(%s)%s' % (lhssql, rhssql) |
856 sql = 'DATE(%s)%s' % (lhssql, rhssql) |
852 # XXX which cast function should be used |
857 # XXX which cast function should be used |
853 #elif relation._q_needcast == 'NOW': |
858 #elif relation._q_needcast == 'NOW': |
964 |
969 |
965 def visit_columnalias(self, colalias, contextrels=None): |
970 def visit_columnalias(self, colalias, contextrels=None): |
966 """get the sql name for a subquery column alias""" |
971 """get the sql name for a subquery column alias""" |
967 if colalias.name in self._varmap: |
972 if colalias.name in self._varmap: |
968 sql = self._varmap[colalias.name] |
973 sql = self._varmap[colalias.name] |
969 self.add_table(sql.split('.', 1)[0]) |
974 table = sql.split('.', 1)[0] |
|
975 colalias._q_sqltable = table |
|
976 colalias._q_sql = sql |
|
977 self.add_table(table) |
970 return sql |
978 return sql |
971 return colalias._q_sql |
979 return colalias._q_sql |
972 |
980 |
973 def visit_variable(self, variable, contextrels=None): |
981 def visit_variable(self, variable, contextrels=None): |
974 """get the table name and sql string for a variable""" |
982 """get the table name and sql string for a variable""" |
987 elif variable._q_invariant: |
995 elif variable._q_invariant: |
988 # since variable is invariant, we know we won't found final relation |
996 # since variable is invariant, we know we won't found final relation |
989 principal = variable.stinfo['principal'] |
997 principal = variable.stinfo['principal'] |
990 if principal is None: |
998 if principal is None: |
991 vtablename = variable.name |
999 vtablename = variable.name |
992 self.add_table('entities AS %s' % variable.name, vtablename) |
1000 self.add_table('entities AS %s' % vtablename, vtablename) |
993 sql = '%s.eid' % vtablename |
1001 sql = '%s.eid' % vtablename |
994 if variable.stinfo['typerels']: |
1002 if variable.stinfo['typerels']: |
995 # add additional restriction on entities.type column |
1003 # add additional restriction on entities.type column |
996 pts = variable.stinfo['possibletypes'] |
1004 pts = variable.stinfo['possibletypes'] |
997 if len(pts) == 1: |
1005 if len(pts) == 1: |
1052 etype = self._state.solution[var.name] |
1060 etype = self._state.solution[var.name] |
1053 # XXX this check should be moved in rql.stcheck |
1061 # XXX this check should be moved in rql.stcheck |
1054 if self.schema.eschema(etype).is_final(): |
1062 if self.schema.eschema(etype).is_final(): |
1055 raise BadRQLQuery(var.stmt.root) |
1063 raise BadRQLQuery(var.stmt.root) |
1056 table = var.name |
1064 table = var.name |
1057 sql = '%s.eid' % table |
1065 sql = '%s.%seid' % (table, SQL_PREFIX) |
1058 self.add_table('%s AS %s' % (etype, table), table, scope=scope) |
1066 self.add_table('%s%s AS %s' % (SQL_PREFIX, etype, table), table, scope=scope) |
1059 return sql, table |
1067 return sql, table |
1060 |
1068 |
1061 def _inlined_var_sql(self, var, rtype): |
1069 def _inlined_var_sql(self, var, rtype): |
1062 try: |
1070 try: |
1063 sql = self._varmap['%s.%s' % (var.name, rtype)] |
1071 sql = self._varmap['%s.%s' % (var.name, rtype)] |
1064 scope = var.sqlscope is var.stmt and 0 or -1 |
1072 scope = var.sqlscope is var.stmt and 0 or -1 |
1065 self.add_table(sql.split('.', 1)[0], scope=scope) |
1073 self.add_table(sql.split('.', 1)[0], scope=scope) |
1066 except KeyError: |
1074 except KeyError: |
1067 sql = '%s.%s' % (self._var_table(var), rtype) |
1075 sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype) |
1068 #self._state.done.add(var.name) |
1076 #self._state.done.add(var.name) |
1069 return sql |
1077 return sql |
1070 |
1078 |
1071 def _linked_var_sql(self, variable, contextrels=None): |
1079 def _linked_var_sql(self, variable, contextrels=None): |
1072 if contextrels is None: |
1080 if contextrels is None: |
1085 % variable.name) |
1093 % variable.name) |
1086 try: |
1094 try: |
1087 sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)] |
1095 sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)] |
1088 except KeyError: |
1096 except KeyError: |
1089 linkedvar.accept(self) |
1097 linkedvar.accept(self) |
1090 sql = '%s.%s' % (linkedvar._q_sqltable, rel.r_type) |
1098 sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type) |
1091 return sql |
1099 return sql |
1092 |
1100 |
1093 # tables handling ######################################################### |
1101 # tables handling ######################################################### |
1094 |
1102 |
1095 def alias_and_add_table(self, tablename): |
1103 def alias_and_add_table(self, tablename): |