From: Federico Caselli Date: Wed, 19 Jan 2022 22:31:13 +0000 (+0100) Subject: Added support for ``FILESTREAM`` in MSSQL. X-Git-Tag: rel_2_0_0b1~524^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=64fd7a3968448f21ce1c14bff89fc78e949e04d1;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Added support for ``FILESTREAM`` in MSSQL. Fixes: #7243 Change-Id: I99880f429dbaac525bdf7d44438aaab6bc8d0ca6 --- diff --git a/doc/build/changelog/unreleased_14/7243.rst b/doc/build/changelog/unreleased_14/7243.rst new file mode 100644 index 0000000000..b4661c2942 --- /dev/null +++ b/doc/build/changelog/unreleased_14/7243.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: mssql + :tickets: 7243 + + Added support for ``FILESTREAM`` when using ``VARBINARY(max)`` + in MSSQL. + + .. seealso:: + + :paramref:`_mssql.VARBINARY.filestream` diff --git a/doc/build/dialects/mssql.rst b/doc/build/dialects/mssql.rst index f372ed6cb3..9de56338ff 100644 --- a/doc/build/dialects/mssql.rst +++ b/doc/build/dialects/mssql.rst @@ -108,6 +108,9 @@ construction arguments, are as follows: :members: __init__ +.. autoclass:: VARBINARY + :members: __init__ + .. autoclass:: VARCHAR :members: __init__ diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index d48cca2a88..2aff7d8b6d 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -1283,9 +1283,10 @@ class NTEXT(sqltypes.UnicodeText): class VARBINARY(sqltypes.VARBINARY, sqltypes.LargeBinary): """The MSSQL VARBINARY type. - This type is present to support "deprecate_large_types" mode where - either ``VARBINARY(max)`` or IMAGE is rendered. Otherwise, this type - object is redundant vs. :class:`_types.VARBINARY`. + This type adds additional features to the core :class:`_types.VARBINARY` + type, including "deprecate_large_types" mode where + either ``VARBINARY(max)`` or IMAGE is rendered, as well as the SQL + Server ``FILESTREAM`` option. .. versionadded:: 1.0.0 @@ -1293,12 +1294,33 @@ class VARBINARY(sqltypes.VARBINARY, sqltypes.LargeBinary): :ref:`mssql_large_type_deprecation` - - """ __visit_name__ = "VARBINARY" + def __init__(self, length=None, filestream=False): + """ + Construct a VARBINARY type. + + :param length: optional, a length for the column for use in + DDL statements, for those binary types that accept a length, + such as the MySQL BLOB type. + + :param filestream=False: if True, renders the ``FILESTREAM`` keyword + in the table definition. In this case ``length`` must be ``None`` + or ``'max'``. + + .. versionadded:: 1.4.31 + + """ + + self.filestream = filestream + if self.filestream and length not in (None, "max"): + raise ValueError( + "length must be None or 'max' when setting filestream" + ) + super(VARBINARY, self).__init__(length=length) + class IMAGE(sqltypes.LargeBinary): __visit_name__ = "IMAGE" @@ -1566,7 +1588,10 @@ class MSTypeCompiler(compiler.GenericTypeCompiler): return "XML" def visit_VARBINARY(self, type_, **kw): - return self._extend("VARBINARY", type_, length=type_.length or "max") + text = self._extend("VARBINARY", type_, length=type_.length or "max") + if getattr(type_, "filestream", False): + text += " FILESTREAM" + return text def visit_boolean(self, type_, **kw): return self.visit_BIT(type_) diff --git a/test/dialect/mssql/test_types.py b/test/dialect/mssql/test_types.py index 63c6cf26d7..2ef8b76dae 100644 --- a/test/dialect/mssql/test_types.py +++ b/test/dialect/mssql/test_types.py @@ -48,6 +48,7 @@ from sqlalchemy.testing import AssertsExecutionResults from sqlalchemy.testing import ComparesTables from sqlalchemy.testing import engines from sqlalchemy.testing import eq_ +from sqlalchemy.testing import expect_raises_message from sqlalchemy.testing import fixtures from sqlalchemy.testing import is_ from sqlalchemy.testing import is_not @@ -529,6 +530,12 @@ class TypeDDLTest(fixtures.TestBase): (mssql.MSVarBinary, [10], {}, "VARBINARY(10)"), (types.VARBINARY, [10], {}, "VARBINARY(10)"), (types.VARBINARY, [], {}, "VARBINARY(max)"), + ( + mssql.MSVarBinary, + [], + {"filestream": True}, + "VARBINARY(max) FILESTREAM", + ), (mssql.MSImage, [], {}, "IMAGE"), (mssql.IMAGE, [], {}, "IMAGE"), (types.LargeBinary, [], {}, "IMAGE"), @@ -552,6 +559,17 @@ class TypeDDLTest(fixtures.TestBase): ) self.assert_(repr(col)) + def test_VARBINARY_init(self): + d = mssql.dialect() + t = mssql.MSVarBinary(length=None, filestream=True) + eq_(str(t.compile(dialect=d)), "VARBINARY(max) FILESTREAM") + t = mssql.MSVarBinary(length="max", filestream=True) + eq_(str(t.compile(dialect=d)), "VARBINARY(max) FILESTREAM") + with expect_raises_message( + ValueError, "length must be None or 'max' when setting filestream" + ): + mssql.MSVarBinary(length=1000, filestream=True) + class TypeRoundTripTest( fixtures.TestBase, AssertsExecutionResults, ComparesTables @@ -1007,6 +1025,15 @@ class TypeRoundTripTest( ), ] + if testing.requires.mssql_filestream.enabled: + columns.append( + ( + mssql.MSVarBinary, + [], + {"filestream": True}, + "VARBINARY(max) FILESTREAM", + ) + ) engine = engines.testing_engine( options={"deprecate_large_types": deprecate_large_types} ) @@ -1233,6 +1260,15 @@ class BinaryTest(fixtures.TestBase): None, False, ), + ( + mssql.VARBINARY(filestream=True), + "binary_data_one.dat", + None, + True, + None, + False, + testing.requires.mssql_filestream, + ), ( sqltypes.LargeBinary, "binary_data_one.dat", diff --git a/test/requirements.py b/test/requirements.py index b42bab7d35..b93cc4cb4c 100644 --- a/test/requirements.py +++ b/test/requirements.py @@ -1703,3 +1703,17 @@ class DefaultRequirements(SuiteRequirements): def json_deserializer_binary(self): "indicates if the json_deserializer function is called with bytes" return only_on(["postgresql+psycopg"]) + + @property + def mssql_filestream(self): + "returns if mssql supports filestream" + + def check(config): + with config.db.connect() as conn: + res = conn.exec_driver_sql( + "SELECT [type] FROM sys.master_files WHERE " + "database_id = DB_ID() AND [type] = 2" + ).scalar() + return res is not None + + return only_on(["mssql"]) + only_if(check)