401 def pop_scope(self): |
428 def pop_scope(self): |
402 del self.scopes[self.scope_nodes[-1]] |
429 del self.scopes[self.scope_nodes[-1]] |
403 self.scope_nodes.pop() |
430 self.scope_nodes.pop() |
404 restrictions = self.restrictions |
431 restrictions = self.restrictions |
405 self.restrictions = self._restr_stack.pop() |
432 self.restrictions = self._restr_stack.pop() |
406 return restrictions, self.actual_tables.pop() |
433 scope = len(self.actual_tables) - 1 |
|
434 # check if we have some outer chain for this scope |
|
435 matching_chains = [] |
|
436 for chain in self.outer_chains: |
|
437 for tablealias in chain: |
|
438 if self.tables[tablealias][0] < scope: |
|
439 # chain belongs to outer scope |
|
440 break |
|
441 else: |
|
442 # chain match current scope |
|
443 matching_chains.append(chain) |
|
444 # call to `tables_sql` will pop actual_tables |
|
445 tables = self.tables_sql(matching_chains) |
|
446 # cleanup outer join related structure for tables in matching chains |
|
447 for chain in matching_chains: |
|
448 self.outer_chains.remove(chain) |
|
449 for alias in chain: |
|
450 del self.outer_tables[alias] |
|
451 return restrictions, tables |
|
452 |
|
453 # tables handling ######################################################### |
|
454 |
|
455 def add_table(self, table, key=None, scope=-1): |
|
456 if key is None: |
|
457 key = table |
|
458 if key in self.tables: |
|
459 return |
|
460 if scope < 0: |
|
461 scope = len(self.actual_tables) + scope |
|
462 self.tables[key] = (scope, table) |
|
463 self.actual_tables[scope].append(table) |
|
464 |
|
465 def alias_and_add_table(self, tablename, scope=-1): |
|
466 alias = '%s%s' % (tablename, self.count) |
|
467 self.count += 1 |
|
468 self.add_table('%s AS %s' % (tablename, alias), alias, scope) |
|
469 return alias |
|
470 |
|
471 def relation_table(self, relation): |
|
472 """return the table alias used by the given relation""" |
|
473 if relation in self.done: |
|
474 return relation._q_sqltable |
|
475 rid = 'rel_%s%s' % (relation.r_type, self.count) |
|
476 # relation's table is belonging to the root scope if it is the principal |
|
477 # table of one of it's variable and if that variable belong's to parent |
|
478 # scope |
|
479 for varref in relation.iget_nodes(VariableRef): |
|
480 var = varref.variable |
|
481 if isinstance(var, ColumnAlias): |
|
482 scope = 0 |
|
483 break |
|
484 # XXX may have a principal without being invariant for this generation, |
|
485 # not sure this is a pb or not |
|
486 if var.stinfo.get('principal') is relation and var.scope is var.stmt: |
|
487 scope = 0 |
|
488 break |
|
489 else: |
|
490 scope = -1 |
|
491 self.count += 1 |
|
492 self.add_table('%s_relation AS %s' % (relation.r_type, rid), rid, scope=scope) |
|
493 relation._q_sqltable = rid |
|
494 self.done.add(relation) |
|
495 return rid |
|
496 |
|
497 def fti_table(self, relation, fti_table): |
|
498 """return the table alias used by the given has_text relation, |
|
499 `fti_table` being the table name for the plain text index |
|
500 """ |
|
501 if relation in self.done: |
|
502 try: |
|
503 return relation._q_sqltable |
|
504 except AttributeError: |
|
505 pass |
|
506 self.done.add(relation) |
|
507 scope = self.scopes[relation.scope] |
|
508 alias = self.alias_and_add_table(fti_table, scope=scope) |
|
509 relation._q_sqltable = alias |
|
510 return alias |
|
511 |
|
512 # outer join handling ###################################################### |
|
513 |
|
514 def mark_as_used_in_outer_join(self, tablealias, addpending=True): |
|
515 """Mark table of given alias as used in outer join. This must be called |
|
516 after `outer_tables[tablealias]` has been initialized. |
|
517 """ |
|
518 # remove a table from actual_table because it's used in an outer join |
|
519 # chain |
|
520 scope, tabledef = self.tables[tablealias] |
|
521 self.actual_tables[scope].remove(tabledef) |
|
522 # check if there are some pending outer join condition for this table |
|
523 if addpending: |
|
524 try: |
|
525 pending_conditions = self.outer_pending.pop(tablealias) |
|
526 except KeyError: |
|
527 pass |
|
528 else: |
|
529 self.outer_tables[tablealias][1].extend(pending_conditions) |
|
530 else: |
|
531 assert not tablealias in self.outer_pending |
|
532 |
|
533 def add_outer_join_condition(self, tablealias, condition): |
|
534 try: |
|
535 outer, conditions, chain = self.outer_tables[tablealias] |
|
536 conditions.append(condition) |
|
537 except KeyError: |
|
538 self.outer_pending.setdefault(tablealias, []).append(condition) |
|
539 |
|
540 def replace_tables_by_outer_join(self, leftalias, rightalias, |
|
541 outertype, condition): |
|
542 """tell we need <leftalias> <outertype> JOIN <rightalias> ON <condition> |
|
543 """ |
|
544 assert leftalias != rightalias, leftalias |
|
545 outer_tables = self.outer_tables |
|
546 louter, lconditions, lchain = outer_tables.get(leftalias, |
|
547 (None, None, None)) |
|
548 router, rconditions, rchain = outer_tables.get(rightalias, |
|
549 (None, None, None)) |
|
550 if lchain is None and rchain is None: |
|
551 # create a new outer chaine |
|
552 chain = [leftalias, rightalias] |
|
553 outer_tables[leftalias] = (None, [], chain) |
|
554 outer_tables[rightalias] = (outertype, [condition], chain) |
|
555 self.outer_chains.append(chain) |
|
556 self.mark_as_used_in_outer_join(leftalias, addpending=False) |
|
557 self.mark_as_used_in_outer_join(rightalias) |
|
558 elif lchain is None: |
|
559 # [A > B > C] + [D > A] -> [D > A > B > C] |
|
560 if rightalias == rchain[0]: |
|
561 outer_tables[leftalias] = (None, [], rchain) |
|
562 conditions = outer_tables[rightalias][1] + [condition] |
|
563 outer_tables[rightalias] = (outertype, conditions, rchain) |
|
564 rchain.insert(0, leftalias) |
|
565 else: |
|
566 # [A > B > C] + [D > B] -> [A > B > C < D] |
|
567 if outertype == 'LEFT': |
|
568 outertype = 'RIGHT' |
|
569 outer_tables[leftalias] = (outertype, [condition], rchain) |
|
570 rchain.append(leftalias) |
|
571 self.mark_as_used_in_outer_join(leftalias) |
|
572 elif rchain is None: |
|
573 # [A > B > C] + [B > D] -> [A > B > C > D] |
|
574 outer_tables[rightalias] = (outertype, [condition], lchain) |
|
575 lchain.append(rightalias) |
|
576 self.mark_as_used_in_outer_join(rightalias) |
|
577 elif lchain is rchain: |
|
578 # already in the same chain, simply check compatibility and append |
|
579 # the condition if it's ok |
|
580 lidx = lchain.index(leftalias) |
|
581 ridx = lchain.index(rightalias) |
|
582 if (outertype == 'FULL' and router != 'FULL') \ |
|
583 or (lidx < ridx and router != 'LEFT') \ |
|
584 or (ridx < lidx and louter != 'RIGHT'): |
|
585 raise BadRQLQuery() |
|
586 # merge conditions |
|
587 if lidx < ridx: |
|
588 rconditions.append(condition) |
|
589 else: |
|
590 lconditions.append(condition) |
|
591 else: |
|
592 if louter is not None: |
|
593 raise BadRQLQuery() |
|
594 # merge chains |
|
595 self.outer_chains.remove(lchain) |
|
596 self.mark_as_used_in_outer_join(leftalias) |
|
597 rchain += lchain |
|
598 for alias, (aouter, aconditions, achain) in outer_tables.iteritems(): |
|
599 if achain is lchain: |
|
600 outer_tables[alias] = (aouter, aconditions, rchain) |
|
601 |
|
602 # sql generation helpers ################################################### |
|
603 |
|
604 def tables_sql(self, outer_chains=None): |
|
605 """generate SQL for FROM clause""" |
|
606 # sort for test predictability |
|
607 tables = sorted(self.actual_tables.pop()) |
|
608 # process outer joins |
|
609 if outer_chains is None: |
|
610 assert not self.actual_tables, self.actual_tables |
|
611 assert not self.outer_pending |
|
612 outer_chains = self.outer_chains |
|
613 for chain in sorted(outer_chains): |
|
614 tablealias = chain[0] |
|
615 outertype, conditions, _ = self.outer_tables[tablealias] |
|
616 assert _ is chain, (chain, _) |
|
617 assert outertype is None, (chain, self.outer_chains) |
|
618 assert not conditions, (chain, self.outer_chains) |
|
619 assert len(chain) > 1 |
|
620 tabledef = self.tables[tablealias][1] |
|
621 outerjoin = [tabledef] |
|
622 for tablealias in chain[1:]: |
|
623 outertype, conditions, _ = self.outer_tables[tablealias] |
|
624 assert _ is chain, (chain, self.outer_chains) |
|
625 assert outertype in ('LEFT', 'RIGHT', 'FULL'), ( |
|
626 tablealias, outertype, conditions) |
|
627 assert isinstance(conditions, (list)), ( |
|
628 tablealias, outertype, conditions) |
|
629 tabledef = self.tables[tablealias][1] |
|
630 outerjoin.append('%s OUTER JOIN %s ON (%s)' % ( |
|
631 outertype, tabledef, ' AND '.join(conditions))) |
|
632 tables.append(' '.join(outerjoin)) |
|
633 return ', '.join(tables) |
|
634 |
407 |
635 |
408 def extract_fake_having_terms(having): |
636 def extract_fake_having_terms(having): |
409 """RQL's HAVING may be used to contains stuff that should go in the WHERE |
637 """RQL's HAVING may be used to contains stuff that should go in the WHERE |
410 clause of the SQL query, due to RQL grammar limitation. Split them... |
638 clause of the SQL query, due to RQL grammar limitation. Split them... |
411 |
639 |
596 self._in_wrapping_query = False |
825 self._in_wrapping_query = False |
597 self._state = state |
826 self._state = state |
598 try: |
827 try: |
599 sql = self._solutions_sql(select, morerestr, sols, distinct, |
828 sql = self._solutions_sql(select, morerestr, sols, distinct, |
600 needalias or needwrap) |
829 needalias or needwrap) |
601 # generate groups / having before wrapping query selection to |
830 # generate groups / having before wrapping query selection to get |
602 # get correct column aliases |
831 # correct column aliases |
603 self._in_wrapping_query = needwrap |
832 self._in_wrapping_query = needwrap |
604 if groups: |
833 if groups: |
605 # no constant should be inserted in GROUP BY else the backend will |
834 # no constant should be inserted in GROUP BY else the backend |
606 # interpret it as a positional index in the selection |
835 # will interpret it as a positional index in the selection |
607 groups = ','.join(vref.accept(self) for vref in groups |
836 groups = ','.join(vref.accept(self) for vref in groups |
608 if not isinstance(vref, Constant)) |
837 if not isinstance(vref, Constant)) |
609 if having: |
838 if having: |
610 # filter out constants as for GROUP BY |
839 # filter out constants as for GROUP BY |
611 having = ' AND '.join(term.accept(self) for term in having |
840 having = ' AND '.join(term.accept(self) for term in having |
612 if not isinstance(term, Constant)) |
841 if not isinstance(term, Constant)) |
613 if needwrap: |
842 if needwrap: |
614 sql = '%s FROM (%s) AS T1' % (self._selection_sql(outerselection, distinct, |
843 sql = '%s FROM (%s) AS T1' % ( |
615 needalias), |
844 self._selection_sql(outerselection, distinct,needalias), |
616 sql) |
845 sql) |
617 if groups: |
846 if groups: |
618 sql += '\nGROUP BY %s' % groups |
847 sql += '\nGROUP BY %s' % groups |
619 if having: |
848 if having: |
620 sql += '\nHAVING %s' % having |
849 sql += '\nHAVING %s' % having |
621 # sort |
850 # sort |
923 -> Y LEFT OUTER JOIN relation ON (relation.eid_to=Y.eid) |
1145 -> Y LEFT OUTER JOIN relation ON (relation.eid_to=Y.eid) |
924 LEFT OUTER JOIN X ON (relation.eid_from=X.eid) |
1146 LEFT OUTER JOIN X ON (relation.eid_from=X.eid) |
925 elif it's a full outer join: |
1147 elif it's a full outer join: |
926 -> X FULL OUTER JOIN Y ON (X.relation=Y.eid) |
1148 -> X FULL OUTER JOIN Y ON (X.relation=Y.eid) |
927 """ |
1149 """ |
928 lhsvar, lhsconst, rhsvar, rhsconst = relation_info(relation) |
1150 leftvar, leftconst, rightvar, rightconst = relation_info(relation) |
929 if relation.optional == 'right': |
1151 assert not (leftconst and rightconst), "doesn't make sense" |
|
1152 if relation.optional == 'left': |
|
1153 leftvar, rightvar = rightvar, leftvar |
|
1154 leftconst, rightconst = rightconst, leftconst |
|
1155 joinattr, restrattr = 'eid_to', 'eid_from' |
|
1156 else: |
930 joinattr, restrattr = 'eid_from', 'eid_to' |
1157 joinattr, restrattr = 'eid_from', 'eid_to' |
931 else: |
1158 # search table for this variable, to use as left table of the outer join |
932 lhsvar, rhsvar = rhsvar, lhsvar |
1159 leftalias = None |
933 lhsconst, rhsconst = rhsconst, lhsconst |
1160 if leftvar: |
934 joinattr, restrattr = 'eid_to', 'eid_from' |
1161 # take care, may return None for invariant variable |
935 if relation.optional == 'both': |
1162 leftalias = self._var_table(leftvar) |
936 outertype = 'FULL' |
1163 if leftalias is None: |
937 else: |
1164 if leftvar.stinfo['principal'] is not relation: |
938 outertype = 'LEFT' |
1165 # use variable's principal relation |
939 if rschema.inlined or relation.r_type == 'identity': |
1166 leftalias = leftvar.stinfo['principal']._q_sqltable |
940 self._state.done.add(relation) |
1167 else: |
941 t1 = self._var_table(lhsvar) |
1168 # search for relation on which we should join |
942 if relation.r_type == 'identity': |
1169 for orelation in leftvar.stinfo['relations']: |
943 attr = 'eid' |
1170 if (orelation is not relation and |
944 else: |
1171 not self.schema.rschema(orelation.r_type).final): |
945 attr = relation.r_type |
1172 break |
946 # reset lhs/rhs, we need the initial order now |
1173 else: |
947 lhs, rhs = relation.get_variable_parts() |
1174 for orelation in rightvar.stinfo['relations']: |
948 if '%s.%s' % (lhs.name, attr) in self._varmap: |
1175 if (orelation is not relation and |
949 lhssql = self._varmap['%s.%s' % (lhs.name, attr)] |
1176 not self.schema.rschema(orelation.r_type).final |
950 else: |
1177 and orelation.optional): |
951 lhssql = '%s.%s%s' % (self._var_table(lhs.variable), SQL_PREFIX, attr) |
1178 break |
952 if not rhsvar is None: |
1179 else: |
953 t2 = self._var_table(rhsvar) |
1180 # unexpected |
954 if t2 is None: |
1181 assert False, leftvar |
955 if rhsconst is not None: |
1182 leftalias = self._state.relation_table(orelation) |
956 # inlined relation with invariant as rhs |
1183 # right table of the outer join |
957 condition = '%s=%s' % (lhssql, rhsconst.accept(self)) |
1184 rightalias = self._state.relation_table(relation) |
958 if relation.r_type != 'identity': |
1185 # compute join condition |
959 condition = '(%s OR %s IS NULL)' % (condition, lhssql) |
1186 if not leftconst or (leftvar and not leftvar._q_invariant): |
960 if not lhsvar.stinfo.get('optrelations'): |
1187 leftsql = leftvar.accept(self) |
961 return condition |
1188 else: |
962 self.add_outer_join_condition(lhsvar, t1, condition) |
1189 leftsql = leftconst.accept(self) |
963 return |
1190 condition = '%s.%s=%s' % (rightalias, joinattr, leftsql) |
964 else: |
1191 if rightconst: |
965 condition = '%s=%s' % (lhssql, rhsconst.accept(self)) |
1192 condition += ' AND %s.%s=%s' % (rightalias, restrattr, rightconst.accept(self)) |
966 self.add_outer_join_condition(lhsvar, t1, condition) |
1193 # record outer join |
967 join = '%s OUTER JOIN %s ON (%s=%s)' % ( |
1194 outertype = 'FULL' if relation.optional == 'both' else 'LEFT' |
968 outertype, self._state.tables[t2][1], lhssql, rhs.accept(self)) |
1195 self._state.replace_tables_by_outer_join(leftalias, rightalias, |
969 self.replace_tables_by_outer_join(join, t1, t2) |
1196 outertype, condition) |
970 return '' |
1197 # need another join? |
971 lhssql = lhsconst and lhsconst.accept(self) or lhsvar.accept(self) |
1198 if rightconst is None: |
972 rhssql = rhsconst and rhsconst.accept(self) or rhsvar.accept(self) |
1199 # we need another outer join for the other side of the relation (e.g. |
973 rid = self._relation_table(relation) |
1200 # for "X relation Y?" in RQL, we treated earlier the (cw_X.eid / |
974 if not lhsvar: |
1201 # relation.eid_from) join, now we've to do (relation.eid_to / |
975 join = '' |
1202 # cw_Y.eid) |
976 toreplace = [] |
1203 leftalias = rightalias |
977 maintable = rid |
1204 rightsql = rightvar.accept(self) # accept before using var_table |
978 else: |
1205 rightalias = self._var_table(rightvar) |
979 join = '%s OUTER JOIN %s ON (%s.%s=%s' % ( |
1206 if rightalias is None: |
980 outertype, self._state.tables[rid][1], rid, joinattr, lhssql) |
1207 if rightvar.stinfo['principal'] is not relation: |
981 toreplace = [rid] |
1208 self._state.replace_tables_by_outer_join( |
982 maintable = self._var_table(lhsvar) |
1209 leftalias, rightvar.stinfo['principal']._q_sqltable, |
983 if rhsconst: |
1210 outertype, '%s.%s=%s' % (leftalias, restrattr, rightvar.accept(self))) |
984 join += ' AND %s.%s=%s)' % (rid, restrattr, rhssql) |
1211 else: |
985 else: |
1212 self._state.replace_tables_by_outer_join( |
986 join += ')' |
1213 leftalias, rightalias, outertype, |
987 if not rhsconst: |
1214 '%s.%s=%s' % (leftalias, restrattr, rightvar.accept(self))) |
988 rhstable = rhsvar._q_sqltable |
1215 # this relation will hence be expressed in FROM clause, return nothing |
989 if rhstable: |
1216 # here |
990 assert rhstable is not None, rhsvar |
1217 return '' |
991 join += ' %s OUTER JOIN %s ON (%s.%s=%s)' % ( |
1218 |
992 outertype, self._state.tables[rhstable][1], rid, restrattr, |
1219 |
993 rhssql) |
1220 def _visit_outer_join_inlined_relation(self, relation, rschema): |
994 toreplace.append(rhstable) |
1221 leftvar, leftconst, rightvar, rightconst = relation_info(relation) |
995 self.replace_tables_by_outer_join(join, maintable, *toreplace) |
1222 assert not (leftconst and rightconst), "doesn't make sense" |
|
1223 if relation.optional != 'right': |
|
1224 leftvar, rightvar = rightvar, leftvar |
|
1225 leftconst, rightconst = rightconst, leftconst |
|
1226 outertype = 'FULL' if relation.optional == 'both' else 'LEFT' |
|
1227 leftalias = self._var_table(leftvar) |
|
1228 attr = 'eid' if relation.r_type == 'identity' else relation.r_type |
|
1229 lhs, rhs = relation.get_variable_parts() |
|
1230 try: |
|
1231 lhssql = self._varmap['%s.%s' % (lhs.name, attr)] |
|
1232 except KeyError: |
|
1233 lhssql = '%s.%s%s' % (self._var_table(lhs.variable), SQL_PREFIX, attr) |
|
1234 if rightvar is not None: |
|
1235 rightalias = self._var_table(rightvar) |
|
1236 if rightalias is None: |
|
1237 if rightconst is not None: |
|
1238 # inlined relation with invariant as rhs |
|
1239 condition = '%s=%s' % (lhssql, rightconst.accept(self)) |
|
1240 if relation.r_type != 'identity': |
|
1241 condition = '(%s OR %s IS NULL)' % (condition, lhssql) |
|
1242 if not leftvar.stinfo.get('optrelations'): |
|
1243 return condition |
|
1244 self._state.add_outer_join_condition(leftalias, condition) |
|
1245 return |
|
1246 self._state.replace_tables_by_outer_join( |
|
1247 leftalias, rightalias, outertype, '%s=%s' % (lhssql, rhs.accept(self))) |
996 return '' |
1248 return '' |
997 |
1249 |
998 def _visit_var_attr_relation(self, relation, rhs_vars): |
1250 def _visit_var_attr_relation(self, relation, rhs_vars): |
999 """visit an attribute relation with variable(s) in the RHS |
1251 """visit an attribute relation with variable(s) in the RHS |
1000 |
1252 |
1343 sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type) |
1597 sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type) |
1344 return sql |
1598 return sql |
1345 |
1599 |
1346 # tables handling ######################################################### |
1600 # tables handling ######################################################### |
1347 |
1601 |
1348 def alias_and_add_table(self, tablename, scope=-1): |
|
1349 alias = '%s%s' % (tablename, self._state.count) |
|
1350 self._state.count += 1 |
|
1351 self.add_table('%s AS %s' % (tablename, alias), alias, scope) |
|
1352 return alias |
|
1353 |
|
1354 def add_table(self, table, key=None, scope=-1): |
|
1355 if key is None: |
|
1356 key = table |
|
1357 if key in self._state.tables: |
|
1358 return |
|
1359 if scope < 0: |
|
1360 scope = len(self._state.actual_tables) + scope |
|
1361 self._state.tables[key] = (scope, table) |
|
1362 self._state.actual_tables[scope].append(table) |
|
1363 |
|
1364 def replace_tables_by_outer_join(self, substitute, lefttable, *tables): |
|
1365 for table in tables: |
|
1366 try: |
|
1367 scope, alias = self._state.tables[table] |
|
1368 self._state.actual_tables[scope].remove(alias) |
|
1369 except ValueError: # huum, not sure about what should be done here |
|
1370 msg = "%s already used in an outer join, don't know what to do!" |
|
1371 raise Exception(msg % table) |
|
1372 try: |
|
1373 tablealias = self._state.outer_tables[lefttable] |
|
1374 actualtables = self._state.actual_tables[-1] |
|
1375 except KeyError: |
|
1376 tablescope, tablealias = self._state.tables[lefttable] |
|
1377 actualtables = self._state.actual_tables[tablescope] |
|
1378 outerjoin = '%s %s' % (tablealias, substitute) |
|
1379 self._update_outer_tables(lefttable, actualtables, tablealias, outerjoin) |
|
1380 for table in tables: |
|
1381 self._state.outer_tables[table] = outerjoin |
|
1382 |
|
1383 def add_outer_join_condition(self, var, table, condition): |
|
1384 try: |
|
1385 tablealias = self._state.outer_tables[table] |
|
1386 actualtables = self._state.actual_tables[-1] |
|
1387 except KeyError: |
|
1388 for rel in var.stinfo.get('optrelations'): |
|
1389 self.visit_relation(rel) |
|
1390 assert self._state.outer_tables |
|
1391 self.add_outer_join_condition(var, table, condition) |
|
1392 return |
|
1393 before, after = tablealias.split(' AS %s ' % table, 1) |
|
1394 beforep, afterp = after.split(')', 1) |
|
1395 outerjoin = '%s AS %s %s AND %s) %s' % (before, table, beforep, |
|
1396 condition, afterp) |
|
1397 self._update_outer_tables(table, actualtables, tablealias, outerjoin) |
|
1398 |
|
1399 def _update_outer_tables(self, table, actualtables, oldalias, newalias): |
|
1400 actualtables.remove(oldalias) |
|
1401 actualtables.append(newalias) |
|
1402 self._state.outer_tables[table] = newalias |
|
1403 # some tables which have already been used as outer table and replaced |
|
1404 # by <oldalias> may not be reused here, though their associated value |
|
1405 # in the outer_tables dict has to be updated as well |
|
1406 for table, outerexpr in self._state.outer_tables.iteritems(): |
|
1407 if outerexpr == oldalias: |
|
1408 self._state.outer_tables[table] = newalias |
|
1409 |
|
1410 def _var_table(self, var): |
1602 def _var_table(self, var): |
1411 var.accept(self)#.visit_variable(var) |
1603 var.accept(self)#.visit_variable(var) |
1412 return var._q_sqltable |
1604 return var._q_sqltable |
1413 |
|
1414 def _relation_table(self, relation): |
|
1415 """return the table alias used by the given relation""" |
|
1416 if relation in self._state.done: |
|
1417 return relation._q_sqltable |
|
1418 assert not self.schema.rschema(relation.r_type).final, relation.r_type |
|
1419 rid = 'rel_%s%s' % (relation.r_type, self._state.count) |
|
1420 # relation's table is belonging to the root scope if it is the principal |
|
1421 # table of one of it's variable and if that variable belong's to parent |
|
1422 # scope |
|
1423 for varref in relation.iget_nodes(VariableRef): |
|
1424 var = varref.variable |
|
1425 if isinstance(var, ColumnAlias): |
|
1426 scope = 0 |
|
1427 break |
|
1428 # XXX may have a principal without being invariant for this generation, |
|
1429 # not sure this is a pb or not |
|
1430 if var.stinfo.get('principal') is relation and var.scope is var.stmt: |
|
1431 scope = 0 |
|
1432 break |
|
1433 else: |
|
1434 scope = -1 |
|
1435 self._state.count += 1 |
|
1436 self.add_table('%s_relation AS %s' % (relation.r_type, rid), rid, scope=scope) |
|
1437 relation._q_sqltable = rid |
|
1438 self._state.done.add(relation) |
|
1439 return rid |
|
1440 |
|
1441 def _fti_table(self, relation): |
|
1442 if relation in self._state.done: |
|
1443 try: |
|
1444 return relation._q_sqltable |
|
1445 except AttributeError: |
|
1446 pass |
|
1447 self._state.done.add(relation) |
|
1448 scope = self._state.scopes[relation.scope] |
|
1449 alias = self.alias_and_add_table(self.dbhelper.fti_table, scope=scope) |
|
1450 relation._q_sqltable = alias |
|
1451 return alias |
|