server/sources/rql2sql.py
branchtls-sprint
changeset 1802 d628defebc17
parent 1522 47b2ffbee760
child 1862 94dc8ccd320b
--- a/server/sources/rql2sql.py	Thu May 14 10:24:56 2009 +0200
+++ b/server/sources/rql2sql.py	Thu May 14 11:38:40 2009 +0200
@@ -17,7 +17,7 @@
 
    -> direct join between nonfinal1 and nonfinal2, whatever X,Y, Z (unless
       inlined...)
-      
+
       NOT IMPLEMENTED (and quite hard to implement)
 
 Potential optimization information is collected by the querier, sql generation
@@ -41,7 +41,7 @@
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.utils import cleanup_solutions
 
-def _new_var(select, varname): 
+def _new_var(select, varname):
     newvar = select.get_variable(varname)
     if not 'relations' in newvar.stinfo:
         # not yet initialized
@@ -61,7 +61,7 @@
                 _fill_to_wrap_rel(vref.variable, newselect, towrap, schema)
         elif rschema.is_final():
             towrap.add( (var, rel) )
-   
+
 def rewrite_unstable_outer_join(select, solutions, unstable, schema):
     """if some optional variables are unstable, they should be selected in a
     subquery. This function check this and rewrite the rql syntax tree if
@@ -104,7 +104,7 @@
                 var.stinfo['relations'].add(newrel)
                 var.stinfo['rhsrelations'].add(newrel)
                 if rel.optional in ('right', 'both'):
-                    var.stinfo['optrelations'].add(newrel)                
+                    var.stinfo['optrelations'].add(newrel)
         # extract subquery solutions
         solutions = [sol.copy() for sol in solutions]
         cleanup_solutions(newselect, solutions)
@@ -205,7 +205,7 @@
                 for vref in term.iget_nodes(VariableRef):
                     if not vref in groups:
                         groups.append(vref)
-        
+
 def fix_selection(rqlst, selectedidx, needwrap, sorts, groups, having):
     if sorts:
         sort_term_selection(sorts, selectedidx, rqlst, not needwrap and groups)
@@ -230,7 +230,7 @@
         self.existssols = existssols
         self.unstablevars = unstablevars
         self.subtables = {}
-        
+
     def reset(self, solution):
         """reset some visit variables"""
         self.solution = solution
@@ -246,11 +246,11 @@
         self.aliases = {}
         self.restrictions = []
         self._restr_stack = []
-        
+
     def add_restriction(self, restr):
         if restr:
             self.restrictions.append(restr)
-            
+
     def iter_exists_sols(self, exists):
         if not exists in self.existssols:
             yield 1
@@ -286,8 +286,8 @@
         restrictions = self.restrictions
         self.restrictions = self._restr_stack.pop()
         return restrictions, self.actual_tables.pop()
-    
-    
+
+
 class SQLGenerator(object):
     """
     generation of SQL from the fully expanded RQL syntax tree
@@ -295,13 +295,13 @@
 
     Groups and sort are not handled here since they should not be handled at
     this level (see cubicweb.server.querier)
-    
+
     we should not have errors here !
 
     WARNING: a CubicWebSQLGenerator instance is not thread safe, but generate is
     protected by a lock
     """
-    
+
     def __init__(self, schema, dbms_helper, dbencoding='UTF-8'):
         self.schema = schema
         self.dbms_helper = dbms_helper
@@ -312,7 +312,7 @@
         if not self.dbms_helper.union_parentheses_support:
             self.union_sql = self.noparen_union_sql
         self._lock = threading.Lock()
-        
+
     def generate(self, union, args=None, varmap=None):
         """return SQL queries and a variable dictionnary from a RQL syntax tree
 
@@ -355,7 +355,7 @@
         sqls = (self.select_sql(select, needalias)
                 for i, select in enumerate(union.children))
         return '\nUNION ALL\n'.join(sqls)
