diff options
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/dialects/mssql/base.py | 6 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 13 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/elements.py | 9 | ||||
| -rw-r--r-- | lib/sqlalchemy/testing/requirements.py | 14 | ||||
| -rw-r--r-- | lib/sqlalchemy/testing/suite/test_select.py | 40 |
5 files changed, 77 insertions, 5 deletions
diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index 9f4e7a9c4..e72ca06b0 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -1713,13 +1713,13 @@ class MSIdentifierPreparer(compiler.IdentifierPreparer): reserved_words = RESERVED_WORDS def __init__(self, dialect): - super(MSIdentifierPreparer, self).__init__(dialect, initial_quote='[', - final_quote=']') + super(MSIdentifierPreparer, self).__init__( + dialect, initial_quote='[', + final_quote=']', quote_case_sensitive_collations=False) def _escape_identifier(self, value): return value - def quote_schema(self, schema, force=None): """Prepare a quoted table and schema name.""" diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index cb058affa..9411329a1 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -733,6 +733,9 @@ class SQLCompiler(Compiled): self.preparer.quote(tablename) + \ "." + name + def visit_collation(self, element, **kw): + return self.preparer.format_collation(element.collation) + def visit_fromclause(self, fromclause, **kwargs): return fromclause.name @@ -2961,7 +2964,8 @@ class IdentifierPreparer(object): schema_for_object = schema._schema_getter(None) def __init__(self, dialect, initial_quote='"', - final_quote=None, escape_quote='"', omit_schema=False): + final_quote=None, escape_quote='"', + quote_case_sensitive_collations=True, omit_schema=False): """Construct a new ``IdentifierPreparer`` object. initial_quote @@ -2982,6 +2986,7 @@ class IdentifierPreparer(object): self.escape_quote = escape_quote self.escape_to_quote = self.escape_quote * 2 self.omit_schema = omit_schema + self.quote_case_sensitive_collations = quote_case_sensitive_collations self._strings = {} self._double_percents = self.dialect.paramstyle in ('format', 'pyformat') @@ -3064,6 +3069,12 @@ class IdentifierPreparer(object): else: return ident + def format_collation(self, collation_name): + if self.quote_case_sensitive_collations: + return self.quote(collation_name) + else: + return collation_name + def format_sequence(self, sequence, use_schema=True): name = self.quote(sequence.name) diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 2cc1d9c42..fd2c9c0bd 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -52,7 +52,7 @@ def collate(expression, collation): expr = _literal_as_binds(expression) return BinaryExpression( expr, - ColumnClause(collation), + CollationClause(collation), operators.collate, type_=expr.type) @@ -3873,6 +3873,13 @@ class ColumnClause(Immutable, ColumnElement): return c +class CollationClause(ColumnElement): + __visit_name__ = "collation" + + def __init__(self, collation): + self.collation = collation + + class _IdentifiedClause(Executable, ClauseElement): __visit_name__ = 'identified' diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py index b89d149d6..cc9e074ef 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -757,6 +757,20 @@ class SuiteRequirements(Requirements): return exclusions.closed() @property + def order_by_collation(self): + def check(config): + try: + self.get_order_by_collation(config) + return False + except NotImplementedError: + return True + + return exclusions.skip_if(check) + + def get_order_by_collation(self, config): + raise NotImplementedError() + + @property def unicode_connections(self): """Target driver must support non-ASCII characters being passed at all. diff --git a/lib/sqlalchemy/testing/suite/test_select.py b/lib/sqlalchemy/testing/suite/test_select.py index df638c140..d9755c8f9 100644 --- a/lib/sqlalchemy/testing/suite/test_select.py +++ b/lib/sqlalchemy/testing/suite/test_select.py @@ -9,6 +9,46 @@ from sqlalchemy import literal_column from ..schema import Table, Column +class CollateTest(fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table("some_table", metadata, + Column('id', Integer, primary_key=True), + Column('data', String(100)) + ) + + @classmethod + def insert_data(cls): + config.db.execute( + cls.tables.some_table.insert(), + [ + {"id": 1, "data": "collate data1"}, + {"id": 2, "data": "collate data2"}, + ] + ) + + def _assert_result(self, select, result): + eq_( + config.db.execute(select).fetchall(), + result + ) + + @testing.requires.order_by_collation + def test_collate_order_by(self): + collation = testing.requires.get_order_by_collation(testing.config) + + self._assert_result( + select([self.tables.some_table]). + order_by(self.tables.some_table.c.data.collate(collation).asc()), + [ + (1, "collate data1"), + (2, "collate data2"), + ] + ) + + class OrderByLabelTest(fixtures.TablesTest): """Test the dialect sends appropriate ORDER BY expressions when labels are used. |
