diff -pruN 0.41.0-5/.pre-commit-config.yaml 0.41.2-2/.pre-commit-config.yaml
--- 0.41.0-5/.pre-commit-config.yaml	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/.pre-commit-config.yaml	2024-03-22 15:16:53.000000000 +0000
@@ -2,7 +2,7 @@
 
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.3.0
+    rev: v4.5.0
     hooks:
       - id: check-yaml
       - id: end-of-file-fixer
@@ -14,7 +14,7 @@ repos:
       - id: flake8
 
   - repo: https://github.com/pycqa/isort
-    rev: 5.11.5
+    rev: 5.13.2
     hooks:
       - id: isort
 
@@ -25,7 +25,7 @@ repos:
         stages: [manual]
 
   - repo: https://github.com/asottile/pyupgrade
-    rev: v3.1.0
+    rev: v3.15.1
     hooks:
       - id: pyupgrade
         args: [--py36-plus]
diff -pruN 0.41.0-5/CHANGES.rst 0.41.2-2/CHANGES.rst
--- 0.41.0-5/CHANGES.rst	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/CHANGES.rst	2024-03-22 15:16:53.000000000 +0000
@@ -3,6 +3,18 @@ Changelog
 
 Here you can see the full list of changes between each SQLAlchemy-Utils release.
 
