:tickets: 9752
- Added new :func:`.try_cast` factory function and corresponding
- :class:`.TryCast` SQL Element, which implements a cast where
+ Generalized the MSSQL :func:`.try_cast` function to any dialect.
+ This implements a cast where
un-castable values are returned as NULL, instead of raising an error.
+ Now this function can be taken advantage of by third party dialects
+ that also support this function, such as
- This is currently implemented as ``TRY_CAST`` in Microsoft SQL Server.
- and could be implemented in other backends:
- * ``SAFE_CAST`` in Google BigQuery, and
- * ``TRY_CAST`` in DuckDB.
- * ``TRY_CAST`` in Snowflake.
+ * ``SAFE_CAST`` in Google BigQuery
+ * ``TRY_CAST`` in DuckDB
+ * ``TRY_CAST`` in Snowflake
# the MIT License: https://www.opensource.org/licenses/mit-license.php
# mypy: ignore-errors
-
+from ...sql._elements_constructors import try_cast
from . import base # noqa
from . import pymssql # noqa
from . import pyodbc # noqa
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
from ...sql import roles
from ...sql import sqltypes
from ...sql import util as sql_util
+from ...sql._elements_constructors import try_cast
from ...sql._typing import is_sql_compiler
from ...sql.compiler import InsertmanyvaluesSentinelOpts
from ...sql.elements import TryCast
__visit_name__ = "SQL_VARIANT"
-def try_cast(*arg, **kw):
- util.warn_deprecated(
- "`sqlalchemy.dialects.mssql.base.try_cast` is deprecated. "
- "Use directly from sqlalchemy instead, i.e. `sa.try_cast(...)`",
- "2.1",
- )
- return TryCast(*arg, **kw)
-
-
# old names.
MSDateTime = _MSDateTime
MSDate = _MSDate
else:
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 translate_select_structure(self, select_stmt, **kwargs):
"""Look for ``LIMIT`` and OFFSET in a select statement, and if
so tries to wrap it in a subquery with ``row_number()`` criterion.
cast.typeclause._compiler_dispatch(self, **kwargs),
)
+ def visit_try_cast(self, cast, **kwargs):
+ return "TRY_CAST(%s AS %s)" % (
+ cast.clause._compiler_dispatch(self, **kwargs),
+ cast.typeclause._compiler_dispatch(self, **kwargs),
+ )
+
def _format_frame_clause(self, range_, **kw):
return "%s AND %s" % (
from sqlalchemy import update
from sqlalchemy.dialects import mssql
from sqlalchemy.dialects.mssql import base as mssql_base
-from sqlalchemy.dialects.mssql.base import try_cast
from sqlalchemy.sql import column
from sqlalchemy.sql import quoted_name
from sqlalchemy.sql import table
metadata = MetaData()
t1 = Table("t1", metadata, Column("id", Integer, primary_key=True))
- def call(func):
- self.assert_compile(
- select(func(t1.c.id, Integer)),
- "SELECT TRY_CAST (t1.id AS INTEGER) AS id FROM t1",
- )
-
- with testing.expect_deprecated(".*try_cast.*"):
- call(mssql_base.try_cast)
- call(try_cast)
+ self.assert_compile(
+ select(try_cast(t1.c.id, Integer)),
+ "SELECT TRY_CAST (t1.id AS INTEGER) AS id FROM t1",
+ )
@testing.combinations(
("no_persisted", "", "ignore"),