diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2018-01-12 15:50:28 -0500 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@ci.zzzcomputing.com> | 2018-01-12 15:50:28 -0500 |
| commit | e72cf013cd11cc77de5518c1fe66c9b8b42c3548 (patch) | |
| tree | 7d45a959a8b5cbabfb09871a1c41cfb773bbfcfe /lib/sqlalchemy | |
| parent | 699272e4dcb9aa71ebbc0d9487fb6de82d3abc2b (diff) | |
| parent | 7402987fd218c42ed2a909a5031186d2b702bb88 (diff) | |
| download | sqlalchemy-e72cf013cd11cc77de5518c1fe66c9b8b42c3548.tar.gz | |
Merge "Make column-level collation quoting dialect-specific"
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 b8820c2c5..840c355dc 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 7ecbd2907..560585cab 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 d5d079348..22636a05b 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 f2c03a8b5..1990eb9d5 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -764,6 +764,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. |
