server/sources/rql2sql.py
branchstable
changeset 5582 3e133b29a1a4
parent 5426 0d4853a6e5ee
child 5593 f6c55bec9326
equal deleted inserted replaced
5581:0aae5216f99e 5582:3e133b29a1a4
    42 
    42 
    43 cross RDMS note : read `Comparison of different SQL implementations`_
    43 cross RDMS note : read `Comparison of different SQL implementations`_
    44 by Troels Arvin. Features SQL ISO Standard, PG, mysql, Oracle, MS SQL, DB2
    44 by Troels Arvin. Features SQL ISO Standard, PG, mysql, Oracle, MS SQL, DB2
    45 and Informix.
    45 and Informix.
    46 
    46 
    47 .. _Comparison of different SQL implementations: http://www.troels.arvin.dk/db/rdbms 
    47 .. _Comparison of different SQL implementations: http://www.troels.arvin.dk/db/rdbms
    48 
    48 
    49 
    49 
    50 """
    50 """
    51 __docformat__ = "restructuredtext en"
    51 __docformat__ = "restructuredtext en"
    52 
    52 
   110             continue
   110             continue
   111         modified = True
   111         modified = True
   112         unstable.remove(varname)
   112         unstable.remove(varname)
   113         torewrite.add(var)
   113         torewrite.add(var)
   114         newselect = Select()
   114         newselect = Select()
   115         newselect.need_distinct = newselect.need_intersect = False
   115         newselect.need_distinct = False
   116         myunion = Union()
   116         myunion = Union()
   117         myunion.append(newselect)
   117         myunion.append(newselect)
   118         # extract aliases / selection
   118         # extract aliases / selection
   119         newvar = _new_var(newselect, var.name)
   119         newvar = _new_var(newselect, var.name)
   120         newselect.selection = [VariableRef(newvar)]
   120         newselect.selection = [VariableRef(newvar)]
   314 
   314 
   315 
   315 
   316 # IGenerator implementation for RQL->SQL #######################################
   316 # IGenerator implementation for RQL->SQL #######################################
   317 
   317 
   318 class StateInfo(object):
   318 class StateInfo(object):
   319     def __init__(self, existssols, unstablevars):
   319     def __init__(self, select, existssols, unstablevars):
   320         self.existssols = existssols
   320         self.existssols = existssols
   321         self.unstablevars = unstablevars
   321         self.unstablevars = unstablevars
   322         self.subtables = {}
   322         self.subtables = {}
   323         self.needs_source_cb = None
   323         self.needs_source_cb = None
   324         self.subquery_source_cb = None
   324         self.subquery_source_cb = None
   325         self.source_cb_funcs = set()
   325         self.source_cb_funcs = set()
       
   326         self.scopes = {select: 0}
       
   327         self.scope_nodes = []
   326 
   328 
   327     def reset(self, solution):
   329     def reset(self, solution):
   328         """reset some visit variables"""
   330         """reset some visit variables"""
   329         self.solution = solution
   331         self.solution = solution
   330         self.count = 0
   332         self.count = 0
   379                     if rel in done:
   381                     if rel in done:
   380                         done.remove(rel)
   382                         done.remove(rel)
   381         self.solution = origsol
   383         self.solution = origsol
   382         self.tables = origtables
   384         self.tables = origtables
   383 
   385 
   384     def push_scope(self):
   386     def push_scope(self, scope_node):
       
   387         self.scope_nodes.append(scope_node)
       
   388         self.scopes[scope_node] = len(self.actual_tables)
   385         self.actual_tables.append([])
   389         self.actual_tables.append([])
   386         self._restr_stack.append(self.restrictions)
   390         self._restr_stack.append(self.restrictions)
   387         self.restrictions = []
   391         self.restrictions = []
   388 
   392 
   389     def pop_scope(self):
   393     def pop_scope(self):
       
   394         del self.scopes[self.scope_nodes[-1]]
       
   395         self.scope_nodes.pop()
   390         restrictions = self.restrictions
   396         restrictions = self.restrictions
   391         self.restrictions = self._restr_stack.pop()
   397         self.restrictions = self._restr_stack.pop()
   392         return restrictions, self.actual_tables.pop()
   398         return restrictions, self.actual_tables.pop()
   393 
   399 
   394 
   400 
   440         self._lock.acquire()
   446         self._lock.acquire()
   441         self._args = args
   447         self._args = args
   442         self._varmap = varmap
   448         self._varmap = varmap
   443         self._query_attrs = {}
   449         self._query_attrs = {}
   444         self._state = None
   450         self._state = None
   445         self._not_scope_offset = 0
   451         # self._not_scope_offset = 0
   446         try:
   452         try:
   447             # union query for each rqlst / solution
   453             # union query for each rqlst / solution
   448             sql = self.union_sql(union)
   454             sql = self.union_sql(union)
   449             # we are done
   455             # we are done
   450             return sql, self._query_attrs, self._state.needs_source_cb
   456             return sql, self._query_attrs, self._state.needs_source_cb
   507                 if groups or select.has_aggregat:
   513                 if groups or select.has_aggregat:
   508                     select.select_only_variables()
   514                     select.select_only_variables()
   509                     needwrap = True
   515                     needwrap = True
   510         else:
   516         else:
   511             existssols, unstable = {}, ()
   517             existssols, unstable = {}, ()
   512         state = StateInfo(existssols, unstable)
   518         state = StateInfo(select, existssols, unstable)
   513         if self._state is not None:
   519         if self._state is not None:
   514             # state from a previous unioned select
   520             # state from a previous unioned select
   515             state.merge_source_cbs(self._state.needs_source_cb)
   521             state.merge_source_cbs(self._state.needs_source_cb)
   516         # treat subqueries
   522         # treat subqueries
   517         self._subqueries_sql(select, state)
   523         self._subqueries_sql(select, state)
   620                 # sort for test predictability
   626                 # sort for test predictability
   621                 sql.insert(1, 'FROM %s' % ', '.join(sorted(tables)))
   627                 sql.insert(1, 'FROM %s' % ', '.join(sorted(tables)))
   622             elif self._state.restrictions and self.dbhelper.needs_from_clause:
   628             elif self._state.restrictions and self.dbhelper.needs_from_clause:
   623                 sql.insert(1, 'FROM (SELECT 1) AS _T')
   629                 sql.insert(1, 'FROM (SELECT 1) AS _T')
   624             sqls.append('\n'.join(sql))
   630             sqls.append('\n'.join(sql))
   625         if select.need_intersect:
   631         if distinct:
   626             #if distinct or not self.dbhelper.intersect_all_support:
       
   627             return '\nINTERSECT\n'.join(sqls)
       
   628             #else:
       
   629             #    return '\nINTERSECT ALL\n'.join(sqls)
       
   630         elif distinct:
       
   631             return '\nUNION\n'.join(sqls)
   632             return '\nUNION\n'.join(sqls)
   632         else:
   633         else:
   633             return '\nUNION ALL\n'.join(sqls)
   634             return '\nUNION ALL\n'.join(sqls)
   634 
   635 
   635     def _selection_sql(self, selected, distinct, needaliasing=False):
   636     def _selection_sql(self, selected, distinct, needaliasing=False):
   680                 return '(%s)' % ' OR '.join(res)
   681                 return '(%s)' % ' OR '.join(res)
   681             return res[0]
   682             return res[0]
   682         return ''
   683         return ''
   683 
   684 
   684     def visit_not(self, node):
   685     def visit_not(self, node):
   685         self._state.push_scope()
       
   686         if isinstance(node.children[0], Relation):
       
   687             self._not_scope_offset += 1
       
   688         csql = node.children[0].accept(self)
   686         csql = node.children[0].accept(self)
   689         if isinstance(node.children[0], Relation):
       
   690             self._not_scope_offset -= 1
       
   691         sqls, tables = self._state.pop_scope()
       
   692         if node in self._state.done or not csql:
   687         if node in self._state.done or not csql:
   693             # already processed or no sql generated by children
   688             # already processed or no sql generated by children
   694             self._state.actual_tables[-1] += tables
       
   695             self._state.restrictions += sqls
       
   696             return csql
   689             return csql
   697         if isinstance(node.children[0], Exists):
   690         return 'NOT (%s)' % csql
   698             assert not sqls, (sqls, str(node.stmt))
       
   699             assert not tables, (tables, str(node.stmt))
       
   700             return 'NOT %s' % csql
       
   701         sqls.append(csql)
       
   702         if tables:
       
   703             select = 'SELECT 1 FROM %s' % ','.join(tables)
       
   704         else:
       
   705             select = 'SELECT 1'
       
   706         if sqls:
       
   707             sql = 'NOT EXISTS(%s WHERE %s)' % (select, ' AND '.join(sqls))
       
   708         else:
       
   709             sql = 'NOT EXISTS(%s)' % select
       
   710         return sql
       
   711 
   691 
   712     def visit_exists(self, exists):
   692     def visit_exists(self, exists):
   713         """generate SQL name for a exists subquery"""
   693         """generate SQL name for a exists subquery"""
   714         sqls = []
   694         sqls = []
   715         for dummy in self._state.iter_exists_sols(exists):
   695         for dummy in self._state.iter_exists_sols(exists):
   719         if not sqls:
   699         if not sqls:
   720             return ''
   700             return ''
   721         return 'EXISTS(%s)' % ' UNION '.join(sqls)
   701         return 'EXISTS(%s)' % ' UNION '.join(sqls)
   722 
   702 
   723     def _visit_exists(self, exists):
   703     def _visit_exists(self, exists):
   724         self._state.push_scope()
   704         self._state.push_scope(exists)
   725         restriction = exists.children[0].accept(self)
   705         restriction = exists.children[0].accept(self)
   726         restrictions, tables = self._state.pop_scope()
   706         restrictions, tables = self._state.pop_scope()
   727         if restriction:
   707         if restriction:
   728             restrictions.append(restriction)
   708             restrictions.append(restriction)
   729         restriction = ' AND '.join(restrictions)
   709         restriction = ' AND '.join(restrictions)
   760                     # if variable(s) in the RHS
   740                     # if variable(s) in the RHS
   761                     sql = self._visit_var_attr_relation(relation, rhs_vars)
   741                     sql = self._visit_var_attr_relation(relation, rhs_vars)
   762                 else:
   742                 else:
   763                     # no variables in the RHS
   743                     # no variables in the RHS
   764                     sql = self._visit_attribute_relation(relation)
   744                     sql = self._visit_attribute_relation(relation)
   765                 if relation.neged(strict=True):
       
   766                     self._state.done.add(relation.parent)
       
   767                     sql = 'NOT (%s)' % sql
       
   768         else:
   745         else:
   769             if rtype == 'is' and rhs.operator == 'IS':
   746             if rtype == 'is' and rhs.operator == 'IS':
   770                 # special case "C is NULL"
   747                 # special case "C is NULL"
   771                 if lhs.name in self._varmap:
   748                 if lhs.name in self._varmap:
   772                     lhssql = self._varmap[lhs.name]
   749                     lhssql = self._varmap[lhs.name]
   831         implements optimization 1.
   808         implements optimization 1.
   832         """
   809         """
   833         if relation.r_type == 'identity':
   810         if relation.r_type == 'identity':
   834             # special case "X identity Y"
   811             # special case "X identity Y"
   835             lhs, rhs = relation.get_parts()
   812             lhs, rhs = relation.get_parts()
   836             if isinstance(relation.parent, Not):
       
   837                 self._state.done.add(relation.parent)
       
   838                 return 'NOT %s%s' % (lhs.accept(self), rhs.accept(self))
       
   839             return '%s%s' % (lhs.accept(self), rhs.accept(self))
   813             return '%s%s' % (lhs.accept(self), rhs.accept(self))
   840         lhsvar, lhsconst, rhsvar, rhsconst = relation_info(relation)
   814         lhsvar, lhsconst, rhsvar, rhsconst = relation_info(relation)
   841         rid = self._relation_table(relation)
   815         rid = self._relation_table(relation)
   842         sqls = []
   816         sqls = []
   843         sqls += self._process_relation_term(relation, rid, lhsvar, lhsconst, 'eid_from')
   817         sqls += self._process_relation_term(relation, rid, lhsvar, lhsconst, 'eid_from')
  1039             self._state.done.add(rel.parent)
  1013             self._state.done.add(rel.parent)
  1040             not_ = True
  1014             not_ = True
  1041         else:
  1015         else:
  1042             not_ = False
  1016             not_ = False
  1043         return self.dbhelper.fti_restriction_sql(alias, const.eval(self._args),
  1017         return self.dbhelper.fti_restriction_sql(alias, const.eval(self._args),
  1044                                                     jointo, not_) + restriction
  1018                                                  jointo, not_) + restriction
  1045 
  1019 
  1046     def visit_comparison(self, cmp):
  1020     def visit_comparison(self, cmp):
  1047         """generate SQL for a comparison"""
  1021         """generate SQL for a comparison"""
  1048         if len(cmp.children) == 2:
  1022         if len(cmp.children) == 2:
  1049             # XXX occurs ?
  1023             # XXX occurs ?
  1202             # so nothing to return here
  1176             # so nothing to return here
  1203             pass
  1177             pass
  1204         return ''
  1178         return ''
  1205 
  1179 
  1206     def _var_info(self, var):
  1180     def _var_info(self, var):
  1207         # if current var or one of its attribute is selected , it *must*
  1181         scope = self._state.scopes[var.scope]
  1208         # appear in the toplevel's FROM even if we're currently visiting
       
  1209         # a EXISTS node
       
  1210         if var.sqlscope is var.stmt:
       
  1211             scope = 0
       
  1212         # don't consider not_scope_offset if the variable is only used in one
       
  1213         # relation
       
  1214         elif len(var.stinfo['relations']) > 1:
       
  1215             scope = -1 - self._not_scope_offset
       
  1216         else:
       
  1217             scope = -1
       
  1218         try:
  1182         try:
  1219             sql = self._varmap[var.name]
  1183             sql = self._varmap[var.name]
  1220             tablealias = sql.split('.', 1)[0]
  1184             tablealias = sql.split('.', 1)[0]
  1221             if scope < 0:
       
  1222                 scope = self._varmap_table_scope(var.stmt, tablealias)
       
  1223             self.add_table(tablealias, scope=scope)
  1185             self.add_table(tablealias, scope=scope)
  1224         except KeyError:
  1186         except KeyError:
  1225             etype = self._state.solution[var.name]
  1187             etype = self._state.solution[var.name]
  1226             # XXX this check should be moved in rql.stcheck
  1188             # XXX this check should be moved in rql.stcheck
  1227             if self.schema.eschema(etype).final:
  1189             if self.schema.eschema(etype).final:
  1233         return sql, tablealias
  1195         return sql, tablealias
  1234 
  1196 
  1235     def _inlined_var_sql(self, var, rtype):
  1197     def _inlined_var_sql(self, var, rtype):
  1236         try:
  1198         try:
  1237             sql = self._varmap['%s.%s' % (var.name, rtype)]
  1199             sql = self._varmap['%s.%s' % (var.name, rtype)]
  1238             scope = var.sqlscope is var.stmt and 0 or -1
  1200             scope = self._state.scopes[var.scope]
  1239             self.add_table(sql.split('.', 1)[0], scope=scope)
  1201             self.add_table(sql.split('.', 1)[0], scope=scope)
  1240         except KeyError:
  1202         except KeyError:
  1241             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
  1203             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
  1242             #self._state.done.add(var.name)
  1204             #self._state.done.add(var.name)
  1243         return sql
  1205         return sql
  1356             if isinstance(var, ColumnAlias):
  1318             if isinstance(var, ColumnAlias):
  1357                 scope = 0
  1319                 scope = 0
  1358                 break
  1320                 break
  1359             # XXX may have a principal without being invariant for this generation,
  1321             # XXX may have a principal without being invariant for this generation,
  1360             #     not sure this is a pb or not
  1322             #     not sure this is a pb or not
  1361             if var.stinfo.get('principal') is relation and var.sqlscope is var.stmt:
  1323             if var.stinfo.get('principal') is relation and var.scope is var.stmt:
  1362                 scope = 0
  1324                 scope = 0
  1363                 break
  1325                 break
  1364         else:
  1326         else:
  1365             scope = -1
  1327             scope = -1
  1366         self._state.count += 1
  1328         self._state.count += 1
  1377                 pass
  1339                 pass
  1378         self._state.done.add(relation)
  1340         self._state.done.add(relation)
  1379         alias = self.alias_and_add_table(self.dbhelper.fti_table)
  1341         alias = self.alias_and_add_table(self.dbhelper.fti_table)
  1380         relation._q_sqltable = alias
  1342         relation._q_sqltable = alias
  1381         return alias
  1343         return alias
  1382 
       
  1383     def _varmap_table_scope(self, select, table):
       
  1384         """since a varmap table may be used for multiple variable, its scope is
       
  1385         the most outer scope of each variables
       
  1386         """
       
  1387         scope = -1
       
  1388         for varname, alias in self._varmap.iteritems():
       
  1389             # check '.' in varname since there are 'X.attribute' keys in varmap
       
  1390             if not '.' in varname and alias.split('.', 1)[0] == table:
       
  1391                 if select.defined_vars[varname].sqlscope is select:
       
  1392                     return 0
       
  1393         return scope