cubicweb/server/sources/rql2sql.py
changeset 11237 f32134dd0067
parent 11057 0b59724cb3f2
child 11301 a76feec0a861
equal deleted inserted replaced
11232:25ec9be5f305 11237:f32134dd0067
   165             asol[vname] = origsol[vname]
   165             asol[vname] = origsol[vname]
   166         if not asol in newsolutions:
   166         if not asol in newsolutions:
   167             newsolutions.append(asol)
   167             newsolutions.append(asol)
   168     return newsolutions
   168     return newsolutions
   169 
   169 
   170 def remove_unused_solutions(rqlst, solutions, varmap, schema):
   170 def remove_unused_solutions(rqlst, solutions, schema):
   171     """cleanup solutions: remove solutions where invariant variables are taking
   171     """cleanup solutions: remove solutions where invariant variables are taking
   172     different types
   172     different types
   173     """
   173     """
   174     newsols = _new_solutions(rqlst, solutions)
   174     newsols = _new_solutions(rqlst, solutions)
   175     existssols = {}
   175     existssols = {}
   176     unstable = set()
   176     unstable = set()
   177     invariants = {}
   177     invariants = {}
   178     for vname, var in rqlst.defined_vars.items():
   178     for vname, var in rqlst.defined_vars.items():
   179         vtype = newsols[0][vname]
   179         vtype = newsols[0][vname]
   180         if var._q_invariant or vname in varmap:
   180         if var._q_invariant:
   181             # remove invariant variable from solutions to remove duplicates
   181             # remove invariant variable from solutions to remove duplicates
   182             # later, then reinserting a type for the variable even later
   182             # later, then reinserting a type for the variable even later
   183             for sol in newsols:
   183             for sol in newsols:
   184                 invariants.setdefault(id(sol), {})[vname] = sol.pop(vname)
   184                 invariants.setdefault(id(sol), {})[vname] = sol.pop(vname)
   185         elif var.scope is not rqlst:
   185         elif var.scope is not rqlst:
   374         self.outer_pending = {}
   374         self.outer_pending = {}
   375         self.duplicate_switches = []
   375         self.duplicate_switches = []
   376         self.aliases = {}
   376         self.aliases = {}
   377         self.restrictions = []
   377         self.restrictions = []
   378         self._restr_stack = []
   378         self._restr_stack = []
   379         self.ignore_varmap = False
       
   380         self._needs_source_cb = {}
   379         self._needs_source_cb = {}
   381 
   380 
   382     def merge_source_cbs(self, needs_source_cb):
   381     def merge_source_cbs(self, needs_source_cb):
   383         if self.needs_source_cb is None:
   382         if self.needs_source_cb is None:
   384             self.needs_source_cb = needs_source_cb
   383             self.needs_source_cb = needs_source_cb
   713         self._lock = threading.Lock()
   712         self._lock = threading.Lock()
   714         if attrmap is None:
   713         if attrmap is None:
   715             attrmap = {}
   714             attrmap = {}
   716         self.attr_map = attrmap
   715         self.attr_map = attrmap
   717 
   716 
   718     def generate(self, union, args=None, varmap=None):
   717     def generate(self, union, args=None):
   719         """return SQL queries and a variable dictionary from a RQL syntax tree
   718         """return SQL queries and a variable dictionary from a RQL syntax tree
   720 
   719 
   721         :partrqls: a list of couple (rqlst, solutions)
   720         :partrqls: a list of couple (rqlst, solutions)
   722         :args: optional dictionary with values of substitutions used in the query
   721         :args: optional dictionary with values of substitutions used in the query
   723         :varmap: optional dictionary mapping variable name to a special table
       
   724           name, in case the query as to fetch data from temporary tables
       
   725 
   722 
   726         return an sql string and a dictionary with substitutions values
   723         return an sql string and a dictionary with substitutions values
   727         """
   724         """
   728         if args is None:
   725         if args is None:
   729             args = {}
   726             args = {}
   730         if varmap is None:
       
   731             varmap =  {}
       
   732         self._lock.acquire()
   727         self._lock.acquire()
   733         self._args = args
   728         self._args = args
   734         self._varmap = varmap
       
   735         self._query_attrs = {}
   729         self._query_attrs = {}
   736         self._state = None
   730         self._state = None
   737         # self._not_scope_offset = 0
   731         # self._not_scope_offset = 0
   738         try:
   732         try:
   739             # union query for each rqlst / solution
   733             # union query for each rqlst / solution
   801         sols = select.solutions
   795         sols = select.solutions
   802         selectsortterms = distinct
   796         selectsortterms = distinct
   803         if len(sols) > 1:
   797         if len(sols) > 1:
   804             # remove invariant from solutions
   798             # remove invariant from solutions
   805             sols, existssols, unstable = remove_unused_solutions(
   799             sols, existssols, unstable = remove_unused_solutions(
   806                 select, sols, self._varmap, self.schema)
   800                 select, sols, self.schema)
   807             if len(sols) > 1:
   801             if len(sols) > 1:
   808                 # if there is still more than one solution, a UNION will be
   802                 # if there is still more than one solution, a UNION will be
   809                 # generated and so sort terms have to be selected
   803                 # generated and so sort terms have to be selected
   810                 selectsortterms = True
   804                 selectsortterms = True
   811                 # and if select is using group by or aggregat, a wrapping
   805                 # and if select is using group by or aggregat, a wrapping
  1046                 else:
  1040                 else:
  1047                     # no variables in the RHS
  1041                     # no variables in the RHS
  1048                     sql = self._visit_attribute_relation(relation)
  1042                     sql = self._visit_attribute_relation(relation)
  1049         elif (rtype == 'is' and isinstance(rhs.children[0], Constant)
  1043         elif (rtype == 'is' and isinstance(rhs.children[0], Constant)
  1050               and rhs.children[0].eval(self._args) is None):
  1044               and rhs.children[0].eval(self._args) is None):
  1051             # special case "C is NULL"
  1045             lhssql = lhs.accept(self)
  1052             if lhs.name in self._varmap:
       
  1053                 lhssql = self._varmap[lhs.name]
       
  1054             else:
       
  1055                 lhssql = lhs.accept(self)
       
  1056             return '%s%s' % (lhssql, rhs.accept(self))
  1046             return '%s%s' % (lhssql, rhs.accept(self))
  1057         elif '%s.%s' % (lhs, relation.r_type) in self._varmap:
       
  1058             # relation has already been processed by a previous step
       
  1059             return ''
       
  1060         elif relation.optional:
  1047         elif relation.optional:
  1061             # OPTIONAL relation, generate a left|right outer join
  1048             # OPTIONAL relation, generate a left|right outer join
  1062             if rtype == 'identity' or rschema.inlined:
  1049             if rtype == 'identity' or rschema.inlined:
  1063                 sql = self._visit_outer_join_inlined_relation(relation, rschema)
  1050                 sql = self._visit_outer_join_inlined_relation(relation, rschema)
  1064             else:
  1051             else:
  1086                 # column != 1234 may not get back rows where column is NULL...
  1073                 # column != 1234 may not get back rows where column is NULL...
  1087                 sql = '(%s IS NULL OR %s!=%s)' % (
  1074                 sql = '(%s IS NULL OR %s!=%s)' % (
  1088                     lhssql, lhssql, (rhsvar or rhsconst).accept(self))
  1075                     lhssql, lhssql, (rhsvar or rhsconst).accept(self))
  1089         elif rhsconst is not None:
  1076         elif rhsconst is not None:
  1090             sql = '%s=%s' % (lhssql, rhsconst.accept(self))
  1077             sql = '%s=%s' % (lhssql, rhsconst.accept(self))
  1091         elif isinstance(rhsvar, Variable) and rhsvar._q_invariant and \
  1078         elif isinstance(rhsvar, Variable) and rhsvar._q_invariant:
  1092                  not rhsvar.name in self._varmap:
       
  1093             # if the rhs variable is only linked to this relation, this mean we
  1079             # if the rhs variable is only linked to this relation, this mean we
  1094             # only want the relation to exists, eg NOT NULL in case of inlined
  1080             # only want the relation to exists, eg NOT NULL in case of inlined
  1095             # relation
  1081             # relation
  1096             if moresql is not None:
  1082             if moresql is not None:
  1097                 return moresql
  1083                 return moresql
  1105     def _process_relation_term(self, relation, rid, termvar, termconst, relfield):
  1091     def _process_relation_term(self, relation, rid, termvar, termconst, relfield):
  1106         if termconst or not termvar._q_invariant:
  1092         if termconst or not termvar._q_invariant:
  1107             termsql = termconst and termconst.accept(self) or termvar.accept(self)
  1093             termsql = termconst and termconst.accept(self) or termvar.accept(self)
  1108             yield '%s.%s=%s' % (rid, relfield, termsql)
  1094             yield '%s.%s=%s' % (rid, relfield, termsql)
  1109         elif termvar._q_invariant:
  1095         elif termvar._q_invariant:
  1110             # if the variable is mapped, generate restriction anyway
       
  1111             if termvar.name in self._varmap:
       
  1112                 termsql = termvar.accept(self)
       
  1113                 yield '%s.%s=%s' % (rid, relfield, termsql)
       
  1114             extrajoin = self._extra_join_sql(relation, '%s.%s' % (rid, relfield), termvar)
  1096             extrajoin = self._extra_join_sql(relation, '%s.%s' % (rid, relfield), termvar)
  1115             if extrajoin is not None:
  1097             if extrajoin is not None:
  1116                 yield extrajoin
  1098                 yield extrajoin
  1117 
  1099 
  1118     def _visit_relation(self, relation, rschema):
  1100     def _visit_relation(self, relation, rschema):
  1234         lhsvar, lhsconst, rhsvar, rhsconst = relation_info(relation)
  1216         lhsvar, lhsconst, rhsvar, rhsconst = relation_info(relation)
  1235         assert not (lhsconst and rhsconst), "doesn't make sense"
  1217         assert not (lhsconst and rhsconst), "doesn't make sense"
  1236         attr = 'eid' if relation.r_type == 'identity' else relation.r_type
  1218         attr = 'eid' if relation.r_type == 'identity' else relation.r_type
  1237         lhsalias = self._var_table(lhsvar)
  1219         lhsalias = self._var_table(lhsvar)
  1238         rhsalias = rhsvar and self._var_table(rhsvar)
  1220         rhsalias = rhsvar and self._var_table(rhsvar)
  1239         try:
  1221         if lhsalias is None:
  1240             lhssql = self._varmap['%s.%s' % (lhsvar.name, attr)]
  1222             lhssql = lhsconst.accept(self)
  1241         except KeyError:
  1223         elif attr == 'eid':
  1242             if lhsalias is None:
  1224             lhssql = lhsvar.accept(self)
  1243                 lhssql = lhsconst.accept(self)
  1225         else:
  1244             elif attr == 'eid':
  1226             lhssql = '%s.%s%s' % (lhsalias, SQL_PREFIX, attr)
  1245                 lhssql = lhsvar.accept(self)
       
  1246             else:
       
  1247                 lhssql = '%s.%s%s' % (lhsalias, SQL_PREFIX, attr)
       
  1248         condition = '%s=%s' % (lhssql, (rhsconst or rhsvar).accept(self))
  1227         condition = '%s=%s' % (lhssql, (rhsconst or rhsvar).accept(self))
  1249         # this is not a typo, rhs optional variable means lhs outer join and vice-versa
  1228         # this is not a typo, rhs optional variable means lhs outer join and vice-versa
  1250         if relation.optional == 'left':
  1229         if relation.optional == 'left':
  1251             lhsvar, rhsvar = rhsvar, lhsvar
  1230             lhsvar, rhsvar = rhsvar, lhsvar
  1252             lhsconst, rhsconst = rhsconst, lhsconst
  1231             lhsconst, rhsconst = rhsconst, lhsconst
  1283         (eg X attr1 A, Y attr2 A). In case of selection, nothing to do here.
  1262         (eg X attr1 A, Y attr2 A). In case of selection, nothing to do here.
  1284         """
  1263         """
  1285         ored = relation.ored()
  1264         ored = relation.ored()
  1286         for vref in rhs_vars:
  1265         for vref in rhs_vars:
  1287             var = vref.variable
  1266             var = vref.variable
  1288             if var.name in self._varmap:
       
  1289                 # ensure table is added
       
  1290                 self._var_info(var)
       
  1291             if isinstance(var, ColumnAlias):
  1267             if isinstance(var, ColumnAlias):
  1292                 # force sql generation whatever the computed principal
  1268                 # force sql generation whatever the computed principal
  1293                 principal = 1
  1269                 principal = 1
  1294             else:
  1270             else:
  1295                 principal = var.stinfo.get('principal')
  1271                 principal = var.stinfo.get('principal')
  1306                             if not rel is principal][0]
  1282                             if not rel is principal][0]
  1307                 else:
  1283                 else:
  1308                     _rel = relation
  1284                     _rel = relation
  1309                 lhssql = self._inlined_var_sql(_rel.children[0].variable,
  1285                 lhssql = self._inlined_var_sql(_rel.children[0].variable,
  1310                                                _rel.r_type)
  1286                                                _rel.r_type)
  1311                 try:
  1287                 sql = lhssql + relation.children[1].accept(self)
  1312                     self._state.ignore_varmap = True
       
  1313                     sql = lhssql + relation.children[1].accept(self)
       
  1314                 finally:
       
  1315                     self._state.ignore_varmap = False
       
  1316                 if relation.optional == 'right':
  1288                 if relation.optional == 'right':
  1317                     leftalias = self._var_table(principal.children[0].variable)
  1289                     leftalias = self._var_table(principal.children[0].variable)
  1318                     rightalias = self._var_table(relation.children[0].variable)
  1290                     rightalias = self._var_table(relation.children[0].variable)
  1319                     self._state.replace_tables_by_outer_join(
  1291                     self._state.replace_tables_by_outer_join(
  1320                         leftalias, rightalias, 'LEFT', sql)
  1292                         leftalias, rightalias, 'LEFT', sql)
  1329         table = self._var_table(lhs.variable)
  1301         table = self._var_table(lhs.variable)
  1330         if table is None:
  1302         if table is None:
  1331             assert rel.r_type == 'eid'
  1303             assert rel.r_type == 'eid'
  1332             lhssql = lhs.accept(self)
  1304             lhssql = lhs.accept(self)
  1333         else:
  1305         else:
  1334             try:
  1306             mapkey = '%s.%s' % (self._state.solution[lhs.name], rel.r_type)
  1335                 lhssql = self._varmap['%s.%s' % (lhs.name, rel.r_type)]
  1307             if mapkey in self.attr_map:
  1336             except KeyError:
  1308                 cb, sourcecb = self.attr_map[mapkey]
  1337                 mapkey = '%s.%s' % (self._state.solution[lhs.name], rel.r_type)
  1309                 if sourcecb:
  1338                 if mapkey in self.attr_map:
  1310                     # callback is a source callback, we can't use this
  1339                     cb, sourcecb = self.attr_map[mapkey]
  1311                     # attribute in restriction
  1340                     if sourcecb:
  1312                     raise QueryError("can't use %s (%s) in restriction"
  1341                         # callback is a source callback, we can't use this
  1313                                      % (mapkey, rel.as_string()))
  1342                         # attribute in restriction
  1314                 lhssql = cb(self, lhs.variable, rel)
  1343                         raise QueryError("can't use %s (%s) in restriction"
  1315             elif rel.r_type == 'eid':
  1344                                          % (mapkey, rel.as_string()))
  1316                 lhssql = lhs.variable._q_sql
  1345                     lhssql = cb(self, lhs.variable, rel)
  1317             else:
  1346                 elif rel.r_type == 'eid':
  1318                 lhssql = '%s.%s%s' % (table, SQL_PREFIX, rel.r_type)
  1347                     lhssql = lhs.variable._q_sql
       
  1348                 else:
       
  1349                     lhssql = '%s.%s%s' % (table, SQL_PREFIX, rel.r_type)
       
  1350         try:
  1319         try:
  1351             if rel._q_needcast == 'TODAY':
  1320             if rel._q_needcast == 'TODAY':
  1352                 sql = 'DATE(%s)%s' % (lhssql, rhssql)
  1321                 sql = 'DATE(%s)%s' % (lhssql, rhssql)
  1353             # XXX which cast function should be used
  1322             # XXX which cast function should be used
  1354             #elif rel._q_needcast == 'NOW':
  1323             #elif rel._q_needcast == 'NOW':
  1373         me_is_principal = lhsvar.stinfo.get('principal') is rel
  1342         me_is_principal = lhsvar.stinfo.get('principal') is rel
  1374         if me_is_principal:
  1343         if me_is_principal:
  1375             if lhsvar.stinfo['typerel'] is None:
  1344             if lhsvar.stinfo['typerel'] is None:
  1376                 # the variable is using the fti table, no join needed
  1345                 # the variable is using the fti table, no join needed
  1377                 jointo = None
  1346                 jointo = None
  1378             elif not lhsvar.name in self._varmap:
  1347             else:
  1379                 # join on entities instead of etype's table to get result for
  1348                 # join on entities instead of etype's table to get result for
  1380                 # external entities on multisources configurations
  1349                 # external entities on multisources configurations
  1381                 ealias = lhsvar._q_sqltable = '_' + lhsvar.name
  1350                 ealias = lhsvar._q_sqltable = '_' + lhsvar.name
  1382                 jointo = lhsvar._q_sql = '%s.eid' % ealias
  1351                 jointo = lhsvar._q_sql = '%s.eid' % ealias
  1383                 self._state.add_table('entities AS %s' % ealias, ealias)
  1352                 self._state.add_table('entities AS %s' % ealias, ealias)
  1508             rel = constant.relation()
  1477             rel = constant.relation()
  1509             if rel is not None:
  1478             if rel is not None:
  1510                 rel._q_needcast = value
  1479                 rel._q_needcast = value
  1511             return self.keyword_map[value]()
  1480             return self.keyword_map[value]()
  1512         if constant.type == 'Substitute':
  1481         if constant.type == 'Substitute':
  1513             try:
  1482             _id = value
  1514                 # we may found constant from simplified var in varmap
  1483             if PY2 and isinstance(_id, unicode):
  1515                 return self._mapped_term(constant, '%%(%s)s' % value)[0]
  1484                 _id = _id.encode()
  1516             except KeyError:
       
  1517                 _id = value
       
  1518                 if PY2 and isinstance(_id, unicode):
       
  1519                     _id = _id.encode()
       
  1520         else:
  1485         else:
  1521             _id = str(id(constant)).replace('-', '', 1)
  1486             _id = str(id(constant)).replace('-', '', 1)
  1522             self._query_attrs[_id] = value
  1487             self._query_attrs[_id] = value
  1523         return '%%(%s)s' % _id
  1488         return '%%(%s)s' % _id
  1524 
  1489 
  1527         # use accept, .variable may be a variable or a columnalias
  1492         # use accept, .variable may be a variable or a columnalias
  1528         return variableref.variable.accept(self)
  1493         return variableref.variable.accept(self)
  1529 
  1494 
  1530     def visit_columnalias(self, colalias):
  1495     def visit_columnalias(self, colalias):
  1531         """get the sql name for a subquery column alias"""
  1496         """get the sql name for a subquery column alias"""
  1532         if colalias.name in self._varmap:
       
  1533             sql = self._varmap[colalias.name]
       
  1534             table = sql.split('.', 1)[0]
       
  1535             colalias._q_sqltable = table
       
  1536             colalias._q_sql = sql
       
  1537             self._state.add_table(table)
       
  1538             return sql
       
  1539         return colalias._q_sql
  1497         return colalias._q_sql
  1540 
  1498 
  1541     def visit_variable(self, variable):
  1499     def visit_variable(self, variable):
  1542         """get the table name and sql string for a variable"""
  1500         """get the table name and sql string for a variable"""
  1543         #if contextrels is None and variable.name in self._state.done:
  1501         #if contextrels is None and variable.name in self._state.done:
  1545             if self._in_wrapping_query:
  1503             if self._in_wrapping_query:
  1546                 return 'T1.%s' % self._state.aliases[variable.name]
  1504                 return 'T1.%s' % self._state.aliases[variable.name]
  1547             return variable._q_sql
  1505             return variable._q_sql
  1548         self._state.done.add(variable.name)
  1506         self._state.done.add(variable.name)
  1549         vtablename = None
  1507         vtablename = None
  1550         if not self._state.ignore_varmap and variable.name in self._varmap:
  1508         if variable.stinfo['attrvar']:
  1551             sql, vtablename = self._var_info(variable)
       
  1552         elif variable.stinfo['attrvar']:
       
  1553             # attribute variable (systematically used in rhs of final
  1509             # attribute variable (systematically used in rhs of final
  1554             # relation(s)), get table name and sql from any rhs relation
  1510             # relation(s)), get table name and sql from any rhs relation
  1555             sql = self._linked_var_sql(variable)
  1511             sql = self._linked_var_sql(variable)
  1556         elif variable._q_invariant:
  1512         elif variable._q_invariant:
  1557             # since variable is invariant, we know we won't found final relation
  1513             # since variable is invariant, we know we won't found final relation
  1608             # no principal defined, relation is necessarily the principal and
  1564             # no principal defined, relation is necessarily the principal and
  1609             # so nothing to return here
  1565             # so nothing to return here
  1610             pass
  1566             pass
  1611         return None
  1567         return None
  1612 
  1568 
  1613     def _temp_table_scope(self, select, table):
  1569     def _var_info(self, var):
  1614         scope = 9999
  1570         scope = self._state.scopes[var.scope]
  1615         for var, sql in self._varmap.items():
  1571         etype = self._state.solution[var.name]
  1616             # skip "attribute variable" in varmap (such 'T.login')
  1572         # XXX this check should be moved in rql.stcheck
  1617             if not '.' in var and table == sql.split('.', 1)[0]:
  1573         if self.schema.eschema(etype).final:
  1618                 try:
  1574             raise BadRQLQuery(var.stmt.root)
  1619                     scope = min(scope, self._state.scopes[select.defined_vars[var].scope])
  1575         tablealias = '_' + var.name
  1620                 except KeyError:
  1576         sql = '%s.%seid' % (tablealias, SQL_PREFIX)
  1621                     scope = 0 # XXX
  1577         self._state.add_table('%s%s AS %s' % (SQL_PREFIX, etype, tablealias),
  1622                 if scope == 0:
  1578                               tablealias, scope=scope)
  1623                     break
       
  1624         return scope
       
  1625 
       
  1626     def _mapped_term(self, term, key):
       
  1627         """return sql and table alias to the `term`, mapped as `key` or raise
       
  1628         KeyError when the key is not found in the varmap
       
  1629         """
       
  1630         sql = self._varmap[key]
       
  1631         tablealias = sql.split('.', 1)[0]
       
  1632         scope = self._temp_table_scope(term.stmt, tablealias)
       
  1633         self._state.add_table(tablealias, scope=scope)
       
  1634         return sql, tablealias
  1579         return sql, tablealias
  1635 
  1580 
  1636     def _var_info(self, var):
       
  1637         try:
       
  1638             return self._mapped_term(var, var.name)
       
  1639         except KeyError:
       
  1640             scope = self._state.scopes[var.scope]
       
  1641             etype = self._state.solution[var.name]
       
  1642             # XXX this check should be moved in rql.stcheck
       
  1643             if self.schema.eschema(etype).final:
       
  1644                 raise BadRQLQuery(var.stmt.root)
       
  1645             tablealias = '_' + var.name
       
  1646             sql = '%s.%seid' % (tablealias, SQL_PREFIX)
       
  1647             self._state.add_table('%s%s AS %s' % (SQL_PREFIX, etype, tablealias),
       
  1648                            tablealias, scope=scope)
       
  1649         return sql, tablealias
       
  1650 
       
  1651     def _inlined_var_sql(self, var, rtype):
  1581     def _inlined_var_sql(self, var, rtype):
  1652         try:
  1582         # rtype may be an attribute relation when called from
  1653             sql = self._varmap['%s.%s' % (var.name, rtype)]
  1583         # _visit_var_attr_relation.  take care about 'eid' rtype, since in
  1654             scope = self._state.scopes[var.scope]
  1584         # some case we may use the `entities` table, so in that case we've
  1655             self._state.add_table(sql.split('.', 1)[0], scope=scope)
  1585         # to properly use variable'sql
  1656         except KeyError:
  1586         if rtype == 'eid':
  1657             # rtype may be an attribute relation when called from
  1587             sql = var.accept(self)
  1658             # _visit_var_attr_relation.  take care about 'eid' rtype, since in
  1588         else:
  1659             # some case we may use the `entities` table, so in that case we've
  1589             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
  1660             # to properly use variable'sql
       
  1661             if rtype == 'eid':
       
  1662                 sql = var.accept(self)
       
  1663             else:
       
  1664                 sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
       
  1665         return sql
  1590         return sql
  1666 
  1591 
  1667     def _linked_var_sql(self, variable):
  1592     def _linked_var_sql(self, variable):
  1668         if not self._state.ignore_varmap:
       
  1669             try:
       
  1670                 return self._varmap[variable.name]
       
  1671             except KeyError:
       
  1672                 pass
       
  1673         rel = (variable.stinfo.get('principal') or
  1593         rel = (variable.stinfo.get('principal') or
  1674                next(iter(variable.stinfo['rhsrelations'])))
  1594                next(iter(variable.stinfo['rhsrelations'])))
  1675         linkedvar = rel.children[0].variable
  1595         linkedvar = rel.children[0].variable
  1676         if rel.r_type == 'eid':
  1596         if rel.r_type == 'eid':
  1677             return linkedvar.accept(self)
  1597             return linkedvar.accept(self)
  1678         if isinstance(linkedvar, ColumnAlias):
  1598         if isinstance(linkedvar, ColumnAlias):
  1679             raise BadRQLQuery('variable %s should be selected by the subquery'
  1599             raise BadRQLQuery('variable %s should be selected by the subquery'
  1680                               % variable.name)
  1600                               % variable.name)
  1681         try:
  1601         mapkey = '%s.%s' % (self._state.solution[linkedvar.name], rel.r_type)
  1682             sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)]
  1602         if mapkey in self.attr_map:
  1683         except KeyError:
  1603             cb, sourcecb = self.attr_map[mapkey]
  1684             mapkey = '%s.%s' % (self._state.solution[linkedvar.name], rel.r_type)
  1604             if not sourcecb:
  1685             if mapkey in self.attr_map:
  1605                 return cb(self, linkedvar, rel)
  1686                 cb, sourcecb = self.attr_map[mapkey]
  1606             # attribute mapped at the source level (bfss for instance)
  1687                 if not sourcecb:
  1607             stmt = rel.stmt
  1688                     return cb(self, linkedvar, rel)
  1608             for selectidx, vref in iter_mapped_var_sels(stmt, variable):
  1689                 # attribute mapped at the source level (bfss for instance)
  1609                 stack = [cb]
  1690                 stmt = rel.stmt
  1610                 update_source_cb_stack(self._state, stmt, vref, stack)
  1691                 for selectidx, vref in iter_mapped_var_sels(stmt, variable):
  1611                 self._state._needs_source_cb[selectidx] = stack
  1692                     stack = [cb]
  1612         linkedvar.accept(self)
  1693                     update_source_cb_stack(self._state, stmt, vref, stack)
  1613         return '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type)
  1694                     self._state._needs_source_cb[selectidx] = stack
       
  1695             linkedvar.accept(self)
       
  1696             sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type)
       
  1697         return sql
       
  1698 
  1614 
  1699     # tables handling #########################################################
  1615     # tables handling #########################################################
  1700 
  1616 
  1701     def _var_table(self, var):
  1617     def _var_table(self, var):
  1702         var.accept(self)#.visit_variable(var)
  1618         var.accept(self)#.visit_variable(var)