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