From: Anton Kovalevich Date: Fri, 26 Mar 2021 01:46:35 +0000 (+0300) Subject: Move dialect-specific code to it's directory + implement against modifier X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=218c3dc50f5dd8aa5d5b37bc1fcb462bb2dc9414;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Move dialect-specific code to it's directory + implement against modifier --- diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index 7910415532..8eb2250ac7 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -934,6 +934,7 @@ from sqlalchemy import literal_column from sqlalchemy import text from sqlalchemy.sql import visitors from . import reflection as _reflection +from .expression_enum import MatchExpressionModifier from .enumerated import ENUM from .enumerated import SET from .json import JSON @@ -1588,17 +1589,25 @@ class MySQLCompiler(compiler.SQLCompiler): ) def visit_match_op_binary(self, binary, operator, **kw): - boolean_mode = kw.pop('boolean_mode', True) + modifier = kw.pop('modifier', MatchExpressionModifier.in_boolean_mode) - if boolean_mode: - template = "MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" - else: - template = "MATCH (%s) AGAINST (%s)" + match_clause = self.process(binary.left, **kw) + against_clause = self.process(binary.right, **kw) - return template % ( - self.process(binary.left, **kw), - self.process(binary.right, **kw), - ) + if modifier: + if not isinstance(modifier, MatchExpressionModifier): + raise exc.CompileError( + "The `modifier` keyword argument must be a member of " + "`sqlalchemy.mysql.expression_enum." + "MatchExpressionModifier` enum or `None`" + ) + + against_clause = ' '.join(( + against_clause, + modifier.value, + )) + + return "MATCH (%s) AGAINST (%s)" % (match_clause, against_clause) def get_from_hint_text(self, table, text): return text diff --git a/lib/sqlalchemy/dialects/mysql/expression.py b/lib/sqlalchemy/dialects/mysql/expression.py new file mode 100644 index 0000000000..c8da18549f --- /dev/null +++ b/lib/sqlalchemy/dialects/mysql/expression.py @@ -0,0 +1,59 @@ +from ...sql.elements import ClauseElementBatch + + +def match(*clauselist, against, **kwargs): + """Produce a ``MATCH (X, Y) AGAINST ('TEXT')`` clause. + + E.g.:: + + from sqlalchemy import match + + match_columns_where = match( + users_table.c.firstname, + users_table.c.lastname, + against="John Connor", + ) + + match_columns_order = match( + users_table.c.firstname, + users_table.c.lastname, + against="John Connor", + boolean_mode=False, + ) + + stmt = select(users_table)\ + .where(match_columns_where)\ + .order_by(match_columns_order) + + Would produce SQL resembling:: + + SELECT id, firstname, lastname FROM user + WHERE MATCH(firstname, lastname) + AGAINST (:param_1 IN BOOLEAN MODE) + ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) + + The :func:`.match` function is a standalone version of the + :meth:`_expression.ColumnElement.match` method available on all + SQL expressions, as when :meth:`_expression.ColumnElement.match` is + used, but allows to pass multiple columns + + All positional arguments passed to :func:`.match`, should + be :class:`_expression.ColumnElement` subclass. + + :param clauselist: a column iterator, typically a + :class:`_expression.ColumnElement` instances or alternatively a Python + scalar expression to be coerced into a column expression, + serving as the ``MATCH`` side of expression. + + :param against: str. + + .. versionadded:: 1.4.4 + + .. seealso:: + + :meth:`_expression.ColumnElement.match` + + """ + + clause_batch = ClauseElementBatch(*clauselist, group=False) + return clause_batch.match(against, **kwargs) diff --git a/lib/sqlalchemy/dialects/mysql/expression_enum.py b/lib/sqlalchemy/dialects/mysql/expression_enum.py new file mode 100644 index 0000000000..1548dba07a --- /dev/null +++ b/lib/sqlalchemy/dialects/mysql/expression_enum.py @@ -0,0 +1,11 @@ +import enum + + +class MatchExpressionModifier(enum.Enum): + in_natural_language_mode = 'IN NATURAL LANGUAGE MODE' + + in_natural_language_mode_with_query_expansion = \ + 'IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION' + + in_boolean_mode = 'IN BOOLEAN MODE' + with_query_expansion = 'WITH QUERY EXPANSION' diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 1f77d01580..0c6fc51c27 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -177,64 +177,6 @@ def not_(clause): return operators.inv(coercions.expect(roles.ExpressionElementRole, clause)) -def match(*clauselist, against, **kwargs): - """Produce a ``MATCH (X, Y) AGAINST ('TEXT')`` clause. - - E.g.:: - - from sqlalchemy import match - - match_columns_where = match( - users_table.c.firstname, - users_table.c.lastname, - against="John Connor", - ) - - match_columns_order = match( - users_table.c.firstname, - users_table.c.lastname, - against="John Connor", - boolean_mode=False, - ) - - stmt = select(users_table)\ - .where(match_columns_where)\ - .order_by(match_columns_order) - - Would produce SQL resembling:: - - SELECT id, firstname, lastname FROM user - WHERE MATCH(firstname, lastname) - AGAINST (:param_1 IN BOOLEAN MODE) - ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) - - The :func:`.match` function is a standalone version of the - :meth:`_expression.ColumnElement.match` method available on all - SQL expressions, as when :meth:`_expression.ColumnElement.match` is - used, but allows to pass multiple columns - - All positional arguments passed to :func:`.match`, should - be :class:`_expression.ColumnElement` subclass. - - :param clauselist: a column iterator, typically a - :class:`_expression.ColumnElement` instances or alternatively a Python - scalar expression to be coerced into a column expression, - serving as the ``MATCH`` side of expression. - - :param against: str. - - .. versionadded:: 1.4.4 - - .. seealso:: - - :meth:`_expression.ColumnElement.match` - - """ - - clause_batch = ClauseElementBatch(*clauselist, group=False) - return clause_batch.match(against, **kwargs) - - @inspection._self_inspects class ClauseElement( roles.SQLRole, diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 2861da2efc..f47dc5fdf0 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -66,7 +66,6 @@ __all__ = [ "lambda_stmt", "literal", "literal_column", - "match", "not_", "null", "nulls_first", @@ -123,7 +122,6 @@ from .elements import Grouping from .elements import Label from .elements import literal from .elements import literal_column -from .elements import match from .elements import not_ from .elements import Null from .elements import outparam