From: Leonel Atencio Date: Fri, 9 Aug 2019 21:16:13 +0000 (-0400) Subject: Add support for try_cast function on sqlalchemy.dialects.mssql X-Git-Tag: rel_1_4_0b1~762 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e0dfd2ad4141289a992adc65d2a6641f46dc82d;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Add support for try_cast function on sqlalchemy.dialects.mssql Added new :func:`.mssql.try_cast` construct for SQL Server which emits "TRY_CAST" syntax. Pull request courtesy Leonel Atencio. Fixes: #4782 Closes: #4785 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/4785 Pull-request-sha: cf13303a9d1c0cc0233a82a5d2ca01f438b6fb9b Change-Id: I74a71ff5e587353f67472534aabe0d54ae8039ae --- diff --git a/doc/build/changelog/unreleased_13/4782.rst b/doc/build/changelog/unreleased_13/4782.rst new file mode 100644 index 0000000000..bde8704838 --- /dev/null +++ b/doc/build/changelog/unreleased_13/4782.rst @@ -0,0 +1,6 @@ +.. change:: + :tags: usecase, mssql + :tickets: 4782 + + Added new :func:`.mssql.try_cast` construct for SQL Server which emits + "TRY_CAST" syntax. Pull request courtesy Leonel Atencio. diff --git a/doc/build/dialects/mssql.rst b/doc/build/dialects/mssql.rst index 6244412292..0b13fbca52 100644 --- a/doc/build/dialects/mssql.rst +++ b/doc/build/dialects/mssql.rst @@ -5,6 +5,13 @@ Microsoft SQL Server .. automodule:: sqlalchemy.dialects.mssql.base +SQL Server SQL Constructs +------------------------- + +.. currentmodule:: sqlalchemy.dialects.mssql + +.. autofunction:: try_cast + SQL Server Data Types --------------------- diff --git a/lib/sqlalchemy/dialects/mssql/__init__.py b/lib/sqlalchemy/dialects/mssql/__init__.py index 7cd1d34a55..8dd6e4d35a 100644 --- a/lib/sqlalchemy/dialects/mssql/__init__.py +++ b/lib/sqlalchemy/dialects/mssql/__init__.py @@ -38,6 +38,7 @@ from .base import TEXT from .base import TIME from .base import TIMESTAMP from .base import TINYINT +from .base import try_cast from .base import UNIQUEIDENTIFIER from .base import VARBINARY from .base import VARCHAR @@ -80,4 +81,5 @@ __all__ = ( "SQL_VARIANT", "XML", "dialect", + "try_cast", ) diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index 86116e6a17..87b675c5fb 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -691,6 +691,7 @@ from ...types import SMALLINT from ...types import TEXT from ...types import VARCHAR from ...util import update_wrapper +from ...util.langhelpers import public_factory # http://sqlserverbuilds.blogspot.com/ @@ -1173,6 +1174,41 @@ class SQL_VARIANT(sqltypes.TypeEngine): __visit_name__ = "SQL_VARIANT" +class TryCast(sql.elements.Cast): + """Represent a SQL Server TRY_CAST expression. + + """ + + __visit_name__ = "try_cast" + + def __init__(self, *arg, **kw): + """Create a TRY_CAST expression. + + :class:`.TryCast` is a subclass of SQLAlchemy's :class:`.Cast` + construct, and works in the same way, except that the SQL expression + rendered is "TRY_CAST" rather than "CAST":: + + from sqlalchemy import select + from sqlalchemy import Numeric + from sqlalchemy.dialects.mssql import try_cast + + stmt = select([ + try_cast(product_table.c.unit_price, Numeric(10, 4)) + ]) + + The above would render:: + + SELECT TRY_CAST (product_table.unit_price AS NUMERIC(10, 4)) + FROM product_table + + .. versionadded:: 1.3.7 + + """ + super(TryCast, self).__init__(*arg, **kw) + + +try_cast = public_factory(TryCast, ".mssql.try_cast") + # old names. MSDateTime = _MSDateTime MSDate = _MSDate @@ -1586,6 +1622,12 @@ class MSSQLCompiler(compiler.SQLCompiler): # Limit in mssql is after the select keyword return "" + def visit_try_cast(self, element, **kw): + return "TRY_CAST (%s AS %s)" % ( + self.process(element.clause, **kw), + self.process(element.typeclause, **kw), + ) + def visit_select(self, select, **kwargs): """Look for ``LIMIT`` and OFFSET in a select statement, and if so tries to wrap it in a subquery with ``row_number()`` criterion. diff --git a/test/dialect/mssql/test_compiler.py b/test/dialect/mssql/test_compiler.py index 498de763c6..642cee4cd3 100644 --- a/test/dialect/mssql/test_compiler.py +++ b/test/dialect/mssql/test_compiler.py @@ -22,6 +22,7 @@ from sqlalchemy import update from sqlalchemy.dialects import mssql from sqlalchemy.dialects.mssql import base from sqlalchemy.dialects.mssql import mxodbc +from sqlalchemy.dialects.mssql.base import try_cast from sqlalchemy.sql import column from sqlalchemy.sql import quoted_name from sqlalchemy.sql import table @@ -1196,6 +1197,15 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): schema.CreateIndex(idx), "CREATE INDEX foo ON test (x) INCLUDE (y)" ) + def test_try_cast(self): + metadata = MetaData() + t1 = Table("t1", metadata, Column("id", Integer, primary_key=True)) + + self.assert_compile( + select([try_cast(t1.c.id, Integer)]), + "SELECT TRY_CAST (t1.id AS INTEGER) AS anon_1 FROM t1", + ) + class SchemaTest(fixtures.TestBase): def setup(self):