server/sources/rql2sql.py
branchstable
changeset 2354 9b4bac626977
parent 2199 bd0a0f219751
child 2915 651bbe1526b6
equal deleted inserted replaced
2353:b11f1068a0d3 2354:9b4bac626977
   301 
   301 
   302     WARNING: a CubicWebSQLGenerator instance is not thread safe, but generate is
   302     WARNING: a CubicWebSQLGenerator instance is not thread safe, but generate is
   303     protected by a lock
   303     protected by a lock
   304     """
   304     """
   305 
   305 
   306     def __init__(self, schema, dbms_helper, dbencoding='UTF-8'):
   306     def __init__(self, schema, dbms_helper, dbencoding='UTF-8', attrmap=None):
   307         self.schema = schema
   307         self.schema = schema
   308         self.dbms_helper = dbms_helper
   308         self.dbms_helper = dbms_helper
   309         self.dbencoding = dbencoding
   309         self.dbencoding = dbencoding
   310         self.keyword_map = {'NOW' : self.dbms_helper.sql_current_timestamp,
   310         self.keyword_map = {'NOW' : self.dbms_helper.sql_current_timestamp,
   311                             'TODAY': self.dbms_helper.sql_current_date,
   311                             'TODAY': self.dbms_helper.sql_current_date,
   312                             }
   312                             }
   313         if not self.dbms_helper.union_parentheses_support:
   313         if not self.dbms_helper.union_parentheses_support:
   314             self.union_sql = self.noparen_union_sql
   314             self.union_sql = self.noparen_union_sql
   315         self._lock = threading.Lock()
   315         self._lock = threading.Lock()
       
   316         if attrmap is None:
       
   317             attrmap = {}
       
   318         self.attr_map = attrmap
   316 
   319 
   317     def generate(self, union, args=None, varmap=None):
   320     def generate(self, union, args=None, varmap=None):
   318         """return SQL queries and a variable dictionnary from a RQL syntax tree
   321         """return SQL queries and a variable dictionnary from a RQL syntax tree
   319 
   322 
   320         :partrqls: a list of couple (rqlst, solutions)
   323         :partrqls: a list of couple (rqlst, solutions)
   851         # generate unification expression
   854         # generate unification expression
   852         lhssql = self._inlined_var_sql(relation.children[0].variable,
   855         lhssql = self._inlined_var_sql(relation.children[0].variable,
   853                                        relation.r_type)
   856                                        relation.r_type)
   854         return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels))
   857         return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels))
   855 
   858 
   856     def _visit_attribute_relation(self, relation):
   859     def _visit_attribute_relation(self, rel):
   857         """generate SQL for an attribute relation"""
   860         """generate SQL for an attribute relation"""
   858         lhs, rhs = relation.get_parts()
   861         lhs, rhs = rel.get_parts()
   859         rhssql = rhs.accept(self)
   862         rhssql = rhs.accept(self)
   860         table = self._var_table(lhs.variable)
   863         table = self._var_table(lhs.variable)
   861         if table is None:
   864         if table is None:
   862             assert relation.r_type == 'eid'
   865             assert rel.r_type == 'eid'
   863             lhssql = lhs.accept(self)
   866             lhssql = lhs.accept(self)
   864         else:
   867         else:
   865             try:
   868             try:
   866                 lhssql = self._varmap['%s.%s' % (lhs.name, relation.r_type)]
   869                 lhssql = self._varmap['%s.%s' % (lhs.name, rel.r_type)]
   867             except KeyError:
   870             except KeyError:
   868                 if relation.r_type == 'eid':
   871                 mapkey = '%s.%s' % (self._state.solution[lhs.name], rel.r_type)
       
   872                 if mapkey in self.attr_map:
       
   873                     lhssql = self.attr_map[mapkey](self, lhs.variable, rel)
       
   874                 elif rel.r_type == 'eid':
   869                     lhssql = lhs.variable._q_sql
   875                     lhssql = lhs.variable._q_sql
   870                 else:
   876                 else:
   871                     lhssql = '%s.%s%s' % (table, SQL_PREFIX, relation.r_type)
   877                     lhssql = '%s.%s%s' % (table, SQL_PREFIX, rel.r_type)
   872         try:
   878         try:
   873             if relation._q_needcast == 'TODAY':
   879             if rel._q_needcast == 'TODAY':
   874                 sql = 'DATE(%s)%s' % (lhssql, rhssql)
   880                 sql = 'DATE(%s)%s' % (lhssql, rhssql)
   875             # XXX which cast function should be used
   881             # XXX which cast function should be used
   876             #elif relation._q_needcast == 'NOW':
   882             #elif rel._q_needcast == 'NOW':
   877             #    sql = 'TIMESTAMP(%s)%s' % (lhssql, rhssql)
   883             #    sql = 'TIMESTAMP(%s)%s' % (lhssql, rhssql)
   878             else:
   884             else:
   879                 sql = '%s%s' % (lhssql, rhssql)
   885                 sql = '%s%s' % (lhssql, rhssql)
   880         except AttributeError:
   886         except AttributeError:
   881             sql = '%s%s' % (lhssql, rhssql)
   887             sql = '%s%s' % (lhssql, rhssql)
   882         if lhs.variable.stinfo['optrelations']:
   888         if lhs.variable.stinfo['optrelations']:
   883             self.add_outer_join_condition(lhs.variable, table, sql)
   889             self.add_outer_join_condition(lhs.variable, table, sql)
   884         else:
   890         else:
   885             return sql
   891             return sql
   886 
   892 
   887     def _visit_has_text_relation(self, relation):
   893     def _visit_has_text_relation(self, rel):
   888         """generate SQL for a has_text relation"""
   894         """generate SQL for a has_text relation"""
   889         lhs, rhs = relation.get_parts()
   895         lhs, rhs = rel.get_parts()
   890         const = rhs.children[0]
   896         const = rhs.children[0]
   891         alias = self._fti_table(relation)
   897         alias = self._fti_table(rel)
   892         jointo = lhs.accept(self)
   898         jointo = lhs.accept(self)
   893         restriction = ''
   899         restriction = ''
   894         lhsvar = lhs.variable
   900         lhsvar = lhs.variable
   895         me_is_principal = lhsvar.stinfo.get('principal') is relation
   901         me_is_principal = lhsvar.stinfo.get('principal') is rel
   896         if me_is_principal:
   902         if me_is_principal:
   897             if not lhsvar.stinfo['typerels']:
   903             if not lhsvar.stinfo['typerels']:
   898                 # the variable is using the fti table, no join needed
   904                 # the variable is using the fti table, no join needed
   899                 jointo = None
   905                 jointo = None
   900             elif not lhsvar.name in self._varmap:
   906             elif not lhsvar.name in self._varmap:
   906                 if not lhsvar._q_invariant or len(lhsvar.stinfo['possibletypes']) == 1:
   912                 if not lhsvar._q_invariant or len(lhsvar.stinfo['possibletypes']) == 1:
   907                     restriction = " AND %s.type='%s'" % (ealias, self._state.solution[lhs.name])
   913                     restriction = " AND %s.type='%s'" % (ealias, self._state.solution[lhs.name])
   908                 else:
   914                 else:
   909                     etypes = ','.join("'%s'" % etype for etype in lhsvar.stinfo['possibletypes'])
   915                     etypes = ','.join("'%s'" % etype for etype in lhsvar.stinfo['possibletypes'])
   910                     restriction = " AND %s.type IN (%s)" % (ealias, etypes)
   916                     restriction = " AND %s.type IN (%s)" % (ealias, etypes)
   911         if isinstance(relation.parent, Not):
   917         if isinstance(rel.parent, Not):
   912             self._state.done.add(relation.parent)
   918             self._state.done.add(rel.parent)
   913             not_ = True
   919             not_ = True
   914         else:
   920         else:
   915             not_ = False
   921             not_ = False
   916         return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args),
   922         return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args),
   917                                                     jointo, not_) + restriction
   923                                                     jointo, not_) + restriction
  1115         if rel.r_type == 'eid':
  1121         if rel.r_type == 'eid':
  1116             return linkedvar.accept(self)
  1122             return linkedvar.accept(self)
  1117         if isinstance(linkedvar, ColumnAlias):
  1123         if isinstance(linkedvar, ColumnAlias):
  1118             raise BadRQLQuery('variable %s should be selected by the subquery'
  1124             raise BadRQLQuery('variable %s should be selected by the subquery'
  1119                               % variable.name)
  1125                               % variable.name)
       
  1126         mapkey = '%s.%s' % (self._state.solution[linkedvar.name], rel.r_type)
       
  1127         if mapkey in self.attr_map:
       
  1128             return self.attr_map[mapkey](self, linkedvar, rel)
  1120         try:
  1129         try:
  1121             sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)]
  1130             sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)]
  1122         except KeyError:
  1131         except KeyError:
  1123             linkedvar.accept(self)
  1132             linkedvar.accept(self)
  1124             sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type)
  1133             sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type)