-    
+
     def select_sql(self, select, needalias=False):
         """return SQL queries and a variable dictionnary from a RQL syntax tree
 
@@ -388,7 +388,7 @@
                 # query will be necessary
                 if groups or select.has_aggregat:
                     select.select_only_variables()
-                    needwrap = True                        
+                    needwrap = True
         else:
             existssols, unstable = {}, ()
         state = StateInfo(existssols, unstable)
@@ -441,7 +441,7 @@
                 sql += '\nHAVING %s' % having
             # sort
             if sorts:
-                sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm, 
+                sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm,
                                                                      fselectidx)
                                                   for sortterm in sorts)
                 if fneedwrap:
@@ -497,7 +497,7 @@
             return '\nUNION\n'.join(sqls)
         else:
             return '\nUNION ALL\n'.join(sqls)
-        
+
     def _selection_sql(self, selected, distinct, needaliasing=False):
         clause = []
         for term in selected:
@@ -546,7 +546,7 @@
                 return '(%s)' % ' OR '.join(res)
             return res[0]
         return ''
-    
+
     def visit_not(self, node):
         self._state.push_scope()
         csql = node.children[0].accept(self)
@@ -581,7 +581,7 @@
         if not sqls:
             return ''
         return 'EXISTS(%s)' % ' UNION '.join(sqls)
-            
+
     def _visit_exists(self, exists):
         self._state.push_scope()
         restriction = exists.children[0].accept(self)
@@ -593,12 +593,12 @@
             return ''
         if not tables:
             # XXX could leave surrounding EXISTS() in this case no?
-            sql = 'SELECT 1 WHERE %s' % restriction 
+            sql = 'SELECT 1 WHERE %s' % restriction
         else:
             sql = 'SELECT 1 FROM %s WHERE %s' % (', '.join(tables), restriction)
         return sql
 
-    
+
     def visit_relation(self, relation):
         """generate SQL for a relation"""
         rtype = relation.r_type
@@ -691,7 +691,7 @@
             extrajoin = self._extra_join_sql(relation, '%s.%s' % (rid, relfield), termvar)
             if extrajoin:
                 yield extrajoin
-        
+
     def _visit_relation(self, relation, rschema):
         """generate SQL for a relation
 
