From 8b888a2371a53185de7962f3366c85e57a858d51 Mon Sep 17 00:00:00 2001 From: Yasin Tatar Date: Wed, 9 Nov 2022 10:06:19 +0100 Subject: [PATCH 1/3] add: render_string_type (thanks @Stamper) --- src/snowflake/sqlalchemy/base.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/snowflake/sqlalchemy/base.py b/src/snowflake/sqlalchemy/base.py index e9125315..d4edc73e 100644 --- a/src/snowflake/sqlalchemy/base.py +++ b/src/snowflake/sqlalchemy/base.py @@ -583,6 +583,15 @@ def visit_identity_column(self, identity, **kw): class SnowflakeTypeCompiler(compiler.GenericTypeCompiler): + def _render_string_type(self, type_, name): + + text = name + if type_.length: + text += f"({type_.length})" + if type_.collation: + text += f"COLLATE '{type_.collation}'" + return text + def visit_BYTEINT(self, type_, **kw): return "BYTEINT" From 9bee1f8ea0cf04db95cdf6aea35ef905e313b48d Mon Sep 17 00:00:00 2001 From: Yasin Tatar Date: Wed, 9 Nov 2022 11:08:35 +0100 Subject: [PATCH 2/3] add: init test structure for collation test (execute on ci) --- tests/test_compiler.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/test_compiler.py b/tests/test_compiler.py index 43820623..0aa21527 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -2,7 +2,8 @@ # Copyright (c) 2012-2022 Snowflake Computing Inc. All rights reserved. # -from sqlalchemy import Integer, String, and_, select +import pytest +from sqlalchemy import Integer, String, and_, select, Table, Column from sqlalchemy.schema import DropColumnComment, DropTableComment from sqlalchemy.sql import column, quoted_name, table from sqlalchemy.testing import AssertsCompiledSQL @@ -99,3 +100,23 @@ def test_quoted_name_label(engine_testaccount): sel_from_tbl = select(col).group_by(col).select_from(table("abc")) compiled_result = sel_from_tbl.compile() assert str(compiled_result) == t["output"] + +@pytest.mark.parameterize("collation_specification", [ + "en", + ]) +def test_string_collation(engine_testaccount, collation_specification): + # create a table with a string column with a certain collation + table = Table("collation_test_table", + Column("chars_col", String(collcation=collation_specification)) + ) + table.create(engine_testaccount) + insert_stmt = table.insert([ + {"chars_col": "a"}, + {"chars_col": "A"}, + {"chars_col": "b"}, + ]) + engine_testaccount.execute(insert_stmt) + # retrieve values and check if collation was used properly + results = engine_testaccount.execute(f"SELECT chars_col FROM {table.schema}.{table.name} ORDER BY chars_col").fetchall() + assert results == [('a',), ('A',), ('b',)] + From 1ba9c10780e1011dbed187382e9be2a12c559dfe Mon Sep 17 00:00:00 2001 From: Yasin Tatar Date: Wed, 9 Nov 2022 14:10:41 +0100 Subject: [PATCH 3/3] add+fix: test for validating if collation information is passed --- src/snowflake/sqlalchemy/base.py | 3 ++- tests/test_compiler.py | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/snowflake/sqlalchemy/base.py b/src/snowflake/sqlalchemy/base.py index d4edc73e..3663c524 100644 --- a/src/snowflake/sqlalchemy/base.py +++ b/src/snowflake/sqlalchemy/base.py @@ -589,7 +589,8 @@ def _render_string_type(self, type_, name): if type_.length: text += f"({type_.length})" if type_.collation: - text += f"COLLATE '{type_.collation}'" + # note: whitespace before the statement is important here + text += f" COLLATE '{type_.collation}'" return text def visit_BYTEINT(self, type_, **kw): diff --git a/tests/test_compiler.py b/tests/test_compiler.py index 0aa21527..b389a8f6 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -3,10 +3,11 @@ # import pytest -from sqlalchemy import Integer, String, and_, select, Table, Column +from sqlalchemy import Integer, MetaData, String, and_, select, Table, Column from sqlalchemy.schema import DropColumnComment, DropTableComment from sqlalchemy.sql import column, quoted_name, table from sqlalchemy.testing import AssertsCompiledSQL +from .conftest import CONNECTION_PARAMETERS table1 = table( "table1", column("id", Integer), column("name", String), column("value", Integer) @@ -101,22 +102,23 @@ def test_quoted_name_label(engine_testaccount): compiled_result = sel_from_tbl.compile() assert str(compiled_result) == t["output"] -@pytest.mark.parameterize("collation_specification", [ - "en", - ]) -def test_string_collation(engine_testaccount, collation_specification): +@pytest.mark.parametrize("collation", ["en", "latin1"]) +def test_string_collation(engine_testaccount, collation): # create a table with a string column with a certain collation - table = Table("collation_test_table", - Column("chars_col", String(collcation=collation_specification)) + metadata = MetaData(bind=engine_testaccount) + table = Table(f"collation_test_table_{collation}", + metadata, + Column("chars_col", String(collation=collation)), + schema=CONNECTION_PARAMETERS["schema"] ) table.create(engine_testaccount) insert_stmt = table.insert([ {"chars_col": "a"}, {"chars_col": "A"}, {"chars_col": "b"}, - ]) + ]) engine_testaccount.execute(insert_stmt) # retrieve values and check if collation was used properly - results = engine_testaccount.execute(f"SELECT chars_col FROM {table.schema}.{table.name} ORDER BY chars_col").fetchall() - assert results == [('a',), ('A',), ('b',)] + column_type = engine_testaccount.execute(f"DESCRIBE TABLE {table.schema}.{table.name}").fetchone()["type"] + assert f"COLLATE '{collation}'" in column_type