]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add support for try_cast function on sqlalchemy.dialects.mssql
authorLeonel Atencio <leo.atencio@gmail.com>
Fri, 9 Aug 2019 21:16:13 +0000 (17:16 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 13 Aug 2019 01:22:04 +0000 (21:22 -0400)
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

doc/build/changelog/unreleased_13/4782.rst [new file with mode: 0644]
doc/build/dialects/mssql.rst
lib/sqlalchemy/dialects/mssql/__init__.py
lib/sqlalchemy/dialects/mssql/base.py
test/dialect/mssql/test_compiler.py

diff --git a/doc/build/changelog/unreleased_13/4782.rst b/doc/build/changelog/unreleased_13/4782.rst
new file mode 100644 (file)
index 0000000..bde8704
--- /dev/null
@@ -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.
index 624441229277780b6f99338d8921c09cdd52449f..0b13fbca5201d9676ac3f3e4031ba0cb60868c3a 100644 (file)
@@ -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
 ---------------------
 
index 7cd1d34a558b04a8b90b874f38b3e2778744902a..8dd6e4d35a365aebf898b25c1641b248d2deeefd 100644 (file)
@@ -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",
 )
index 86116e6a175f5d68e2f89d89b05f93c175bc913a..87b675c5fb613a3d6ca159b40c77efb36ccd7252 100644 (file)
@@ -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.
index 498de763c6d9ed857963b15c090caf52eace1552..642cee4cd35e1e7b047462cf4d0df54155f92813 100644 (file)
@@ -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):