Repository: agronholm/sqlacodegen

Issue:
AssertionError: TypeDecorator implementations require a class-level variable 'impl'
### Things to check first

- [X] I have searched the existing issues and didn't find my bug already reported there

- [X] I have checked that my bug is still present in the latest release


### Sqlacodegen version

3.0.0rc3

### SQLAlchemy version

2.0.25

### RDBMS vendor

Other

### What happened?

I'm seeing the below error when trying to generate code for a `databricks` "database"
```
❯ sqlacodegen "${db_uri}"
Traceback (most recent call last):
  File "/opt/python/envs/dev310/bin/sqlacodegen", line 11, in <module>
    sys.exit(main())
  File "/opt/python/envs/dev310/lib/python3.10/site-packages/sqlacodegen/cli.py", line 92, in main
    outfile.write(generator.generate())
  File "/opt/python/envs/dev310/lib/python3.10/site-packages/sqlacodegen/generators.py", line 172, in generate
    self.fix_column_types(table)
  File "/opt/python/envs/dev310/lib/python3.10/site-packages/sqlacodegen/generators.py", line 649, in fix_column_types
    column.type = self.get_adapted_type(column.type)
  File "/opt/python/envs/dev310/lib/python3.10/site-packages/sqlacodegen/generators.py", line 681, in get_adapted_type
    new_coltype = coltype.adapt(supercls)
  File "/opt/python/envs/dev310/lib/python3.10/site-packages/sqlalchemy/sql/type_api.py", line 1033, in adapt
    return util.constructor_copy(
  File "/opt/python/envs/dev310/lib/python3.10/site-packages/sqlalchemy/util/langhelpers.py", line 1422, in constructor_copy
    return cls(*args, **kw)
  File "/opt/python/envs/dev310/lib/python3.10/site-packages/sqlalchemy/sql/type_api.py", line 1692, in __init__
    raise AssertionError(
AssertionError: TypeDecorator implementations require a class-level variable 'impl' which refers to the class of type being decorated
```


Just posting to remind me to investigate further and in case others are also seeing this...

### Database schema for reproducing the bug

_No response_

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/72427695/badge)](https://coveralls.io/builds/72427695)

coverage: 97.057% (+0.007%) from 97.05%
when pulling **72751276c22bff12bcc185b8c5d1b7240cfea3cf on fix-typedecorator-adapt**
into **70e1a5c079a9d1568f57b0a242e155d43df9510c on master**.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 431359f..a1159ca 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -9,6 +9,8 @@ Version history
 - Fixed two rendering issues in ``ENUM`` columns when a non-default schema is used: an
   unwarranted positional argument and missing the ``schema`` argument
 - Fixed ``AttributeError`` when metadata contains user defined column types
+- Fixed ``AssertionError`` when metadata contains a column type that is a type decorator
+  with an all-uppercase name
 
 **3.0.0rc5**
 
diff --git a/src/sqlacodegen/generators.py b/src/sqlacodegen/generators.py
index b3fc777..f992624 100644
--- a/src/sqlacodegen/generators.py
+++ b/src/sqlacodegen/generators.py
@@ -36,6 +36,7 @@ from sqlalchemy import (
     String,
     Table,
     Text,
+    TypeDecorator,
     UniqueConstraint,
 )
 from sqlalchemy.dialects.postgresql import JSONB
@@ -684,7 +685,7 @@ class TablesGenerator(CodeGenerator):
                 supercls, "__visit_name__"
             ):
                 # Don't try to adapt UserDefinedType as it's not a proper column type
-                if supercls is UserDefinedType:
+                if supercls is UserDefinedType or issubclass(supercls, TypeDecorator):
                     return coltype
 
                 # Hack to fix adaptation of the Enum class which is broken since


Test patch:
diff --git a/tests/test_generator_tables.py b/tests/test_generator_tables.py
index fc99b7a..0bbd322 100644
--- a/tests/test_generator_tables.py
+++ b/tests/test_generator_tables.py
@@ -4,6 +4,7 @@ from textwrap import dedent
 
 import pytest
 from _pytest.fixtures import FixtureRequest
+from sqlalchemy import TypeDecorator
 from sqlalchemy.dialects import mysql, postgresql
 from sqlalchemy.engine import Engine
 from sqlalchemy.schema import (
@@ -18,7 +19,7 @@ from sqlalchemy.schema import (
     UniqueConstraint,
 )
 from sqlalchemy.sql.expression import text
-from sqlalchemy.sql.sqltypes import NullType
+from sqlalchemy.sql.sqltypes import DateTime, NullType
 from sqlalchemy.types import INTEGER, NUMERIC, SMALLINT, VARCHAR, Text
 
 from sqlacodegen.generators import CodeGenerator, TablesGenerator
@@ -26,6 +27,11 @@ from sqlacodegen.generators import CodeGenerator, TablesGenerator
 from .conftest import validate_code
 
 
+# This needs to be uppercased to trigger #315
+class TIMESTAMP_DECORATOR(TypeDecorator[DateTime]):
+    impl = DateTime
+
+
 @pytest.fixture
 def generator(
     request: FixtureRequest, metadata: MetaData, engine: Engine
@@ -45,12 +51,15 @@ def test_fancy_coltypes(generator: CodeGenerator) -> None:
         Column("bool", postgresql.BOOLEAN),
         Column("vector", VECTOR(3)),
         Column("number", NUMERIC(10, asdecimal=False)),
+        Column("timestamp", TIMESTAMP_DECORATOR()),
         schema="someschema",
     )
 
     validate_code(
         generator.generate(),
         """\
+        from tests.test_generator_tables import TIMESTAMP_DECORATOR
+
         from pgvector.sqlalchemy.vector import VECTOR
         from sqlalchemy import Boolean, Column, Enum, MetaData, Numeric, Table
 
@@ -63,6 +72,7 @@ def test_fancy_coltypes(generator: CodeGenerator) -> None:
             Column('bool', Boolean),
             Column('vector', VECTOR(3)),
             Column('number', Numeric(10, asdecimal=False)),
+            Column('timestamp', TIMESTAMP_DECORATOR),
             schema='someschema'
         )
         """,