server/sources/rql2sql.py
branchstable
changeset 3815 50b87f759b5d
parent 3787 82bb2c7f083b
child 3852 03121ca1f85e
equal deleted inserted replaced
3814:a4659adf4eee 3815:50b87f759b5d
   250         self.actual_tables = [[]]
   250         self.actual_tables = [[]]
   251         for _, tsql in self.tables.itervalues():
   251         for _, tsql in self.tables.itervalues():
   252             self.actual_tables[-1].append(tsql)
   252             self.actual_tables[-1].append(tsql)
   253         self.outer_tables = {}
   253         self.outer_tables = {}
   254         self.duplicate_switches = []
   254         self.duplicate_switches = []
   255         self.attr_vars = {}
       
   256         self.aliases = {}
   255         self.aliases = {}
   257         self.restrictions = []
   256         self.restrictions = []
   258         self._restr_stack = []
   257         self._restr_stack = []
       
   258         self.ignore_varmap = False
   259 
   259 
   260     def add_restriction(self, restr):
   260     def add_restriction(self, restr):
   261         if restr:
   261         if restr:
   262             self.restrictions.append(restr)
   262             self.restrictions.append(restr)
   263 
   263 
   846         attribute variables are used either in the selection or for
   846         attribute variables are used either in the selection or for
   847         unification (eg X attr1 A, Y attr2 A). In case of selection,
   847         unification (eg X attr1 A, Y attr2 A). In case of selection,
   848         nothing to do here.
   848         nothing to do here.
   849         """
   849         """
   850         contextrels = {}
   850         contextrels = {}
   851         attrvars = self._state.attr_vars
       
   852         for var in rhs_vars:
   851         for var in rhs_vars:
   853             try:
       
   854                 contextrels[var.name] = attrvars[var.name]
       
   855             except KeyError:
       
   856                 attrvars[var.name] = relation
       
   857             if var.name in self._varmap:
   852             if var.name in self._varmap:
   858                 # ensure table is added
   853                 # ensure table is added
   859                 self._var_info(var.variable)
   854                 self._var_info(var.variable)
       
   855             principal = var.variable.stinfo.get('principal')
       
   856             if principal is not None and principal is not relation:
       
   857                 contextrels[var.name] = relation
   860         if not contextrels:
   858         if not contextrels:
   861             relation.children[1].accept(self, contextrels)
       
   862             return ''
   859             return ''
   863         # at least one variable is already in attr_vars, this means we have to
   860         # we have to generate unification expression
   864         # generate unification expression
       
   865         lhssql = self._inlined_var_sql(relation.children[0].variable,
   861         lhssql = self._inlined_var_sql(relation.children[0].variable,
   866                                        relation.r_type)
   862                                        relation.r_type)
   867         return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels))
   863         try:
       
   864             self._state.ignore_varmap = True
       
   865             return '%s%s' % (lhssql, relation.children[1].accept(self))
       
   866         finally:
       
   867             self._state.ignore_varmap = False
   868 
   868 
   869     def _visit_attribute_relation(self, rel):
   869     def _visit_attribute_relation(self, rel):
   870         """generate SQL for an attribute relation"""
   870         """generate SQL for an attribute relation"""
   871         lhs, rhs = rel.get_parts()
   871         lhs, rhs = rel.get_parts()
   872         rhssql = rhs.accept(self)
   872         rhssql = rhs.accept(self)
   930         else:
   930         else:
   931             not_ = False
   931             not_ = False
   932         return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args),
   932         return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args),
   933                                                     jointo, not_) + restriction
   933                                                     jointo, not_) + restriction
   934 
   934 
   935     def visit_comparison(self, cmp, contextrels=None):
   935     def visit_comparison(self, cmp):
   936         """generate SQL for a comparison"""
   936         """generate SQL for a comparison"""
   937         if len(cmp.children) == 2:
   937         if len(cmp.children) == 2:
   938             # XXX occurs ?
   938             # XXX occurs ?
   939             lhs, rhs = cmp.children
   939             lhs, rhs = cmp.children
   940         else:
   940         else:
   948                 operator = ' %s ' % operator
   948                 operator = ' %s ' % operator
   949         elif (operator == '=' and isinstance(rhs, Constant)
   949         elif (operator == '=' and isinstance(rhs, Constant)
   950               and rhs.eval(self._args) is None):
   950               and rhs.eval(self._args) is None):
   951             if lhs is None:
   951             if lhs is None:
   952                 return ' IS NULL'
   952                 return ' IS NULL'
   953             return '%s IS NULL' % lhs.accept(self, contextrels)
   953             return '%s IS NULL' % lhs.accept(self)
   954         elif isinstance(rhs, Function) and rhs.name == 'IN':
   954         elif isinstance(rhs, Function) and rhs.name == 'IN':
   955             assert operator == '='
   955             assert operator == '='
   956             operator = ' '
   956             operator = ' '
   957         if lhs is None:
   957         if lhs is None:
   958             return '%s%s'% (operator, rhs.accept(self, contextrels))
   958             return '%s%s'% (operator, rhs.accept(self))
   959         return '%s%s%s'% (lhs.accept(self, contextrels), operator,
   959         return '%s%s%s'% (lhs.accept(self), operator, rhs.accept(self))
   960                           rhs.accept(self, contextrels))
   960 
   961 
   961     def visit_mathexpression(self, mexpr):
   962     def visit_mathexpression(self, mexpr, contextrels=None):
       
   963         """generate SQL for a mathematic expression"""
   962         """generate SQL for a mathematic expression"""
   964         lhs, rhs = mexpr.get_parts()
   963         lhs, rhs = mexpr.get_parts()
   965         # check for string concatenation
   964         # check for string concatenation
   966         operator = mexpr.operator
   965         operator = mexpr.operator
   967         try:
   966         try:
   968             if mexpr.operator == '+' and mexpr.get_type(self._state.solution, self._args) == 'String':
   967             if mexpr.operator == '+' and mexpr.get_type(self._state.solution, self._args) == 'String':
   969                 operator = '||'
   968                 operator = '||'
   970         except CoercionError:
   969         except CoercionError:
   971             pass
   970             pass
   972         return '(%s %s %s)'% (lhs.accept(self, contextrels), operator,
   971         return '(%s %s %s)'% (lhs.accept(self), operator, rhs.accept(self))
   973                               rhs.accept(self, contextrels))
   972 
   974 
   973     def visit_function(self, func):
   975     def visit_function(self, func, contextrels=None):
       
   976         """generate SQL name for a function"""
   974         """generate SQL name for a function"""
   977         # function_description will check function is supported by the backend
   975         # function_description will check function is supported by the backend
   978         sqlname = self.dbms_helper.func_sqlname(func.name)
   976         sqlname = self.dbms_helper.func_sqlname(func.name)
   979         return '%s(%s)' % (sqlname, ', '.join(c.accept(self, contextrels)
   977         return '%s(%s)' % (sqlname, ', '.join(c.accept(self)
   980                                               for c in func.children))
   978                                               for c in func.children))
   981 
   979 
   982     def visit_constant(self, constant, contextrels=None):
   980     def visit_constant(self, constant):
   983         """generate SQL name for a constant"""
   981         """generate SQL name for a constant"""
   984         value = constant.value
   982         value = constant.value
   985         if constant.type is None:
   983         if constant.type is None:
   986             return 'NULL'
   984             return 'NULL'
   987         if constant.type == 'Int' and  isinstance(constant.parent, SortTerm):
   985         if constant.type == 'Int' and  isinstance(constant.parent, SortTerm):
  1002             if isinstance(value, unicode):
  1000             if isinstance(value, unicode):
  1003                 value = value.encode(self.dbencoding)
  1001                 value = value.encode(self.dbencoding)
  1004             self._query_attrs[_id] = value
  1002             self._query_attrs[_id] = value
  1005         return '%%(%s)s' % _id
  1003         return '%%(%s)s' % _id
  1006 
  1004 
  1007     def visit_variableref(self, variableref, contextrels=None):
  1005     def visit_variableref(self, variableref):
  1008         """get the sql name for a variable reference"""
  1006         """get the sql name for a variable reference"""
  1009         # use accept, .variable may be a variable or a columnalias
  1007         # use accept, .variable may be a variable or a columnalias
  1010         return variableref.variable.accept(self, contextrels)
  1008         return variableref.variable.accept(self)
  1011 
  1009 
  1012     def visit_columnalias(self, colalias, contextrels=None):
  1010     def visit_columnalias(self, colalias):
  1013         """get the sql name for a subquery column alias"""
  1011         """get the sql name for a subquery column alias"""
  1014         if colalias.name in self._varmap:
  1012         if colalias.name in self._varmap:
  1015             sql = self._varmap[colalias.name]
  1013             sql = self._varmap[colalias.name]
  1016             table = sql.split('.', 1)[0]
  1014             table = sql.split('.', 1)[0]
  1017             colalias._q_sqltable = table
  1015             colalias._q_sqltable = table
  1018             colalias._q_sql = sql
  1016             colalias._q_sql = sql
  1019             self.add_table(table)
  1017             self.add_table(table)
  1020             return sql
  1018             return sql
  1021         return colalias._q_sql
  1019         return colalias._q_sql
  1022 
  1020 
  1023     def visit_variable(self, variable, contextrels=None):
  1021     def visit_variable(self, variable):
  1024         """get the table name and sql string for a variable"""
  1022         """get the table name and sql string for a variable"""
  1025         if contextrels is None and variable.name in self._state.done:
  1023         #if contextrels is None and variable.name in self._state.done:
       
  1024         if variable.name in self._state.done:
  1026             if self._in_wrapping_query:
  1025             if self._in_wrapping_query:
  1027                 return 'T1.%s' % self._state.aliases[variable.name]
  1026                 return 'T1.%s' % self._state.aliases[variable.name]
  1028             return variable._q_sql
  1027             return variable._q_sql
  1029         self._state.done.add(variable.name)
  1028         self._state.done.add(variable.name)
  1030         vtablename = None
  1029         vtablename = None
  1031         if contextrels is None and variable.name in self._varmap:
  1030         if not self._state.ignore_varmap and variable.name in self._varmap:
  1032             sql, vtablename = self._var_info(variable)
  1031             sql, vtablename = self._var_info(variable)
  1033         elif variable.stinfo['attrvar']:
  1032         elif variable.stinfo['attrvar']:
  1034             # attribute variable (systematically used in rhs of final
  1033             # attribute variable (systematically used in rhs of final
  1035             # relation(s)), get table name and sql from any rhs relation
  1034             # relation(s)), get table name and sql from any rhs relation
  1036             sql = self._linked_var_sql(variable, contextrels)
  1035             sql = self._linked_var_sql(variable)
  1037         elif variable._q_invariant:
  1036         elif variable._q_invariant:
  1038             # since variable is invariant, we know we won't found final relation
  1037             # since variable is invariant, we know we won't found final relation
  1039             principal = variable.stinfo['principal']
  1038             principal = variable.stinfo['principal']
  1040             if principal is None:
  1039             if principal is None:
  1041                 vtablename = '_' + variable.name
  1040                 vtablename = '_' + variable.name
  1054             elif principal.r_type == 'has_text':
  1053             elif principal.r_type == 'has_text':
  1055                 sql = '%s.%s' % (self._fti_table(principal),
  1054                 sql = '%s.%s' % (self._fti_table(principal),
  1056                                  self.dbms_helper.fti_uid_attr)
  1055                                  self.dbms_helper.fti_uid_attr)
  1057             elif principal in variable.stinfo['rhsrelations']:
  1056             elif principal in variable.stinfo['rhsrelations']:
  1058                 if self.schema.rschema(principal.r_type).inlined:
  1057                 if self.schema.rschema(principal.r_type).inlined:
  1059                     sql = self._linked_var_sql(variable, contextrels)
  1058                     sql = self._linked_var_sql(variable)
  1060                 else:
  1059                 else:
  1061                     sql = '%s.eid_to' % self._relation_table(principal)
  1060                     sql = '%s.eid_to' % self._relation_table(principal)
  1062             else:
  1061             else:
  1063                 sql = '%s.eid_from' % self._relation_table(principal)
  1062                 sql = '%s.eid_from' % self._relation_table(principal)
  1064         else:
  1063         else:
  1074     def _extra_join_sql(self, relation, sql, var):
  1073     def _extra_join_sql(self, relation, sql, var):
  1075         # if rhs var is invariant, and this relation is not its principal,
  1074         # if rhs var is invariant, and this relation is not its principal,
  1076         # generate extra join
  1075         # generate extra join
  1077         try:
  1076         try:
  1078             if not var.stinfo['principal'] is relation:
  1077             if not var.stinfo['principal'] is relation:
  1079                 # need a predicable result for tests
  1078                 op = relation.operator()
  1080                 return '%s=%s' % tuple(sorted((sql, var.accept(self))))
  1079                 if op == '=':
       
  1080                     # need a predicable result for tests
       
  1081                     args = sorted( (sql, var.accept(self)) )
       
  1082                     args.insert(1, op)
       
  1083                 else:
       
  1084                     args = (sql, op, var.accept(self))
       
  1085                 return '%s%s%s' % tuple(args)
  1081         except KeyError:
  1086         except KeyError:
  1082             # no principal defined, relation is necessarily the principal and
  1087             # no principal defined, relation is necessarily the principal and
  1083             # so nothing to return here
  1088             # so nothing to return here
  1084             pass
  1089             pass
  1085         return ''
  1090         return ''
  1121         except KeyError:
  1126         except KeyError:
  1122             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
  1127             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
  1123             #self._state.done.add(var.name)
  1128             #self._state.done.add(var.name)
  1124         return sql
  1129         return sql
  1125 
  1130 
  1126     def _linked_var_sql(self, variable, contextrels=None):
  1131     def _linked_var_sql(self, variable):
  1127         if contextrels is None:
  1132         if not self._state.ignore_varmap:
  1128             try:
  1133             try:
  1129                 return self._varmap[variable.name]
  1134                 return self._varmap[variable.name]
  1130             except KeyError:
  1135             except KeyError:
  1131                 pass
  1136                 pass
  1132         rel = (contextrels and contextrels.get(variable.name) or
  1137         rel = (variable.stinfo.get('principal') or
  1133                variable.stinfo.get('principal') or
       
  1134                iter(variable.stinfo['rhsrelations']).next())
  1138                iter(variable.stinfo['rhsrelations']).next())
  1135         linkedvar = rel.children[0].variable
  1139         linkedvar = rel.children[0].variable
  1136         if rel.r_type == 'eid':
  1140         if rel.r_type == 'eid':
  1137             return linkedvar.accept(self)
  1141             return linkedvar.accept(self)
  1138         if isinstance(linkedvar, ColumnAlias):
  1142         if isinstance(linkedvar, ColumnAlias):