+0.41.2 (2024-03-22)
+^^^^^^^^^^^^^^^^^^^
+
+- Fix breaking change introduced on SQLAlchemy 2.0.22 changes to `attributes.AttributeImpl` constructor (#733)
+
+
+0.41.1 (2023-04-27)
+^^^^^^^^^^^^^^^^^^^
+
+- Use a custom SQL construct for refreshing materialized views in
+  `refresh_materialized_view` (#703)
+
 
 0.41.0 (2023-04-13)
 ^^^^^^^^^^^^^^^^^^^
diff -pruN 0.41.0-5/README.rst 0.41.2-2/README.rst
--- 0.41.0-5/README.rst	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/README.rst	2024-03-22 15:16:53.000000000 +0000
@@ -1,8 +1,7 @@
 SQLAlchemy-Utils
 ================
 
-|Build Status| |Version Status| |Downloads|
-
+|Build| |Version Status| |Downloads|
 
 Various utility functions, new data types and helpers for SQLAlchemy.
 
@@ -14,8 +13,9 @@ Resources
 - `Issue Tracker <https://github.com/kvesteri/sqlalchemy-utils/issues>`_
 - `Code <https://github.com/kvesteri/sqlalchemy-utils/>`_
 
-.. |Build Status| image:: https://travis-ci.org/kvesteri/sqlalchemy-utils.svg?branch=master
-   :target: https://travis-ci.org/kvesteri/sqlalchemy-utils
+.. |Build| image:: https://github.com/kvesteri/sqlalchemy-utils/actions/workflows/test.yaml/badge.svg?branch=master
+   :target: https://github.com/kvesteri/sqlalchemy-utils/actions/workflows/test.yaml
+   :alt: Tests
 .. |Version Status| image:: https://img.shields.io/pypi/v/SQLAlchemy-Utils.svg
    :target: https://pypi.python.org/pypi/SQLAlchemy-Utils/
 .. |Downloads| image:: https://img.shields.io/pypi/dm/SQLAlchemy-Utils.svg
diff -pruN 0.41.0-5/debian/changelog 0.41.2-2/debian/changelog
--- 0.41.0-5/debian/changelog	2024-12-16 15:07:27.000000000 +0000
+++ 0.41.2-2/debian/changelog	2025-09-28 13:22:15.000000000 +0000
@@ -1,3 +1,15 @@
+python-sqlalchemy-utils (0.41.2-2) unstable; urgency=medium
+
+  * Uploading to unstable.
+
+ -- Thomas Goirand <zigo@debian.org>  Sun, 28 Sep 2025 15:22:15 +0200
+
+python-sqlalchemy-utils (0.41.2-1) experimental; urgency=medium
+
+  * New upstream release.
+
+ -- Thomas Goirand <zigo@debian.org>  Wed, 27 Aug 2025 12:18:58 +0200
+
 python-sqlalchemy-utils (0.41.0-5) unstable; urgency=medium
 
   * Add remove-intersphinx.patch (Closes: #1090161).
diff -pruN 0.41.0-5/docs/data_types.rst 0.41.2-2/docs/data_types.rst
--- 0.41.0-5/docs/data_types.rst	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/docs/data_types.rst	2024-03-22 15:16:53.000000000 +0000
@@ -76,6 +76,9 @@ EncryptedType
 
 .. module:: sqlalchemy_utils.types.encrypted.encrypted_type
 
+.. deprecated:: 0.36.6
+    Use StringEncryptedType instead
+
 .. autoclass:: EncryptedType
 
 JSONType
@@ -142,6 +145,14 @@ ScalarListType
 .. autoclass:: ScalarListType
 
 
+StringEncryptedType
+-------------------
+
+.. module:: sqlalchemy_utils.types.encrypted.encrypted_type
+
+.. autoclass:: StringEncryptedType
+
+
 TimezoneType
 ------------
 
diff -pruN 0.41.0-5/docs/installation.rst 0.41.2-2/docs/installation.rst
--- 0.41.0-5/docs/installation.rst	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/docs/installation.rst	2024-03-22 15:16:53.000000000 +0000
@@ -8,7 +8,6 @@ Supported platforms
 
 SQLAlchemy-Utils is currently tested against the following Python platforms.
 
-- cPython 3.6
 - cPython 3.7
 - cPython 3.8
 - cPython 3.9
diff -pruN 0.41.0-5/setup.py 0.41.2-2/setup.py
--- 0.41.0-5/setup.py	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/setup.py	2024-03-22 15:16:53.000000000 +0000
@@ -22,7 +22,7 @@ def get_version():
 
 extras_require = {
     'test': [
-        'pytest>=2.7.1',
+        'pytest==7.4.4',
         'Pygments>=1.2',
         'Jinja2>=2.3',
         'docutils>=0.10',
@@ -79,7 +79,7 @@ setup(
         "importlib_metadata ; python_version<'3.8'",
     ],
     extras_require=extras_require,
-    python_requires='>=3.6',
+    python_requires='>=3.7',
     classifiers=[
         'Environment :: Web Environment',
         'Intended Audience :: Developers',
@@ -87,7 +87,6 @@ setup(
         'Operating System :: OS Independent',
         'Programming Language :: Python',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
diff -pruN 0.41.0-5/sqlalchemy_utils/__init__.py 0.41.2-2/sqlalchemy_utils/__init__.py
--- 0.41.0-5/sqlalchemy_utils/__init__.py	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/sqlalchemy_utils/__init__.py	2024-03-22 15:16:53.000000000 +0000
@@ -101,4 +101,4 @@ from .view import (  # noqa
     refresh_materialized_view
 )
 
-__version__ = '0.41.0'
+__version__ = '0.41.2'
diff -pruN 0.41.0-5/sqlalchemy_utils/functions/database.py 0.41.2-2/sqlalchemy_utils/functions/database.py
--- 0.41.0-5/sqlalchemy_utils/functions/database.py	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/sqlalchemy_utils/functions/database.py	2024-03-22 15:16:53.000000000 +0000
@@ -13,7 +13,7 @@ from .orm import quote
 
 def escape_like(string, escape_char='*'):
     """
-    Escape the string paremeter used in SQL LIKE expressions.
+    Escape the string parameter used in SQL LIKE expressions.
 
     ::
 
diff -pruN 0.41.0-5/sqlalchemy_utils/generic.py 0.41.2-2/sqlalchemy_utils/generic.py
--- 0.41.0-5/sqlalchemy_utils/generic.py	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/sqlalchemy_utils/generic.py	2024-03-22 15:16:53.000000000 +0000
@@ -13,6 +13,24 @@ from .functions.orm import _get_class_re
 
 
 class GenericAttributeImpl(attributes.ScalarAttributeImpl):
+    def __init__(self, *args, **kwargs):
+        """
+        The constructor of attributes.AttributeImpl changed in SQLAlchemy 2.0.22,
+        adding a 'default_function' required positional argument before 'dispatch'.
+        This adjustment ensures compatibility across versions by inserting None for
+        'default_function' in versions >= 2.0.22.
+
+        Arguments received: (class, key, dispatch)
+        Required by AttributeImpl: (class, key, default_function, dispatch)
+        Setting None as default_function here.
+        """
+        # Adjust for SQLAlchemy version change
+        sqlalchemy_version = tuple(map(int, sa.__version__.split('.')))
+        if sqlalchemy_version >= (2, 0, 22):
+            args = (*args[:2], None, *args[2:])
+
+        super().__init__(*args, **kwargs)
+
     def get(self, state, dict_, passive=attributes.PASSIVE_OFF):
         if self.key in dict_:
             return dict_[self.key]
diff -pruN 0.41.0-5/sqlalchemy_utils/types/encrypted/encrypted_type.py 0.41.2-2/sqlalchemy_utils/types/encrypted/encrypted_type.py
--- 0.41.0-5/sqlalchemy_utils/types/encrypted/encrypted_type.py	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/sqlalchemy_utils/types/encrypted/encrypted_type.py	2024-03-22 15:16:53.000000000 +0000
@@ -223,15 +223,15 @@ class FernetEngine(EncryptionDecryptionB
 
 class StringEncryptedType(TypeDecorator, ScalarCoercible):
     """
-    EncryptedType provides a way to encrypt and decrypt values,
+    StringEncryptedType provides a way to encrypt and decrypt values,
     to and from databases, that their type is a basic SQLAlchemy type.
     For example Unicode, String or even Boolean.
     On the way in, the value is encrypted and on the way out the stored value
     is decrypted.
 
-    EncryptedType needs Cryptography_ library in order to work.
+    StringEncryptedType needs Cryptography_ library in order to work.
 
-    When declaring a column which will be of type EncryptedType
+    When declaring a column which will be of type StringEncryptedType
     it is better to be as precise as possible and follow the pattern
     below.
 
@@ -240,11 +240,11 @@ class StringEncryptedType(TypeDecorator,
     ::
 
 
-        a_column = sa.Column(EncryptedType(sa.Unicode,
+        a_column = sa.Column(StringEncryptedType(sa.Unicode,
                                            secret_key,
                                            FernetEngine))
 
-        another_column = sa.Column(EncryptedType(sa.Unicode,
+        another_column = sa.Column(StringEncryptedType(sa.Unicode,
                                            secret_key,
                                            AesEngine,
                                            'pkcs5'))
@@ -264,7 +264,7 @@ class StringEncryptedType(TypeDecorator,
             from sqlalchemy.ext.declarative import declarative_base
         from sqlalchemy.orm import sessionmaker
 
-        from sqlalchemy_utils import EncryptedType
+        from sqlalchemy_utils import StringEncryptedType
         from sqlalchemy_utils.types.encrypted.encrypted_type import AesEngine
 
         secret_key = 'secretkey1234'
@@ -277,19 +277,19 @@ class StringEncryptedType(TypeDecorator,
         class User(Base):
             __tablename__ = "user"
             id = sa.Column(sa.Integer, primary_key=True)
-            username = sa.Column(EncryptedType(sa.Unicode,
+            username = sa.Column(StringEncryptedType(sa.Unicode,
                                                secret_key,
                                                AesEngine,
                                                'pkcs5'))
-            access_token = sa.Column(EncryptedType(sa.String,
+            access_token = sa.Column(StringEncryptedType(sa.String,
                                                    secret_key,
                                                    AesEngine,
                                                    'pkcs5'))
-            is_active = sa.Column(EncryptedType(sa.Boolean,
+            is_active = sa.Column(StringEncryptedType(sa.Boolean,
                                                 secret_key,
                                                 AesEngine,
                                                 'zeroes'))
-            number_of_accounts = sa.Column(EncryptedType(sa.Integer,
+            number_of_accounts = sa.Column(StringEncryptedType(sa.Integer,
                                                          secret_key,
                                                          AesEngine,
                                                          'oneandzeroes'))
@@ -345,7 +345,7 @@ class StringEncryptedType(TypeDecorator,
         class User(Base):
             __tablename__ = 'user'
             id = sa.Column(sa.Integer, primary_key=True)
-            username = sa.Column(EncryptedType(
+            username = sa.Column(StringEncryptedType(
                 sa.Unicode, get_key))
 
     """
@@ -363,7 +363,7 @@ class StringEncryptedType(TypeDecorator,
         """Initialization."""
         if not cryptography:
             raise ImproperlyConfigured(
-                "'cryptography' is required to use EncryptedType"
+                "'cryptography' is required to use StringEncryptedType"
             )
         super().__init__(**kwargs)
         # set the underlying type
@@ -457,6 +457,11 @@ class StringEncryptedType(TypeDecorator,
 
 
 class EncryptedType(StringEncryptedType):
+    """
+    The 'EncryptedType' class will change implementation from
+    'LargeBinary' to 'String' in a future version. Use
+    'StringEncryptedType' to use the 'String' implementation.
+    """
     impl = LargeBinary
 
     def __init__(self, *args, **kwargs):
diff -pruN 0.41.0-5/sqlalchemy_utils/view.py 0.41.2-2/sqlalchemy_utils/view.py
--- 0.41.0-5/sqlalchemy_utils/view.py	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/sqlalchemy_utils/view.py	2024-03-22 15:16:53.000000000 +0000
@@ -1,20 +1,25 @@
 import sqlalchemy as sa
 from sqlalchemy.ext import compiler
 from sqlalchemy.schema import DDLElement, PrimaryKeyConstraint
+from sqlalchemy.sql.expression import ClauseElement, Executable
 
 from sqlalchemy_utils.functions import get_columns
 
 
 class CreateView(DDLElement):
-    def __init__(self, name, selectable, materialized=False):
+    def __init__(self, name, selectable, materialized=False, replace=False):
+        if materialized and replace:
+            raise ValueError("Cannot use CREATE OR REPLACE with materialized views")
         self.name = name
         self.selectable = selectable
         self.materialized = materialized
+        self.replace = replace
 
 
 @compiler.compiles(CreateView)
 def compile_create_materialized_view(element, compiler, **kw):
-    return 'CREATE {}VIEW {} AS {}'.format(
+    return 'CREATE {}{}VIEW {} AS {}'.format(
+        'OR REPLACE ' if element.replace else '',
         'MATERIALIZED ' if element.materialized else '',
         compiler.dialect.identifier_preparer.quote(element.name),
         compiler.sql_compiler.process(element.selectable, literal_binds=True),
@@ -123,7 +128,8 @@ def create_view(
     name,
     selectable,
     metadata,
-    cascade_on_drop=True
+    cascade_on_drop=True,
+    replace=False,
 ):
     """ Create a view on a given metadata
 
@@ -132,6 +138,10 @@ def create_view(
     :param metadata:
         An SQLAlchemy Metadata instance that stores the features of the
         database being described.
+    :param cascade_on_drop: If ``True`` the view will be dropped with
+        ``CASCADE``, deleting all dependent objects as well.
+    :param replace: If ``True`` the view will be created with ``OR REPLACE``,
+        replacing an existing view with the same name.
 
     The process for creating a view is similar to the standard way that a
     table is constructed, except that a selectable is provided instead of
@@ -163,7 +173,11 @@ def create_view(
         metadata=None
     )
 
-    sa.event.listen(metadata, 'after_create', CreateView(name, selectable))
+    sa.event.listen(
+        metadata,
+        'after_create',
+        CreateView(name, selectable, replace=replace),
+    )
 
     @sa.event.listens_for(metadata, 'after_create')
     def create_indexes(target, connection, **kw):
@@ -178,6 +192,22 @@ def create_view(
     return table
 
 
+class RefreshMaterializedView(Executable, ClauseElement):
+    inherit_cache = True
+
+    def __init__(self, name, concurrently):
+        self.name = name
+        self.concurrently = concurrently
+
+
+@compiler.compiles(RefreshMaterializedView)
+def compile_refresh_materialized_view(element, compiler):
+    return 'REFRESH MATERIALIZED VIEW {concurrently}{name}'.format(
+        concurrently='CONCURRENTLY ' if element.concurrently else '',
+        name=compiler.dialect.identifier_preparer.quote(element.name),
+    )
+
+
 def refresh_materialized_view(session, name, concurrently=False):
     """ Refreshes an already existing materialized view
 
@@ -190,9 +220,4 @@ def refresh_materialized_view(session, n
     # Since session.execute() bypasses autoflush, we must manually flush in
     # order to include newly-created/modified objects in the refresh.
     session.flush()
-    session.execute(
-        sa.text('REFRESH MATERIALIZED VIEW {}{}'.format(
-            'CONCURRENTLY ' if concurrently else '',
-            session.bind.engine.dialect.identifier_preparer.quote(name)
-        ))
-    )
+    session.execute(RefreshMaterializedView(name, concurrently))
diff -pruN 0.41.0-5/tests/aggregate/test_m2m.py 0.41.2-2/tests/aggregate/test_m2m.py
--- 0.41.0-5/tests/aggregate/test_m2m.py	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/tests/aggregate/test_m2m.py	2024-03-22 15:16:53.000000000 +0000
@@ -44,6 +44,7 @@ def init_models(User, Group):
     pass
 
 
+@pytest.mark.skip
 @pytest.mark.usefixtures('postgresql_dsn')
 class TestAggregatesWithManyToManyRelationships:
 
diff -pruN 0.41.0-5/tests/test_views.py 0.41.2-2/tests/test_views.py
--- 0.41.0-5/tests/test_views.py	2023-04-13 10:36:30.000000000 +0000
+++ 0.41.2-2/tests/test_views.py	2024-03-22 15:16:53.000000000 +0000
@@ -8,6 +8,7 @@ from sqlalchemy_utils import (
     refresh_materialized_view
 )
 from sqlalchemy_utils.compat import _select_args
+from sqlalchemy_utils.view import CreateView
 
 
 @pytest.fixture
@@ -15,7 +16,7 @@ def Article(Base, User):
     class Article(Base):
         __tablename__ = 'article'
         id = sa.Column(sa.Integer, primary_key=True)
-        name = sa.Column(sa.String)
+        name = sa.Column(sa.String(128))
         author_id = sa.Column(sa.Integer, sa.ForeignKey(User.id))
         author = sa.orm.relationship(User)
     return Article
@@ -26,7 +27,7 @@ def User(Base):
     class User(Base):
         __tablename__ = 'user'
         id = sa.Column(sa.Integer, primary_key=True)
-        name = sa.Column(sa.String)
+        name = sa.Column(sa.String(128))
     return User
 
 
@@ -121,16 +122,18 @@ class TrivialViewTestCases:
         engine,
         metadata,
         column,
-        cascade_on_drop
+        cascade_on_drop,
+        replace=False,
     ):
-        __table__ = create_view(
+        create_view(
             name='trivial_view',
             selectable=sa.select(*_select_args(column)),
             metadata=metadata,
-            cascade_on_drop=cascade_on_drop
+            cascade_on_drop=cascade_on_drop,
+            replace=replace,
         )
-        __table__.create(engine)
-        __table__.drop(engine)
+        metadata.create_all(engine)
+        metadata.drop_all(engine)
 
 
 class SupportsCascade(TrivialViewTestCases):
@@ -164,13 +167,67 @@ class SupportsNoCascade(TrivialViewTestC
         self.life_cycle(engine, Base.metadata, User.id, cascade_on_drop=False)
 
 
+class SupportsReplace(TrivialViewTestCases):
+    def test_life_cycle_replace(
+        self,
+        connection,
+        engine,
+        Base,
+        User
+    ):
+        self.life_cycle(
+            engine,
+            Base.metadata,
+            User.id,
+            cascade_on_drop=False,
+            replace=True,
+        )
+
+    def test_life_cycle_replace_existing(
+        self,
+        connection,
+        engine,
+        Base,
+        User
+    ):
+        create_view(
+            name='trivial_view',
+            selectable=sa.select(*_select_args(User.id)),
+            metadata=Base.metadata,
+        )
+        Base.metadata.create_all(engine)
+        view = CreateView(
+            name='trivial_view',
+            selectable=sa.select(*_select_args(User.id)),
+            replace=True,
+        )
+        with connection.begin():
+            connection.execute(view)
+        Base.metadata.drop_all(engine)
+
+    def test_replace_materialized(
+        self,
+        connection,
+        engine,
+        Base,
+        User
+    ):
+        with pytest.raises(ValueError):
+            CreateView(
+                name='trivial_view',
+                selectable=sa.select(*_select_args(User.id)),
+                materialized=True,
+                replace=True,
+            )
+
+
 @pytest.mark.usefixtures('postgresql_dsn')
-class TestPostgresTrivialView(SupportsCascade, SupportsNoCascade):
+class TestPostgresTrivialView(SupportsCascade, SupportsNoCascade, SupportsReplace):
     pass
 
 
 @pytest.mark.usefixtures('mysql_dsn')
-class TestMySqlTrivialView(SupportsCascade, SupportsNoCascade):
+class TestMySqlTrivialView(SupportsCascade, SupportsNoCascade, SupportsReplace):
     pass
 
 
