server/sources/rql2sql.py
branchtls-sprint
changeset 1802 d628defebc17
parent 1522 47b2ffbee760
child 1862 94dc8ccd320b
equal deleted inserted replaced
1801:672acc730ce5 1802:d628defebc17
    15 
    15 
    16 2. Any X WHERE X nonfinal1 Y, Y nonfinal2 Z
    16 2. Any X WHERE X nonfinal1 Y, Y nonfinal2 Z
    17 
    17 
    18    -> direct join between nonfinal1 and nonfinal2, whatever X,Y, Z (unless
    18    -> direct join between nonfinal1 and nonfinal2, whatever X,Y, Z (unless
    19       inlined...)
    19       inlined...)
    20       
    20 
    21       NOT IMPLEMENTED (and quite hard to implement)
    21       NOT IMPLEMENTED (and quite hard to implement)
    22 
    22 
    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 
    39 
    39 
    40 from cubicweb import server
    40 from cubicweb import server
    41 from cubicweb.server.sqlutils import SQL_PREFIX
    41 from cubicweb.server.sqlutils import SQL_PREFIX
    42 from cubicweb.server.utils import cleanup_solutions
    42 from cubicweb.server.utils import cleanup_solutions
    43 
    43 
    44 def _new_var(select, varname): 
    44 def _new_var(select, varname):
    45     newvar = select.get_variable(varname)
    45     newvar = select.get_variable(varname)
    46     if not 'relations' in newvar.stinfo:
    46     if not 'relations' in newvar.stinfo:
    47         # not yet initialized
    47         # not yet initialized
    48         newvar.prepare_annotation()
    48         newvar.prepare_annotation()
    49         newvar.stinfo['scope'] = select
    49         newvar.stinfo['scope'] = select
    59                 newivar = _new_var(newselect, vref.name)
    59                 newivar = _new_var(newselect, vref.name)
    60                 newselect.selection.append(VariableRef(newivar))
    60                 newselect.selection.append(VariableRef(newivar))
    61                 _fill_to_wrap_rel(vref.variable, newselect, towrap, schema)
    61                 _fill_to_wrap_rel(vref.variable, newselect, towrap, schema)
    62         elif rschema.is_final():
    62         elif rschema.is_final():
    63             towrap.add( (var, rel) )
    63             towrap.add( (var, rel) )
    64    
    64 
    65 def rewrite_unstable_outer_join(select, solutions, unstable, schema):
    65 def rewrite_unstable_outer_join(select, solutions, unstable, schema):
    66     """if some optional variables are unstable, they should be selected in a
    66     """if some optional variables are unstable, they should be selected in a
    67     subquery. This function check this and rewrite the rql syntax tree if
    67     subquery. This function check this and rewrite the rql syntax tree if
    68     necessary (in place). Return a boolean telling if the tree has been modified
    68     necessary (in place). Return a boolean telling if the tree has been modified
    69     """
    69     """
   102             for vref in newrel.children[1].iget_nodes(VariableRef):
   102             for vref in newrel.children[1].iget_nodes(VariableRef):
   103                 var = vref.variable
   103                 var = vref.variable
   104                 var.stinfo['relations'].add(newrel)
   104                 var.stinfo['relations'].add(newrel)
   105                 var.stinfo['rhsrelations'].add(newrel)
   105                 var.stinfo['rhsrelations'].add(newrel)
   106                 if rel.optional in ('right', 'both'):
   106                 if rel.optional in ('right', 'both'):
   107                     var.stinfo['optrelations'].add(newrel)                
   107                     var.stinfo['optrelations'].add(newrel)
   108         # extract subquery solutions
   108         # extract subquery solutions
   109         solutions = [sol.copy() for sol in solutions]
   109         solutions = [sol.copy() for sol in solutions]
   110         cleanup_solutions(newselect, solutions)
   110         cleanup_solutions(newselect, solutions)
   111         newselect.set_possible_types(solutions)
   111         newselect.set_possible_types(solutions)
   112         # full sub-query
   112         # full sub-query
   203             append(term)
   203             append(term)
   204             if groups:
   204             if groups:
   205                 for vref in term.iget_nodes(VariableRef):
   205                 for vref in term.iget_nodes(VariableRef):
   206                     if not vref in groups:
   206                     if not vref in groups:
   207                         groups.append(vref)
   207                         groups.append(vref)
   208         
   208 
   209 def fix_selection(rqlst, selectedidx, needwrap, sorts, groups, having):
   209 def fix_selection(rqlst, selectedidx, needwrap, sorts, groups, having):
   210     if sorts:
   210     if sorts:
   211         sort_term_selection(sorts, selectedidx, rqlst, not needwrap and groups)
   211         sort_term_selection(sorts, selectedidx, rqlst, not needwrap and groups)
   212     if needwrap:
   212     if needwrap:
   213         if groups:
   213         if groups:
   228 class StateInfo(object):
   228 class StateInfo(object):
   229     def __init__(self, existssols, unstablevars):
   229     def __init__(self, existssols, unstablevars):
   230         self.existssols = existssols
   230         self.existssols = existssols
   231         self.unstablevars = unstablevars
   231         self.unstablevars = unstablevars
   232         self.subtables = {}
   232         self.subtables = {}
   233         
   233 
   234     def reset(self, solution):
   234     def reset(self, solution):
   235         """reset some visit variables"""
   235         """reset some visit variables"""
   236         self.solution = solution
   236         self.solution = solution
   237         self.count = 0
   237         self.count = 0
   238         self.done = set()
   238         self.done = set()
   244         self.duplicate_switches = []
   244         self.duplicate_switches = []
   245         self.attr_vars = {}
   245         self.attr_vars = {}
   246         self.aliases = {}
   246         self.aliases = {}
   247         self.restrictions = []
   247         self.restrictions = []
   248         self._restr_stack = []
   248         self._restr_stack = []
   249         
   249 
   250     def add_restriction(self, restr):
   250     def add_restriction(self, restr):
   251         if restr:
   251         if restr:
   252             self.restrictions.append(restr)
   252             self.restrictions.append(restr)
   253             
   253 
   254     def iter_exists_sols(self, exists):
   254     def iter_exists_sols(self, exists):
   255         if not exists in self.existssols:
   255         if not exists in self.existssols:
   256             yield 1
   256             yield 1
   257             return
   257             return
   258         thisexistssols, thisexistsvars = self.existssols[exists]
   258         thisexistssols, thisexistsvars = self.existssols[exists]
   284 
   284 
   285     def pop_scope(self):
   285     def pop_scope(self):
   286         restrictions = self.restrictions
   286         restrictions = self.restrictions
   287         self.restrictions = self._restr_stack.pop()
   287         self.restrictions = self._restr_stack.pop()
   288         return restrictions, self.actual_tables.pop()
   288         return restrictions, self.actual_tables.pop()
   289     
   289 
   290     
   290 
   291 class SQLGenerator(object):
   291 class SQLGenerator(object):
   292     """
   292     """
   293     generation of SQL from the fully expanded RQL syntax tree
   293     generation of SQL from the fully expanded RQL syntax tree
   294     SQL is designed to be used with a CubicWeb SQL schema
   294     SQL is designed to be used with a CubicWeb SQL schema
   295 
   295 
   296     Groups and sort are not handled here since they should not be handled at
   296     Groups and sort are not handled here since they should not be handled at
   297     this level (see cubicweb.server.querier)
   297     this level (see cubicweb.server.querier)
   298     
   298 
   299     we should not have errors here !
   299     we should not have errors here !
   300 
   300 
   301     WARNING: a CubicWebSQLGenerator instance is not thread safe, but generate is
   301     WARNING: a CubicWebSQLGenerator instance is not thread safe, but generate is
   302     protected by a lock
   302     protected by a lock
   303     """
   303     """
   304     
   304 
   305     def __init__(self, schema, dbms_helper, dbencoding='UTF-8'):
   305     def __init__(self, schema, dbms_helper, dbencoding='UTF-8'):
   306         self.schema = schema
   306         self.schema = schema
   307         self.dbms_helper = dbms_helper
   307         self.dbms_helper = dbms_helper
   308         self.dbencoding = dbencoding
   308         self.dbencoding = dbencoding
   309         self.keyword_map = {'NOW' : self.dbms_helper.sql_current_timestamp,
   309         self.keyword_map = {'NOW' : self.dbms_helper.sql_current_timestamp,
   310                             'TODAY': self.dbms_helper.sql_current_date,
   310                             'TODAY': self.dbms_helper.sql_current_date,
   311                             }
   311                             }
   312         if not self.dbms_helper.union_parentheses_support:
   312         if not self.dbms_helper.union_parentheses_support:
   313             self.union_sql = self.noparen_union_sql
   313             self.union_sql = self.noparen_union_sql
   314         self._lock = threading.Lock()
   314         self._lock = threading.Lock()
   315         
   315 
   316     def generate(self, union, args=None, varmap=None):
   316     def generate(self, union, args=None, varmap=None):
   317         """return SQL queries and a variable dictionnary from a RQL syntax tree
   317         """return SQL queries and a variable dictionnary from a RQL syntax tree
   318 
   318 
   319         :partrqls: a list of couple (rqlst, solutions)
   319         :partrqls: a list of couple (rqlst, solutions)
   320         :args: optional dictionary with values of substitutions used in the query
   320         :args: optional dictionary with values of substitutions used in the query
   353         # the subquery) but will work in most case
   353         # the subquery) but will work in most case
   354         # see http://www.sqlite.org/cvstrac/tktview?tn=3074
   354         # see http://www.sqlite.org/cvstrac/tktview?tn=3074
   355         sqls = (self.select_sql(select, needalias)
   355         sqls = (self.select_sql(select, needalias)
   356                 for i, select in enumerate(union.children))
   356                 for i, select in enumerate(union.children))
   357         return '\nUNION ALL\n'.join(sqls)
   357         return '\nUNION ALL\n'.join(sqls)
   358     
   358 
   359     def select_sql(self, select, needalias=False):
   359     def select_sql(self, select, needalias=False):
   360         """return SQL queries and a variable dictionnary from a RQL syntax tree
   360         """return SQL queries and a variable dictionnary from a RQL syntax tree
   361 
   361 
   362         :select: a selection statement of the syntax tree (`rql.stmts.Select`)
   362         :select: a selection statement of the syntax tree (`rql.stmts.Select`)
   363         :solution: a dictionnary containing variables binding.
   363         :solution: a dictionnary containing variables binding.
   386                 selectsortterms = True
   386                 selectsortterms = True
   387                 # and if select is using group by or aggregat, a wrapping
   387                 # and if select is using group by or aggregat, a wrapping
   388                 # query will be necessary
   388                 # query will be necessary
   389                 if groups or select.has_aggregat:
   389                 if groups or select.has_aggregat:
   390                     select.select_only_variables()
   390                     select.select_only_variables()
   391                     needwrap = True                        
   391                     needwrap = True
   392         else:
   392         else:
   393             existssols, unstable = {}, ()
   393             existssols, unstable = {}, ()
   394         state = StateInfo(existssols, unstable)
   394         state = StateInfo(existssols, unstable)
   395         # treat subqueries
   395         # treat subqueries
   396         self._subqueries_sql(select, state)
   396         self._subqueries_sql(select, state)
   439                 sql += '\nGROUP BY %s' % groups
   439                 sql += '\nGROUP BY %s' % groups
   440             if having:
   440             if having:
   441                 sql += '\nHAVING %s' % having
   441                 sql += '\nHAVING %s' % having
   442             # sort
   442             # sort
   443             if sorts:
   443             if sorts:
   444                 sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm, 
   444                 sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm,
   445                                                                      fselectidx)
   445                                                                      fselectidx)
   446                                                   for sortterm in sorts)
   446                                                   for sortterm in sorts)
   447                 if fneedwrap:
   447                 if fneedwrap:
   448                     selection = ['T1.C%s' % i for i in xrange(len(origselection))]
   448                     selection = ['T1.C%s' % i for i in xrange(len(origselection))]
   449                     sql = 'SELECT %s FROM (%s) AS T1' % (','.join(selection), sql)
   449                     sql = 'SELECT %s FROM (%s) AS T1' % (','.join(selection), sql)
   495                 return '\nINTERSECT ALL\n'.join(sqls)
   495                 return '\nINTERSECT ALL\n'.join(sqls)
   496         elif distinct:
   496         elif distinct:
   497             return '\nUNION\n'.join(sqls)
   497             return '\nUNION\n'.join(sqls)
   498         else:
   498         else:
   499             return '\nUNION ALL\n'.join(sqls)
   499             return '\nUNION ALL\n'.join(sqls)
   500         
   500 
   501     def _selection_sql(self, selected, distinct, needaliasing=False):
   501     def _selection_sql(self, selected, distinct, needaliasing=False):
   502         clause = []
   502         clause = []
   503         for term in selected:
   503         for term in selected:
   504             sql = term.accept(self)
   504             sql = term.accept(self)
   505             if needaliasing:
   505             if needaliasing:
   544         if res:
   544         if res:
   545             if len(res) > 1:
   545             if len(res) > 1:
   546                 return '(%s)' % ' OR '.join(res)
   546                 return '(%s)' % ' OR '.join(res)
   547             return res[0]
   547             return res[0]
   548         return ''
   548         return ''
   549     
   549 
   550     def visit_not(self, node):
   550     def visit_not(self, node):
   551         self._state.push_scope()
   551         self._state.push_scope()
   552         csql = node.children[0].accept(self)
   552         csql = node.children[0].accept(self)
   553         sqls, tables = self._state.pop_scope()
   553         sqls, tables = self._state.pop_scope()
   554         if node in self._state.done or not csql:
   554         if node in self._state.done or not csql:
   579             if sql:
   579             if sql:
   580                 sqls.append(sql)
   580                 sqls.append(sql)
   581         if not sqls:
   581         if not sqls:
   582             return ''
   582             return ''
   583         return 'EXISTS(%s)' % ' UNION '.join(sqls)
   583         return 'EXISTS(%s)' % ' UNION '.join(sqls)
   584             
   584 
   585     def _visit_exists(self, exists):
   585     def _visit_exists(self, exists):
   586         self._state.push_scope()
   586         self._state.push_scope()
   587         restriction = exists.children[0].accept(self)
   587         restriction = exists.children[0].accept(self)
   588         restrictions, tables = self._state.pop_scope()
   588         restrictions, tables = self._state.pop_scope()
   589         if restriction:
   589         if restriction:
   591         restriction = ' AND '.join(restrictions)
   591         restriction = ' AND '.join(restrictions)
   592         if not restriction:
   592         if not restriction:
   593             return ''
   593             return ''
   594         if not tables:
   594         if not tables:
   595             # XXX could leave surrounding EXISTS() in this case no?
   595             # XXX could leave surrounding EXISTS() in this case no?
   596             sql = 'SELECT 1 WHERE %s' % restriction 
   596             sql = 'SELECT 1 WHERE %s' % restriction
   597         else:
   597         else:
   598             sql = 'SELECT 1 FROM %s WHERE %s' % (', '.join(tables), restriction)
   598             sql = 'SELECT 1 FROM %s WHERE %s' % (', '.join(tables), restriction)
   599         return sql
   599         return sql
   600 
   600 
   601     
   601 
   602     def visit_relation(self, relation):
   602     def visit_relation(self, relation):
   603         """generate SQL for a relation"""
   603         """generate SQL for a relation"""
   604         rtype = relation.r_type
   604         rtype = relation.r_type
   605         # don't care of type constraint statement (i.e. relation_type = 'is')
   605         # don't care of type constraint statement (i.e. relation_type = 'is')
   606         if relation.is_types_restriction():
   606         if relation.is_types_restriction():
   689                 termsql = termvar.accept(self)
   689                 termsql = termvar.accept(self)
   690                 yield '%s.%s=%s' % (rid, relfield, termsql)
   690                 yield '%s.%s=%s' % (rid, relfield, termsql)
   691             extrajoin = self._extra_join_sql(relation, '%s.%s' % (rid, relfield), termvar)
   691             extrajoin = self._extra_join_sql(relation, '%s.%s' % (rid, relfield), termvar)
   692             if extrajoin:
   692             if extrajoin:
   693                 yield extrajoin
   693                 yield extrajoin
   694         
   694 
   695     def _visit_relation(self, relation, rschema):
   695     def _visit_relation(self, relation, rschema):
   696         """generate SQL for a relation
   696         """generate SQL for a relation
   697 
   697 
   698         implements optimization 1.
   698         implements optimization 1.
   699         """
   699         """
   716 
   716 
   717     def _visit_outer_join_relation(self, relation, rschema):
   717     def _visit_outer_join_relation(self, relation, rschema):
   718         """
   718         """
   719         left outer join syntax (optional=='right'):
   719         left outer join syntax (optional=='right'):
   720           X relation Y?
   720           X relation Y?
   721           
   721 
   722         right outer join syntax (optional=='left'):
   722         right outer join syntax (optional=='left'):
   723           X? relation Y
   723           X? relation Y
   724           
   724 
   725         full outer join syntaxes (optional=='both'):
   725         full outer join syntaxes (optional=='both'):
   726           X? relation Y?
   726           X? relation Y?
   727 
   727 
   728         if relation is inlined:
   728         if relation is inlined:
   729            if it's a left outer join:
   729            if it's a left outer join:
   832         # at least one variable is already in attr_vars, this means we have to
   832         # at least one variable is already in attr_vars, this means we have to
   833         # generate unification expression
   833         # generate unification expression
   834         lhssql = self._inlined_var_sql(relation.children[0].variable,
   834         lhssql = self._inlined_var_sql(relation.children[0].variable,
   835                                        relation.r_type)
   835                                        relation.r_type)
   836         return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels))
   836         return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels))
   837     
   837 
   838     def _visit_attribute_relation(self, relation):
   838     def _visit_attribute_relation(self, relation):
   839         """generate SQL for an attribute relation"""
   839         """generate SQL for an attribute relation"""
   840         lhs, rhs = relation.get_parts()
   840         lhs, rhs = relation.get_parts()
   841         rhssql = rhs.accept(self)
   841         rhssql = rhs.accept(self)
   842         table = self._var_table(lhs.variable)
   842         table = self._var_table(lhs.variable)
   895             not_ = True
   895             not_ = True
   896         else:
   896         else:
   897             not_ = False
   897             not_ = False
   898         return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args),
   898         return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args),
   899                                                     jointo, not_) + restriction
   899                                                     jointo, not_) + restriction
   900         
   900 
   901     def visit_comparison(self, cmp, contextrels=None):
   901     def visit_comparison(self, cmp, contextrels=None):
   902         """generate SQL for a comparaison"""
   902         """generate SQL for a comparaison"""
   903         if len(cmp.children) == 2:
   903         if len(cmp.children) == 2:
   904             lhs, rhs = cmp.children
   904             lhs, rhs = cmp.children
   905         else:
   905         else:
   916             operator = ' '
   916             operator = ' '
   917         if lhs is None:
   917         if lhs is None:
   918             return '%s%s'% (operator, rhs.accept(self, contextrels))
   918             return '%s%s'% (operator, rhs.accept(self, contextrels))
   919         return '%s%s%s'% (lhs.accept(self, contextrels), operator,
   919         return '%s%s%s'% (lhs.accept(self, contextrels), operator,
   920                           rhs.accept(self, contextrels))
   920                           rhs.accept(self, contextrels))
   921             
   921 
   922     def visit_mathexpression(self, mexpr, contextrels=None):
   922     def visit_mathexpression(self, mexpr, contextrels=None):
   923         """generate SQL for a mathematic expression"""
   923         """generate SQL for a mathematic expression"""
   924         lhs, rhs = mexpr.get_parts()
   924         lhs, rhs = mexpr.get_parts()
   925         # check for string concatenation
   925         # check for string concatenation
   926         operator = mexpr.operator
   926         operator = mexpr.operator
   929                 operator = '||'
   929                 operator = '||'
   930         except CoercionError:
   930         except CoercionError:
   931             pass
   931             pass
   932         return '(%s %s %s)'% (lhs.accept(self, contextrels), operator,
   932         return '(%s %s %s)'% (lhs.accept(self, contextrels), operator,
   933                               rhs.accept(self, contextrels))
   933                               rhs.accept(self, contextrels))
   934         
   934 
   935     def visit_function(self, func, contextrels=None):
   935     def visit_function(self, func, contextrels=None):
   936         """generate SQL name for a function"""
   936         """generate SQL name for a function"""
   937         # function_description will check function is supported by the backend
   937         # function_description will check function is supported by the backend
   938         sqlname = self.dbms_helper.func_sqlname(func.name) 
   938         sqlname = self.dbms_helper.func_sqlname(func.name)
   939         return '%s(%s)' % (sqlname, ', '.join(c.accept(self, contextrels)
   939         return '%s(%s)' % (sqlname, ', '.join(c.accept(self, contextrels)
   940                                               for c in func.children))
   940                                               for c in func.children))
   941 
   941 
   942     def visit_constant(self, constant, contextrels=None):
   942     def visit_constant(self, constant, contextrels=None):
   943         """generate SQL name for a constant"""
   943         """generate SQL name for a constant"""
   961             _id = str(id(constant)).replace('-', '', 1)
   961             _id = str(id(constant)).replace('-', '', 1)
   962             if isinstance(value, unicode):
   962             if isinstance(value, unicode):
   963                 value = value.encode(self.dbencoding)
   963                 value = value.encode(self.dbencoding)
   964             self._query_attrs[_id] = value
   964             self._query_attrs[_id] = value
   965         return '%%(%s)s' % _id
   965         return '%%(%s)s' % _id
   966         
   966 
   967     def visit_variableref(self, variableref, contextrels=None):
   967     def visit_variableref(self, variableref, contextrels=None):
   968         """get the sql name for a variable reference"""
   968         """get the sql name for a variable reference"""
   969         # use accept, .variable may be a variable or a columnalias
   969         # use accept, .variable may be a variable or a columnalias
   970         return variableref.variable.accept(self, contextrels)
   970         return variableref.variable.accept(self, contextrels)
   971 
   971 
   977             colalias._q_sqltable = table
   977             colalias._q_sqltable = table
   978             colalias._q_sql = sql
   978             colalias._q_sql = sql
   979             self.add_table(table)
   979             self.add_table(table)
   980             return sql
   980             return sql
   981         return colalias._q_sql
   981         return colalias._q_sql
   982     
   982 
   983     def visit_variable(self, variable, contextrels=None):
   983     def visit_variable(self, variable, contextrels=None):
   984         """get the table name and sql string for a variable"""
   984         """get the table name and sql string for a variable"""
   985         if contextrels is None and variable.name in self._state.done:
   985         if contextrels is None and variable.name in self._state.done:
   986             if self._in_wrapping_query:
   986             if self._in_wrapping_query:
   987                 return 'T1.%s' % self._state.aliases[variable.name]
   987                 return 'T1.%s' % self._state.aliases[variable.name]
   988             return variable._q_sql
   988             return variable._q_sql
   989         self._state.done.add(variable.name)
   989         self._state.done.add(variable.name)
   990         vtablename = None
   990         vtablename = None
   991         if contextrels is None and variable.name in self._varmap:
   991         if contextrels is None and variable.name in self._varmap:
   992             sql, vtablename = self._var_info(variable)            
   992             sql, vtablename = self._var_info(variable)
   993         elif variable.stinfo['attrvar']:
   993         elif variable.stinfo['attrvar']:
   994             # attribute variable (systematically used in rhs of final
   994             # attribute variable (systematically used in rhs of final
   995             # relation(s)), get table name and sql from any rhs relation
   995             # relation(s)), get table name and sql from any rhs relation
   996             sql = self._linked_var_sql(variable, contextrels)
   996             sql = self._linked_var_sql(variable, contextrels)
   997         elif variable._q_invariant:
   997         elif variable._q_invariant:
  1041         except KeyError:
  1041         except KeyError:
  1042             # no principal defined, relation is necessarily the principal and
  1042             # no principal defined, relation is necessarily the principal and
  1043             # so nothing to return here
  1043             # so nothing to return here
  1044             pass
  1044             pass
  1045         return ''
  1045         return ''
  1046     
  1046 
  1047     def _var_info(self, var):
  1047     def _var_info(self, var):
  1048         # if current var or one of its attribute is selected , it *must*
  1048         # if current var or one of its attribute is selected , it *must*
  1049         # appear in the toplevel's FROM even if we're currently visiting
  1049         # appear in the toplevel's FROM even if we're currently visiting
  1050         # a EXISTS node
  1050         # a EXISTS node
  1051         if var.sqlscope is var.stmt:
  1051         if var.sqlscope is var.stmt:
  1065                 raise BadRQLQuery(var.stmt.root)
  1065                 raise BadRQLQuery(var.stmt.root)
  1066             table = var.name
  1066             table = var.name
  1067             sql = '%s.%seid' % (table, SQL_PREFIX)
  1067             sql = '%s.%seid' % (table, SQL_PREFIX)
  1068             self.add_table('%s%s AS %s' % (SQL_PREFIX, etype, table), table, scope=scope)
  1068             self.add_table('%s%s AS %s' % (SQL_PREFIX, etype, table), table, scope=scope)
  1069         return sql, table
  1069         return sql, table
  1070     
  1070 
  1071     def _inlined_var_sql(self, var, rtype):
  1071     def _inlined_var_sql(self, var, rtype):
  1072         try:
  1072         try:
  1073             sql = self._varmap['%s.%s' % (var.name, rtype)]
  1073             sql = self._varmap['%s.%s' % (var.name, rtype)]
  1074             scope = var.sqlscope is var.stmt and 0 or -1
  1074             scope = var.sqlscope is var.stmt and 0 or -1
  1075             self.add_table(sql.split('.', 1)[0], scope=scope)
  1075             self.add_table(sql.split('.', 1)[0], scope=scope)
  1076         except KeyError:
  1076         except KeyError:
  1077             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
  1077             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
  1078             #self._state.done.add(var.name)
  1078             #self._state.done.add(var.name)
  1079         return sql
  1079         return sql
  1080         
  1080 
  1081     def _linked_var_sql(self, variable, contextrels=None):
  1081     def _linked_var_sql(self, variable, contextrels=None):
  1082         if contextrels is None:
  1082         if contextrels is None:
  1083             try:
  1083             try:
  1084                 return self._varmap[variable.name]            
  1084                 return self._varmap[variable.name]
  1085             except KeyError:
  1085             except KeyError:
  1086                 pass
  1086                 pass
  1087         rel = (contextrels and contextrels.get(variable.name) or 
  1087         rel = (contextrels and contextrels.get(variable.name) or
  1088                variable.stinfo.get('principal') or
  1088                variable.stinfo.get('principal') or
  1089                iter(variable.stinfo['rhsrelations']).next())
  1089                iter(variable.stinfo['rhsrelations']).next())
  1090         linkedvar = rel.children[0].variable
  1090         linkedvar = rel.children[0].variable
  1091         if rel.r_type == 'eid':
  1091         if rel.r_type == 'eid':
  1092             return linkedvar.accept(self)
  1092             return linkedvar.accept(self)
  1094             raise BadRQLQuery('variable %s should be selected by the subquery'
  1094             raise BadRQLQuery('variable %s should be selected by the subquery'
  1095                               % variable.name)
  1095                               % variable.name)
  1096         try:
  1096         try:
  1097             sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)]
  1097             sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)]
  1098         except KeyError:
  1098         except KeyError:
  1099             linkedvar.accept(self)            
  1099             linkedvar.accept(self)
  1100             sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type)
  1100             sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type)
  1101         return sql
  1101         return sql
  1102 
  1102 
  1103     # tables handling #########################################################
  1103     # tables handling #########################################################
  1104 
  1104 
  1105     def alias_and_add_table(self, tablename):
  1105     def alias_and_add_table(self, tablename):
  1106         alias = '%s%s' % (tablename, self._state.count)
  1106         alias = '%s%s' % (tablename, self._state.count)
  1107         self._state.count += 1
  1107         self._state.count += 1
  1108         self.add_table('%s AS %s' % (tablename, alias), alias)
  1108         self.add_table('%s AS %s' % (tablename, alias), alias)
  1109         return alias
  1109         return alias
  1110         
  1110 
  1111     def add_table(self, table, key=None, scope=-1):
  1111     def add_table(self, table, key=None, scope=-1):
  1112         if key is None:
  1112         if key is None:
  1113             key = table
  1113             key = table
  1114         if key in self._state.tables:
  1114         if key in self._state.tables:
  1115             return
  1115             return
  1116         self._state.tables[key] = (len(self._state.actual_tables) - 1, table)
  1116         self._state.tables[key] = (len(self._state.actual_tables) - 1, table)
  1117         self._state.actual_tables[scope].append(table)
  1117         self._state.actual_tables[scope].append(table)
  1118     
  1118 
  1119     def replace_tables_by_outer_join(self, substitute, lefttable, *tables):
  1119     def replace_tables_by_outer_join(self, substitute, lefttable, *tables):
  1120         for table in tables:
  1120         for table in tables:
  1121             try:
  1121             try:
  1122                 scope, alias = self._state.tables[table]
  1122                 scope, alias = self._state.tables[table]
  1123                 self._state.actual_tables[scope].remove(alias)
  1123                 self._state.actual_tables[scope].remove(alias)
  1158         # by <oldalias> may not be reused here, though their associated value
  1158         # by <oldalias> may not be reused here, though their associated value
  1159         # in the outer_tables dict has to be updated as well
  1159         # in the outer_tables dict has to be updated as well
  1160         for table, outerexpr in self._state.outer_tables.iteritems():
  1160         for table, outerexpr in self._state.outer_tables.iteritems():
  1161             if outerexpr == oldalias:
  1161             if outerexpr == oldalias:
  1162                 self._state.outer_tables[table] = newalias
  1162                 self._state.outer_tables[table] = newalias
  1163         self._state.outer_tables[table] = newalias        
  1163         self._state.outer_tables[table] = newalias
  1164         
  1164 
  1165     def _var_table(self, var):
  1165     def _var_table(self, var):
  1166         var.accept(self)#.visit_variable(var)
  1166         var.accept(self)#.visit_variable(var)
  1167         return var._q_sqltable
  1167         return var._q_sqltable
  1168 
  1168 
  1169     def _relation_table(self, relation):
  1169     def _relation_table(self, relation):
  1171         if relation in self._state.done:
  1171         if relation in self._state.done:
  1172             return relation._q_sqltable
  1172             return relation._q_sqltable
  1173         assert not self.schema.rschema(relation.r_type).is_final(), relation.r_type
  1173         assert not self.schema.rschema(relation.r_type).is_final(), relation.r_type
  1174         rid = 'rel_%s%s' % (relation.r_type, self._state.count)
  1174         rid = 'rel_%s%s' % (relation.r_type, self._state.count)
  1175         # relation's table is belonging to the root scope if it is the principal
  1175         # relation's table is belonging to the root scope if it is the principal
  1176         # table of one of it's variable and if that variable belong's to parent 
  1176         # table of one of it's variable and if that variable belong's to parent
  1177         # scope
  1177         # scope
  1178         for varref in relation.iget_nodes(VariableRef):
  1178         for varref in relation.iget_nodes(VariableRef):
  1179             var = varref.variable
  1179             var = varref.variable
  1180             if isinstance(var, ColumnAlias):
  1180             if isinstance(var, ColumnAlias):
  1181                 scope = 0
  1181                 scope = 0
  1190         self._state.count += 1
  1190         self._state.count += 1
  1191         self.add_table('%s_relation AS %s' % (relation.r_type, rid), rid, scope=scope)
  1191         self.add_table('%s_relation AS %s' % (relation.r_type, rid), rid, scope=scope)
  1192         relation._q_sqltable = rid
  1192         relation._q_sqltable = rid
  1193         self._state.done.add(relation)
  1193         self._state.done.add(relation)
  1194         return rid
  1194         return rid
  1195     
  1195 
  1196     def _fti_table(self, relation):
  1196     def _fti_table(self, relation):
  1197         if relation in self._state.done:
  1197         if relation in self._state.done:
  1198             try:
  1198             try:
  1199                 return relation._q_sqltable
  1199                 return relation._q_sqltable
  1200             except AttributeError:
  1200             except AttributeError:
  1201                 pass
  1201                 pass
  1202         self._state.done.add(relation)
  1202         self._state.done.add(relation)
  1203         alias = self.alias_and_add_table(self.dbms_helper.fti_table)
  1203         alias = self.alias_and_add_table(self.dbms_helper.fti_table)
  1204         relation._q_sqltable = alias
  1204         relation._q_sqltable = alias
  1205         return alias
  1205         return alias
  1206         
  1206 
  1207     def _varmap_table_scope(self, select, table):
  1207     def _varmap_table_scope(self, select, table):
  1208         """since a varmap table may be used for multiple variable, its scope is
  1208         """since a varmap table may be used for multiple variable, its scope is
  1209         the most outer scope of each variables
  1209         the most outer scope of each variables
  1210         """
  1210         """
  1211         scope = -1
  1211         scope = -1