diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-08-14 13:47:58 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-08-14 13:47:58 -0400 |
commit | 05864ab8a6883cd286f27a05f045ef41a7327fcd (patch) | |
tree | 5bda84a6028168f0d0c6edf5a1f12df214acf095 /lib | |
parent | 7ce34c2b34d7122f6972eaf900ba988c19a0bb98 (diff) | |
download | sqlalchemy-05864ab8a6883cd286f27a05f045ef41a7327fcd.tar.gz |
- fix concat() operator, tests
- [feature] Custom unary operators can now be
used by combining operators.custom_op() with
UnaryExpression().
- clean up the operator dispatch system and make it more consistent.
This does change the compiler contract for custom ops.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/sqlalchemy/dialects/mssql/base.py | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/mysql/base.py | 10 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/oracle/base.py | 7 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/postgresql/base.py | 6 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 87 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/expression.py | 8 |
6 files changed, 83 insertions, 43 deletions
diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index cc50fbe6b..83f6346a7 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -748,12 +748,12 @@ class MSSQLCompiler(compiler.SQLCompiler): def visit_char_length_func(self, fn, **kw): return "LEN%s" % self.function_argspec(fn, **kw) - def visit_concat_op(self, binary, **kw): + def visit_concat_op_binary(self, binary, operator, **kw): return "%s + %s" % \ (self.process(binary.left, **kw), self.process(binary.right, **kw)) - def visit_match_op(self, binary, **kw): + def visit_match_op_binary(self, binary, operator, **kw): return "CONTAINS (%s, %s)" % ( self.process(binary.left, **kw), self.process(binary.right, **kw)) @@ -969,14 +969,14 @@ class MSSQLStrictCompiler(MSSQLCompiler): """ ansi_bind_rules = True - def visit_in_op(self, binary, **kw): + def visit_in_op_binary(self, binary, operator, **kw): kw['literal_binds'] = True return "%s IN %s" % ( self.process(binary.left, **kw), self.process(binary.right, **kw) ) - def visit_notin_op(self, binary, **kw): + def visit_notin_op_binary(self, binary, operator, **kw): kw['literal_binds'] = True return "%s NOT IN %s" % ( self.process(binary.left, **kw), diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index d8bef2d4b..2c440605e 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -1254,11 +1254,13 @@ class MySQLCompiler(compiler.SQLCompiler): def visit_sysdate_func(self, fn, **kw): return "SYSDATE()" - def visit_concat_op(self, binary, **kw): - return "concat(%s, %s)" % (self.process(binary.left), self.process(binary.right)) + def visit_concat_op_binary(self, binary, operator, **kw): + return "concat(%s, %s)" % (self.process(binary.left), + self.process(binary.right)) - def visit_match_op(self, binary, **kw): - return "MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" % (self.process(binary.left), self.process(binary.right)) + def visit_match_op_binary(self, binary, operator, **kw): + return "MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" % \ + (self.process(binary.left), self.process(binary.right)) def get_from_hint_text(self, table, text): return text diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index 3c6ec55af..7279f254b 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -398,7 +398,7 @@ class OracleCompiler(compiler.SQLCompiler): compound_keywords = util.update_copy( compiler.SQLCompiler.compound_keywords, { - expression.CompoundSelect.EXCEPT : 'MINUS' + expression.CompoundSelect.EXCEPT: 'MINUS' } ) @@ -416,8 +416,9 @@ class OracleCompiler(compiler.SQLCompiler): def visit_char_length_func(self, fn, **kw): return "LENGTH" + self.function_argspec(fn, **kw) - def visit_match_op(self, binary, **kw): - return "CONTAINS (%s, %s)" % (self.process(binary.left), self.process(binary.right)) + def visit_match_op_binary(self, binary, operator, **kw): + return "CONTAINS (%s, %s)" % (self.process(binary.left), + self.process(binary.right)) def get_select_hint_text(self, byfroms): return " ".join( diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index d779935ce..a9ff988e8 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -619,12 +619,12 @@ ischema_names = { class PGCompiler(compiler.SQLCompiler): - def visit_match_op(self, binary, **kw): + def visit_match_op_binary(self, binary, operator, **kw): return "%s @@ to_tsquery(%s)" % ( self.process(binary.left), self.process(binary.right)) - def visit_ilike_op(self, binary, **kw): + def visit_ilike_op_binary(self, binary, operator, **kw): escape = binary.modifiers.get("escape", None) return '%s ILIKE %s' % \ (self.process(binary.left), self.process(binary.right)) \ @@ -632,7 +632,7 @@ class PGCompiler(compiler.SQLCompiler): (' ESCAPE ' + self.render_literal_value(escape, None)) or '') - def visit_notilike_op(self, binary, **kw): + def visit_notilike_op_binary(self, binary, operator, **kw): escape = binary.modifiers.get("escape", None) return '%s NOT ILIKE %s' % \ (self.process(binary.left), self.process(binary.right)) \ diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 2588de6b4..300cdb6b4 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -65,7 +65,7 @@ BIND_TEMPLATES = { } -OPERATORS = { +OPERATORS = { # binary operators.and_ : ' AND ', operators.or_ : ' OR ', @@ -594,7 +594,7 @@ class SQLCompiler(engine.Compiled): self.limit_clause(cs) or "" if self.ctes and \ - compound_index==1 and not entry: + compound_index == 1 and not entry: text = self._render_cte_clause() + text self.stack.pop(-1) @@ -604,12 +604,29 @@ class SQLCompiler(engine.Compiled): return text def visit_unary(self, unary, **kw): - s = unary.element._compiler_dispatch(self, **kw) if unary.operator: - s = OPERATORS[unary.operator] + s - if unary.modifier: - s = s + OPERATORS[unary.modifier] - return s + if unary.modifier: + raise exc.CompileError( + "Unary expression does not support operator " + "and modifier simultaneously") + disp = getattr(self, "visit_%s_unary_operator" % + unary.operator.__name__, None) + if disp: + return disp(unary, unary.operator, **kw) + else: + return self._generate_generic_unary_operator(unary, + OPERATORS[unary.operator], **kw) + elif unary.modifier: + disp = getattr(self, "visit_%s_unary_modifier" % + unary.modifier.__name__, None) + if disp: + return disp(unary, unary.modifier, **kw) + else: + return self._generate_generic_unary_modifier(unary, + OPERATORS[unary.modifier], **kw) + else: + raise exc.CompileError( + "Unary expression has no operator or modifier") def visit_binary(self, binary, **kw): # don't allow "? = ?" to render @@ -618,16 +635,38 @@ class SQLCompiler(engine.Compiled): isinstance(binary.right, sql.BindParameter): kw['literal_binds'] = True - return self._operator_dispatch(binary.operator, - binary, - lambda opstr: binary.left._compiler_dispatch(self, **kw) + - opstr + - binary.right._compiler_dispatch( - self, **kw), - **kw - ) + operator = binary.operator + disp = getattr(self, "visit_%s_binary" % operator.__name__, None) + if disp: + return disp(binary, operator, **kw) + else: + return self._generate_generic_binary(binary, + OPERATORS[operator], **kw) + + def visit_custom_op_binary(self, element, operator, **kw): + return self._generate_generic_binary(element, + " " + operator.opstring + " ", **kw) - def visit_like_op(self, binary, **kw): + def visit_custom_op_unary_operator(self, element, operator, **kw): + return self._generate_generic_unary_operator(element, + operator.opstring + " ", **kw) + + def visit_custom_op_unary_modifier(self, element, operator, **kw): + return self._generate_generic_unary_modifier(element, + " " + operator.opstring, **kw) + + def _generate_generic_binary(self, binary, opstring, **kw): + return binary.left._compiler_dispatch(self, **kw) + \ + opstring + \ + binary.right._compiler_dispatch(self, **kw) + + def _generate_generic_unary_operator(self, unary, opstring, **kw): + return opstring + unary.element._compiler_dispatch(self, **kw) + + def _generate_generic_unary_modifier(self, unary, opstring, **kw): + return unary.element._compiler_dispatch(self, **kw) + opstring + + def visit_like_op_binary(self, binary, operator, **kw): escape = binary.modifiers.get("escape", None) return '%s LIKE %s' % ( binary.left._compiler_dispatch(self, **kw), @@ -636,7 +675,7 @@ class SQLCompiler(engine.Compiled): (' ESCAPE ' + self.render_literal_value(escape, None)) or '') - def visit_notlike_op(self, binary, **kw): + def visit_notlike_op_binary(self, binary, operator, **kw): escape = binary.modifiers.get("escape", None) return '%s NOT LIKE %s' % ( binary.left._compiler_dispatch(self, **kw), @@ -645,7 +684,7 @@ class SQLCompiler(engine.Compiled): (' ESCAPE ' + self.render_literal_value(escape, None)) or '') - def visit_ilike_op(self, binary, **kw): + def visit_ilike_op_binary(self, binary, operator, **kw): escape = binary.modifiers.get("escape", None) return 'lower(%s) LIKE lower(%s)' % ( binary.left._compiler_dispatch(self, **kw), @@ -654,7 +693,7 @@ class SQLCompiler(engine.Compiled): (' ESCAPE ' + self.render_literal_value(escape, None)) or '') - def visit_notilike_op(self, binary, **kw): + def visit_notilike_op_binary(self, binary, operator, **kw): escape = binary.modifiers.get("escape", None) return 'lower(%s) NOT LIKE lower(%s)' % ( binary.left._compiler_dispatch(self, **kw), @@ -663,16 +702,6 @@ class SQLCompiler(engine.Compiled): (' ESCAPE ' + self.render_literal_value(escape, None)) or '') - def visit_custom_op(self, element, dispatch_operator, dispatch_fn, **kw): - return dispatch_fn(" " + dispatch_operator.opstring + " ") - - def _operator_dispatch(self, operator, element, fn, **kw): - disp = getattr(self, "visit_%s" % operator.__name__, None) - if disp: - kw.update(dispatch_operator=operator, dispatch_fn=fn) - return disp(element, **kw) - else: - return fn(OPERATORS[operator]) def visit_bindparam(self, bindparam, within_columns_clause=False, literal_binds=False, **kwargs): diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 6021b40b1..31e24e564 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -2057,6 +2057,7 @@ class _DefaultColumnComparator(ColumnOperators): "mod": (__operate,), "truediv": (__operate,), "custom_op": (__operate,), + "concat_op": (__operate,), "lt": (__compare, operators.ge), "le": (__compare, operators.gt), "ne": (__compare, operators.eq), @@ -3475,7 +3476,14 @@ class Extract(ColumnElement): class UnaryExpression(ColumnElement): + """Define a 'unary' expression. + A unary expression has a single column expression + and an operator. The operator can be placed on the left + (where it is called the 'operator') or right (where it is called the + 'modifier') of the column expression. + + """ __visit_name__ = 'unary' def __init__(self, element, operator=None, modifier=None, |