@@ -718,10 +718,10 @@
         """
         left outer join syntax (optional=='right'):
           X relation Y?
-          
+
         right outer join syntax (optional=='left'):
           X? relation Y
-          
+
         full outer join syntaxes (optional=='both'):
           X? relation Y?
 
@@ -834,7 +834,7 @@
         lhssql = self._inlined_var_sql(relation.children[0].variable,
                                        relation.r_type)
         return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels))
-    
+
     def _visit_attribute_relation(self, relation):
         """generate SQL for an attribute relation"""
         lhs, rhs = relation.get_parts()
@@ -897,7 +897,7 @@
             not_ = False
         return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args),
                                                     jointo, not_) + restriction
-        
+
     def visit_comparison(self, cmp, contextrels=None):
         """generate SQL for a comparaison"""
         if len(cmp.children) == 2:
@@ -918,7 +918,7 @@
             return '%s%s'% (operator, rhs.accept(self, contextrels))
         return '%s%s%s'% (lhs.accept(self, contextrels), operator,
                           rhs.accept(self, contextrels))
-            
+
     def visit_mathexpression(self, mexpr, contextrels=None):
         """generate SQL for a mathematic expression"""
         lhs, rhs = mexpr.get_parts()
@@ -931,11 +931,11 @@
             pass
         return '(%s %s %s)'% (lhs.accept(self, contextrels), operator,
                               rhs.accept(self, contextrels))
-        
+
     def visit_function(self, func, contextrels=None):
         """generate SQL name for a function"""
         # function_description will check function is supported by the backend
-        sqlname = self.dbms_helper.func_sqlname(func.name) 
+        sqlname = self.dbms_helper.func_sqlname(func.name)
         return '%s(%s)' % (sqlname, ', '.join(c.accept(self, contextrels)
                                               for c in func.children))
 
@@ -963,7 +963,7 @@
                 value = value.encode(self.dbencoding)
             self._query_attrs[_id] = value
         return '%%(%s)s' % _id
-        
+
     def visit_variableref(self, variableref, contextrels=None):
         """get the sql name for a variable reference"""
         # use accept, .variable may be a variable or a columnalias
@@ -979,7 +979,7 @@
             self.add_table(table)
             return sql
         return colalias._q_sql
-    
+
     def visit_variable(self, variable, contextrels=None):
         """get the table name and sql string for a variable"""
         if contextrels is None and variable.name in self._state.done:
@@ -989,7 +989,7 @@
         self._state.done.add(variable.name)
         vtablename = None
         if contextrels is None and variable.name in self._varmap:
-            sql, vtablename = self._var_info(variable)            
+            sql, vtablename = self._var_info(variable)
         elif variable.stinfo['attrvar']:
             # attribute variable (systematically used in rhs of final
             # relation(s)), get table name and sql from any rhs relation
@@ -1043,7 +1043,7 @@
             # so nothing to return here
             pass
         return ''
-    
+
     def _var_info(self, var):
         # if current var or one of its attribute is selected , it *must*
         # appear in the toplevel's FROM even if we're currently visiting
@@ -1067,7 +1067,7 @@
             sql = '%s.%seid' % (table, SQL_PREFIX)
             self.add_table('%s%s AS %s' % (SQL_PREFIX, etype, table), table, scope=scope)
         return sql, table
-    
+
     def _inlined_var_sql(self, var, rtype):
         try:
             sql = self._varmap['%s.%s' % (var.name, rtype)]
@@ -1077,14 +1077,14 @@
             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
             #self._state.done.add(var.name)
         return sql
-        
+
     def _linked_var_sql(self, variable, contextrels=None):
         if contextrels is None:
             try:
-                return self._varmap[variable.name]            
+                return self._varmap[variable.name]
             except KeyError:
                 pass
-        rel = (contextrels and contextrels.get(variable.name) or 
+        rel = (contextrels and contextrels.get(variable.name) or
                variable.stinfo.get('principal') or
                iter(variable.stinfo['rhsrelations']).next())
         linkedvar = rel.children[0].variable
@@ -1096,7 +1096,7 @@
         try:
             sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)]
         except KeyError:
-            linkedvar.accept(self)            
+            linkedvar.accept(self)
             sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type)
         return sql
 
@@ -1107,7 +1107,7 @@
         self._state.count += 1
         self.add_table('%s AS %s' % (tablename, alias), alias)
         return alias
-        
+
     def add_table(self, table, key=None, scope=-1):
         if key is None:
             key = table
@@ -1115,7 +1115,7 @@
             return
         self._state.tables[key] = (len(self._state.actual_tables) - 1, table)
         self._state.actual_tables[scope].append(table)
-    
+
     def replace_tables_by_outer_join(self, substitute, lefttable, *tables):
         for table in tables:
             try:
@@ -1160,8 +1160,8 @@
         for table, outerexpr in self._state.outer_tables.iteritems():
             if outerexpr == oldalias:
                 self._state.outer_tables[table] = newalias
-        self._state.outer_tables[table] = newalias        
-        
+        self._state.outer_tables[table] = newalias
+
     def _var_table(self, var):
         var.accept(self)#.visit_variable(var)
         return var._q_sqltable
@@ -1173,7 +1173,7 @@
         assert not self.schema.rschema(relation.r_type).is_final(), relation.r_type
         rid = 'rel_%s%s' % (relation.r_type, self._state.count)
         # relation's table is belonging to the root scope if it is the principal
-        # table of one of it's variable and if that variable belong's to parent 
+        # table of one of it's variable and if that variable belong's to parent
         # scope
         for varref in relation.iget_nodes(VariableRef):
             var = varref.variable
@@ -1192,7 +1192,7 @@
         relation._q_sqltable = rid
         self._state.done.add(relation)
         return rid
-    
+
     def _fti_table(self, relation):
         if relation in self._state.done:
             try:
@@ -1203,7 +1203,7 @@
         alias = self.alias_and_add_table(self.dbms_helper.fti_table)
         relation._q_sqltable = alias
         return alias
-        
+
     def _varmap_table_scope(self, select, table):
         """since a varmap table may be used for multiple variable, its scope is
         the most outer scope of each variables