250 self.actual_tables = [[]] |
250 self.actual_tables = [[]] |
251 for _, tsql in self.tables.itervalues(): |
251 for _, tsql in self.tables.itervalues(): |
252 self.actual_tables[-1].append(tsql) |
252 self.actual_tables[-1].append(tsql) |
253 self.outer_tables = {} |
253 self.outer_tables = {} |
254 self.duplicate_switches = [] |
254 self.duplicate_switches = [] |
255 self.attr_vars = {} |
|
256 self.aliases = {} |
255 self.aliases = {} |
257 self.restrictions = [] |
256 self.restrictions = [] |
258 self._restr_stack = [] |
257 self._restr_stack = [] |
|
258 self.ignore_varmap = False |
259 |
259 |
260 def add_restriction(self, restr): |
260 def add_restriction(self, restr): |
261 if restr: |
261 if restr: |
262 self.restrictions.append(restr) |
262 self.restrictions.append(restr) |
263 |
263 |
846 attribute variables are used either in the selection or for |
846 attribute variables are used either in the selection or for |
847 unification (eg X attr1 A, Y attr2 A). In case of selection, |
847 unification (eg X attr1 A, Y attr2 A). In case of selection, |
848 nothing to do here. |
848 nothing to do here. |
849 """ |
849 """ |
850 contextrels = {} |
850 contextrels = {} |
851 attrvars = self._state.attr_vars |
|
852 for var in rhs_vars: |
851 for var in rhs_vars: |
853 try: |
|
854 contextrels[var.name] = attrvars[var.name] |
|
855 except KeyError: |
|
856 attrvars[var.name] = relation |
|
857 if var.name in self._varmap: |
852 if var.name in self._varmap: |
858 # ensure table is added |
853 # ensure table is added |
859 self._var_info(var.variable) |
854 self._var_info(var.variable) |
|
855 principal = var.variable.stinfo.get('principal') |
|
856 if principal is not None and principal is not relation: |
|
857 contextrels[var.name] = relation |
860 if not contextrels: |
858 if not contextrels: |
861 relation.children[1].accept(self, contextrels) |
|
862 return '' |
859 return '' |
863 # at least one variable is already in attr_vars, this means we have to |
860 # we have to generate unification expression |
864 # generate unification expression |
|
865 lhssql = self._inlined_var_sql(relation.children[0].variable, |
861 lhssql = self._inlined_var_sql(relation.children[0].variable, |
866 relation.r_type) |
862 relation.r_type) |
867 return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels)) |
863 try: |
|
864 self._state.ignore_varmap = True |
|
865 return '%s%s' % (lhssql, relation.children[1].accept(self)) |
|
866 finally: |
|
867 self._state.ignore_varmap = False |
868 |
868 |
869 def _visit_attribute_relation(self, rel): |
869 def _visit_attribute_relation(self, rel): |
870 """generate SQL for an attribute relation""" |
870 """generate SQL for an attribute relation""" |
871 lhs, rhs = rel.get_parts() |
871 lhs, rhs = rel.get_parts() |
872 rhssql = rhs.accept(self) |
872 rhssql = rhs.accept(self) |
930 else: |
930 else: |
931 not_ = False |
931 not_ = False |
932 return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args), |
932 return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args), |
933 jointo, not_) + restriction |
933 jointo, not_) + restriction |
934 |
934 |
935 def visit_comparison(self, cmp, contextrels=None): |
935 def visit_comparison(self, cmp): |
936 """generate SQL for a comparison""" |
936 """generate SQL for a comparison""" |
937 if len(cmp.children) == 2: |
937 if len(cmp.children) == 2: |
938 # XXX occurs ? |
938 # XXX occurs ? |
939 lhs, rhs = cmp.children |
939 lhs, rhs = cmp.children |
940 else: |
940 else: |
948 operator = ' %s ' % operator |
948 operator = ' %s ' % operator |
949 elif (operator == '=' and isinstance(rhs, Constant) |
949 elif (operator == '=' and isinstance(rhs, Constant) |
950 and rhs.eval(self._args) is None): |
950 and rhs.eval(self._args) is None): |
951 if lhs is None: |
951 if lhs is None: |
952 return ' IS NULL' |
952 return ' IS NULL' |
953 return '%s IS NULL' % lhs.accept(self, contextrels) |
953 return '%s IS NULL' % lhs.accept(self) |
954 elif isinstance(rhs, Function) and rhs.name == 'IN': |
954 elif isinstance(rhs, Function) and rhs.name == 'IN': |
955 assert operator == '=' |
955 assert operator == '=' |
956 operator = ' ' |
956 operator = ' ' |
957 if lhs is None: |
957 if lhs is None: |
958 return '%s%s'% (operator, rhs.accept(self, contextrels)) |
958 return '%s%s'% (operator, rhs.accept(self)) |
959 return '%s%s%s'% (lhs.accept(self, contextrels), operator, |
959 return '%s%s%s'% (lhs.accept(self), operator, rhs.accept(self)) |
960 rhs.accept(self, contextrels)) |
960 |
961 |
961 def visit_mathexpression(self, mexpr): |
962 def visit_mathexpression(self, mexpr, contextrels=None): |
|
963 """generate SQL for a mathematic expression""" |
962 """generate SQL for a mathematic expression""" |
964 lhs, rhs = mexpr.get_parts() |
963 lhs, rhs = mexpr.get_parts() |
965 # check for string concatenation |
964 # check for string concatenation |
966 operator = mexpr.operator |
965 operator = mexpr.operator |
967 try: |
966 try: |
968 if mexpr.operator == '+' and mexpr.get_type(self._state.solution, self._args) == 'String': |
967 if mexpr.operator == '+' and mexpr.get_type(self._state.solution, self._args) == 'String': |
969 operator = '||' |
968 operator = '||' |
970 except CoercionError: |
969 except CoercionError: |
971 pass |
970 pass |
972 return '(%s %s %s)'% (lhs.accept(self, contextrels), operator, |
971 return '(%s %s %s)'% (lhs.accept(self), operator, rhs.accept(self)) |
973 rhs.accept(self, contextrels)) |
972 |
974 |
973 def visit_function(self, func): |
975 def visit_function(self, func, contextrels=None): |
|
976 """generate SQL name for a function""" |
974 """generate SQL name for a function""" |
977 # function_description will check function is supported by the backend |
975 # function_description will check function is supported by the backend |
978 sqlname = self.dbms_helper.func_sqlname(func.name) |
976 sqlname = self.dbms_helper.func_sqlname(func.name) |
979 return '%s(%s)' % (sqlname, ', '.join(c.accept(self, contextrels) |
977 return '%s(%s)' % (sqlname, ', '.join(c.accept(self) |
980 for c in func.children)) |
978 for c in func.children)) |
981 |
979 |
982 def visit_constant(self, constant, contextrels=None): |
980 def visit_constant(self, constant): |
983 """generate SQL name for a constant""" |
981 """generate SQL name for a constant""" |
984 value = constant.value |
982 value = constant.value |
985 if constant.type is None: |
983 if constant.type is None: |
986 return 'NULL' |
984 return 'NULL' |
987 if constant.type == 'Int' and isinstance(constant.parent, SortTerm): |
985 if constant.type == 'Int' and isinstance(constant.parent, SortTerm): |
1002 if isinstance(value, unicode): |
1000 if isinstance(value, unicode): |
1003 value = value.encode(self.dbencoding) |
1001 value = value.encode(self.dbencoding) |
1004 self._query_attrs[_id] = value |
1002 self._query_attrs[_id] = value |
1005 return '%%(%s)s' % _id |
1003 return '%%(%s)s' % _id |
1006 |
1004 |
1007 def visit_variableref(self, variableref, contextrels=None): |
1005 def visit_variableref(self, variableref): |
1008 """get the sql name for a variable reference""" |
1006 """get the sql name for a variable reference""" |
1009 # use accept, .variable may be a variable or a columnalias |
1007 # use accept, .variable may be a variable or a columnalias |
1010 return variableref.variable.accept(self, contextrels) |
1008 return variableref.variable.accept(self) |
1011 |
1009 |
1012 def visit_columnalias(self, colalias, contextrels=None): |
1010 def visit_columnalias(self, colalias): |
1013 """get the sql name for a subquery column alias""" |
1011 """get the sql name for a subquery column alias""" |
1014 if colalias.name in self._varmap: |
1012 if colalias.name in self._varmap: |
1015 sql = self._varmap[colalias.name] |
1013 sql = self._varmap[colalias.name] |
1016 table = sql.split('.', 1)[0] |
1014 table = sql.split('.', 1)[0] |
1017 colalias._q_sqltable = table |
1015 colalias._q_sqltable = table |
1018 colalias._q_sql = sql |
1016 colalias._q_sql = sql |
1019 self.add_table(table) |
1017 self.add_table(table) |
1020 return sql |
1018 return sql |
1021 return colalias._q_sql |
1019 return colalias._q_sql |
1022 |
1020 |
1023 def visit_variable(self, variable, contextrels=None): |
1021 def visit_variable(self, variable): |
1024 """get the table name and sql string for a variable""" |
1022 """get the table name and sql string for a variable""" |
1025 if contextrels is None and variable.name in self._state.done: |
1023 #if contextrels is None and variable.name in self._state.done: |
|
1024 if variable.name in self._state.done: |
1026 if self._in_wrapping_query: |
1025 if self._in_wrapping_query: |
1027 return 'T1.%s' % self._state.aliases[variable.name] |
1026 return 'T1.%s' % self._state.aliases[variable.name] |
1028 return variable._q_sql |
1027 return variable._q_sql |
1029 self._state.done.add(variable.name) |
1028 self._state.done.add(variable.name) |
1030 vtablename = None |
1029 vtablename = None |
1031 if contextrels is None and variable.name in self._varmap: |
1030 if not self._state.ignore_varmap and variable.name in self._varmap: |
1032 sql, vtablename = self._var_info(variable) |
1031 sql, vtablename = self._var_info(variable) |
1033 elif variable.stinfo['attrvar']: |
1032 elif variable.stinfo['attrvar']: |
1034 # attribute variable (systematically used in rhs of final |
1033 # attribute variable (systematically used in rhs of final |
1035 # relation(s)), get table name and sql from any rhs relation |
1034 # relation(s)), get table name and sql from any rhs relation |
1036 sql = self._linked_var_sql(variable, contextrels) |
1035 sql = self._linked_var_sql(variable) |
1037 elif variable._q_invariant: |
1036 elif variable._q_invariant: |
1038 # since variable is invariant, we know we won't found final relation |
1037 # since variable is invariant, we know we won't found final relation |
1039 principal = variable.stinfo['principal'] |
1038 principal = variable.stinfo['principal'] |
1040 if principal is None: |
1039 if principal is None: |
1041 vtablename = '_' + variable.name |
1040 vtablename = '_' + variable.name |
1054 elif principal.r_type == 'has_text': |
1053 elif principal.r_type == 'has_text': |
1055 sql = '%s.%s' % (self._fti_table(principal), |
1054 sql = '%s.%s' % (self._fti_table(principal), |
1056 self.dbms_helper.fti_uid_attr) |
1055 self.dbms_helper.fti_uid_attr) |
1057 elif principal in variable.stinfo['rhsrelations']: |
1056 elif principal in variable.stinfo['rhsrelations']: |
1058 if self.schema.rschema(principal.r_type).inlined: |
1057 if self.schema.rschema(principal.r_type).inlined: |
1059 sql = self._linked_var_sql(variable, contextrels) |
1058 sql = self._linked_var_sql(variable) |
1060 else: |
1059 else: |
1061 sql = '%s.eid_to' % self._relation_table(principal) |
1060 sql = '%s.eid_to' % self._relation_table(principal) |
1062 else: |
1061 else: |
1063 sql = '%s.eid_from' % self._relation_table(principal) |
1062 sql = '%s.eid_from' % self._relation_table(principal) |
1064 else: |
1063 else: |
1074 def _extra_join_sql(self, relation, sql, var): |
1073 def _extra_join_sql(self, relation, sql, var): |
1075 # if rhs var is invariant, and this relation is not its principal, |
1074 # if rhs var is invariant, and this relation is not its principal, |
1076 # generate extra join |
1075 # generate extra join |
1077 try: |
1076 try: |
1078 if not var.stinfo['principal'] is relation: |
1077 if not var.stinfo['principal'] is relation: |
1079 # need a predicable result for tests |
1078 op = relation.operator() |
1080 return '%s=%s' % tuple(sorted((sql, var.accept(self)))) |
1079 if op == '=': |
|
1080 # need a predicable result for tests |
|
1081 args = sorted( (sql, var.accept(self)) ) |
|
1082 args.insert(1, op) |
|
1083 else: |
|
1084 args = (sql, op, var.accept(self)) |
|
1085 return '%s%s%s' % tuple(args) |
1081 except KeyError: |
1086 except KeyError: |
1082 # no principal defined, relation is necessarily the principal and |
1087 # no principal defined, relation is necessarily the principal and |
1083 # so nothing to return here |
1088 # so nothing to return here |
1084 pass |
1089 pass |
1085 return '' |
1090 return '' |
1121 except KeyError: |
1126 except KeyError: |
1122 sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype) |
1127 sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype) |
1123 #self._state.done.add(var.name) |
1128 #self._state.done.add(var.name) |
1124 return sql |
1129 return sql |
1125 |
1130 |
1126 def _linked_var_sql(self, variable, contextrels=None): |
1131 def _linked_var_sql(self, variable): |
1127 if contextrels is None: |
1132 if not self._state.ignore_varmap: |
1128 try: |
1133 try: |
1129 return self._varmap[variable.name] |
1134 return self._varmap[variable.name] |
1130 except KeyError: |
1135 except KeyError: |
1131 pass |
1136 pass |
1132 rel = (contextrels and contextrels.get(variable.name) or |
1137 rel = (variable.stinfo.get('principal') or |
1133 variable.stinfo.get('principal') or |
|
1134 iter(variable.stinfo['rhsrelations']).next()) |
1138 iter(variable.stinfo['rhsrelations']).next()) |
1135 linkedvar = rel.children[0].variable |
1139 linkedvar = rel.children[0].variable |
1136 if rel.r_type == 'eid': |
1140 if rel.r_type == 'eid': |
1137 return linkedvar.accept(self) |
1141 return linkedvar.accept(self) |
1138 if isinstance(linkedvar, ColumnAlias): |
1142 if isinstance(linkedvar, ColumnAlias): |