diff -r 672acc730ce5 -r d628defebc17 server/sources/rql2sql.py --- a/server/sources/rql2sql.py Thu May 14 10:24:56 2009 +0200 +++ b/server/sources/rql2sql.py Thu May 14 11:38:40 2009 +0200 @@ -17,7 +17,7 @@ -> direct join between nonfinal1 and nonfinal2, whatever X,Y, Z (unless inlined...) - + NOT IMPLEMENTED (and quite hard to implement) Potential optimization information is collected by the querier, sql generation @@ -41,7 +41,7 @@ from cubicweb.server.sqlutils import SQL_PREFIX from cubicweb.server.utils import cleanup_solutions -def _new_var(select, varname): +def _new_var(select, varname): newvar = select.get_variable(varname) if not 'relations' in newvar.stinfo: # not yet initialized @@ -61,7 +61,7 @@ _fill_to_wrap_rel(vref.variable, newselect, towrap, schema) elif rschema.is_final(): towrap.add( (var, rel) ) - + def rewrite_unstable_outer_join(select, solutions, unstable, schema): """if some optional variables are unstable, they should be selected in a subquery. This function check this and rewrite the rql syntax tree if @@ -104,7 +104,7 @@ var.stinfo['relations'].add(newrel) var.stinfo['rhsrelations'].add(newrel) if rel.optional in ('right', 'both'): - var.stinfo['optrelations'].add(newrel) + var.stinfo['optrelations'].add(newrel) # extract subquery solutions solutions = [sol.copy() for sol in solutions] cleanup_solutions(newselect, solutions) @@ -205,7 +205,7 @@ for vref in term.iget_nodes(VariableRef): if not vref in groups: groups.append(vref) - + def fix_selection(rqlst, selectedidx, needwrap, sorts, groups, having): if sorts: sort_term_selection(sorts, selectedidx, rqlst, not needwrap and groups) @@ -230,7 +230,7 @@ self.existssols = existssols self.unstablevars = unstablevars self.subtables = {} - + def reset(self, solution): """reset some visit variables""" self.solution = solution @@ -246,11 +246,11 @@ self.aliases = {} self.restrictions = [] self._restr_stack = [] - + def add_restriction(self, restr): if restr: self.restrictions.append(restr) - + def iter_exists_sols(self, exists): if not exists in self.existssols: yield 1 @@ -286,8 +286,8 @@ restrictions = self.restrictions self.restrictions = self._restr_stack.pop() return restrictions, self.actual_tables.pop() - - + + class SQLGenerator(object): """ generation of SQL from the fully expanded RQL syntax tree @@ -295,13 +295,13 @@ Groups and sort are not handled here since they should not be handled at this level (see cubicweb.server.querier) - + we should not have errors here ! WARNING: a CubicWebSQLGenerator instance is not thread safe, but generate is protected by a lock """ - + def __init__(self, schema, dbms_helper, dbencoding='UTF-8'): self.schema = schema self.dbms_helper = dbms_helper @@ -312,7 +312,7 @@ if not self.dbms_helper.union_parentheses_support: self.union_sql = self.noparen_union_sql self._lock = threading.Lock() - + def generate(self, union, args=None, varmap=None): """return SQL queries and a variable dictionnary from a RQL syntax tree @@ -355,7 +355,7 @@ sqls = (self.select_sql(select, needalias) for i, select in enumerate(union.children)) return '\nUNION ALL\n'.join(sqls) - + def select_sql(self, select, needalias=False): """return SQL queries and a variable dictionnary from a RQL syntax tree @@ -388,7 +388,7 @@ # query will be necessary if groups or select.has_aggregat: select.select_only_variables() - needwrap = True + needwrap = True else: existssols, unstable = {}, () state = StateInfo(existssols, unstable) @@ -441,7 +441,7 @@ sql += '\nHAVING %s' % having # sort if sorts: - sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm, + sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm, fselectidx) for sortterm in sorts) if fneedwrap: @@ -497,7 +497,7 @@ return '\nUNION\n'.join(sqls) else: return '\nUNION ALL\n'.join(sqls) - + def _selection_sql(self, selected, distinct, needaliasing=False): clause = [] for term in selected: @@ -546,7 +546,7 @@ return '(%s)' % ' OR '.join(res) return res[0] return '' - + def visit_not(self, node): self._state.push_scope() csql = node.children[0].accept(self) @@ -581,7 +581,7 @@ if not sqls: return '' return 'EXISTS(%s)' % ' UNION '.join(sqls) - + def _visit_exists(self, exists): self._state.push_scope() restriction = exists.children[0].accept(self) @@ -593,12 +593,12 @@ return '' if not tables: # XXX could leave surrounding EXISTS() in this case no? - sql = 'SELECT 1 WHERE %s' % restriction + sql = 'SELECT 1 WHERE %s' % restriction else: sql = 'SELECT 1 FROM %s WHERE %s' % (', '.join(tables), restriction) return sql - + def visit_relation(self, relation): """generate SQL for a relation""" rtype = relation.r_type @@ -691,7 +691,7 @@ extrajoin = self._extra_join_sql(relation, '%s.%s' % (rid, relfield), termvar) if extrajoin: yield extrajoin - + def _visit_relation(self, relation, rschema): """generate SQL for a relation @@ -718,10 +718,10 @@ """ left outer join syntax (optional=='right'): X relation Y? - + right outer join syntax (optional=='left'): X? relation Y - + full outer join syntaxes (optional=='both'): X? relation Y? @@ -834,7 +834,7 @@ lhssql = self._inlined_var_sql(relation.children[0].variable, relation.r_type) return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels)) - + def _visit_attribute_relation(self, relation): """generate SQL for an attribute relation""" lhs, rhs = relation.get_parts() @@ -897,7 +897,7 @@ not_ = False return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args), jointo, not_) + restriction - + def visit_comparison(self, cmp, contextrels=None): """generate SQL for a comparaison""" if len(cmp.children) == 2: @@ -918,7 +918,7 @@ return '%s%s'% (operator, rhs.accept(self, contextrels)) return '%s%s%s'% (lhs.accept(self, contextrels), operator, rhs.accept(self, contextrels)) - + def visit_mathexpression(self, mexpr, contextrels=None): """generate SQL for a mathematic expression""" lhs, rhs = mexpr.get_parts() @@ -931,11 +931,11 @@ pass return '(%s %s %s)'% (lhs.accept(self, contextrels), operator, rhs.accept(self, contextrels)) - + def visit_function(self, func, contextrels=None): """generate SQL name for a function""" # function_description will check function is supported by the backend - sqlname = self.dbms_helper.func_sqlname(func.name) + sqlname = self.dbms_helper.func_sqlname(func.name) return '%s(%s)' % (sqlname, ', '.join(c.accept(self, contextrels) for c in func.children)) @@ -963,7 +963,7 @@ value = value.encode(self.dbencoding) self._query_attrs[_id] = value return '%%(%s)s' % _id - + def visit_variableref(self, variableref, contextrels=None): """get the sql name for a variable reference""" # use accept, .variable may be a variable or a columnalias @@ -979,7 +979,7 @@ self.add_table(table) return sql return colalias._q_sql - + def visit_variable(self, variable, contextrels=None): """get the table name and sql string for a variable""" if contextrels is None and variable.name in self._state.done: @@ -989,7 +989,7 @@ self._state.done.add(variable.name) vtablename = None if contextrels is None and variable.name in self._varmap: - sql, vtablename = self._var_info(variable) + sql, vtablename = self._var_info(variable) elif variable.stinfo['attrvar']: # attribute variable (systematically used in rhs of final # relation(s)), get table name and sql from any rhs relation @@ -1043,7 +1043,7 @@ # so nothing to return here pass return '' - + def _var_info(self, var): # if current var or one of its attribute is selected , it *must* # appear in the toplevel's FROM even if we're currently visiting @@ -1067,7 +1067,7 @@ sql = '%s.%seid' % (table, SQL_PREFIX) self.add_table('%s%s AS %s' % (SQL_PREFIX, etype, table), table, scope=scope) return sql, table - + def _inlined_var_sql(self, var, rtype): try: sql = self._varmap['%s.%s' % (var.name, rtype)] @@ -1077,14 +1077,14 @@ sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype) #self._state.done.add(var.name) return sql - + def _linked_var_sql(self, variable, contextrels=None): if contextrels is None: try: - return self._varmap[variable.name] + return self._varmap[variable.name] except KeyError: pass - rel = (contextrels and contextrels.get(variable.name) or + rel = (contextrels and contextrels.get(variable.name) or variable.stinfo.get('principal') or iter(variable.stinfo['rhsrelations']).next()) linkedvar = rel.children[0].variable @@ -1096,7 +1096,7 @@ try: sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)] except KeyError: - linkedvar.accept(self) + linkedvar.accept(self) sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type) return sql @@ -1107,7 +1107,7 @@ self._state.count += 1 self.add_table('%s AS %s' % (tablename, alias), alias) return alias - + def add_table(self, table, key=None, scope=-1): if key is None: key = table @@ -1115,7 +1115,7 @@ return self._state.tables[key] = (len(self._state.actual_tables) - 1, table) self._state.actual_tables[scope].append(table) - + def replace_tables_by_outer_join(self, substitute, lefttable, *tables): for table in tables: try: @@ -1160,8 +1160,8 @@ for table, outerexpr in self._state.outer_tables.iteritems(): if outerexpr == oldalias: self._state.outer_tables[table] = newalias - self._state.outer_tables[table] = newalias - + self._state.outer_tables[table] = newalias + def _var_table(self, var): var.accept(self)#.visit_variable(var) return var._q_sqltable @@ -1173,7 +1173,7 @@ assert not self.schema.rschema(relation.r_type).is_final(), relation.r_type rid = 'rel_%s%s' % (relation.r_type, self._state.count) # relation's table is belonging to the root scope if it is the principal - # table of one of it's variable and if that variable belong's to parent + # table of one of it's variable and if that variable belong's to parent # scope for varref in relation.iget_nodes(VariableRef): var = varref.variable @@ -1192,7 +1192,7 @@ relation._q_sqltable = rid self._state.done.add(relation) return rid - + def _fti_table(self, relation): if relation in self._state.done: try: @@ -1203,7 +1203,7 @@ alias = self.alias_and_add_table(self.dbms_helper.fti_table) relation._q_sqltable = alias return alias - + def _varmap_table_scope(self, select, table): """since a varmap table may be used for multiple variable, its scope is the most outer scope of each variables