`TIMESTAMP`, `TIMESTAMP_NTZ`, `TINYINT` and the other `TypeDecorator`s don't declare `python_type`
还没有人认领这个 Issue。
评估
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 新手友好度
- 78/100
调研方向
从定义了五个TypeDecorator子类的src/databricks/sqlalchemy/_types.py开始,阅读它们的实现和现有类型测试。检查每种类型的python_type是否与其返回值相符:TIMESTAMP和TIMESTAMP_NTZ对应datetime,TINYINT对应int,DatabricksTimeType对应time,DatabricksStringType对应str。当这些类型在受支持的SQLAlchemy版本下报告预期的Python类时,即为完成。
由索引模型根据 Issue 内容生成。
描述
Description of the bug
Our use case
We generate SQLAlchemy ORM models from Databricks tables with sqlacodegen. It reflects the tables through this dialect and annotates each column with the column type's python_type, for example Mapped[str | None] for a STRING column.
For TIMESTAMP, TIMESTAMP_NTZ and TINYINT columns we get Mapped[object | None] instead of Mapped[datetime | None] and Mapped[int | None]. These are the types reflection returns for timestamp, timestamp_ntz and tinyint. Type checkers then accept any value for these attributes, and any code reading them sees object.
The same happens to any other tool that reads python_type, and to the two types the dialect uses in place of Time and String through its colspecs (DatabricksTimeType and DatabricksStringType).
The bug
These five types are sqlalchemy.types.TypeDecorator subclasses that don't declare python_type:
| Type | impl |
Values returned | python_type |
|---|---|---|---|
TIMESTAMP |
DateTime |
datetime |
object |
TIMESTAMP_NTZ |
DateTime |
datetime |
object |
TINYINT |
SmallInteger |
int |
object |
DatabricksTimeType |
Time |
time |
object |
DatabricksStringType |
String |
str |
object |
TypeDecorator doesn't take python_type from its impl, so these types get the TypeEngine default: object in SQLAlchemy 2.1, and a NotImplementedError in 2.0.
The SQLAlchemy maintainers consider this the intended behaviour. A TypeDecorator can return different values from its impl through process_result_value, so only the decorator knows its Python type, and it's expected to declare python_type itself: link to the SQLAlchemy discussion.
Versions
- databricks-sqlalchemy: 2.0.10 (also on
main) - databricks-sql-connector: 4.6.0
- SQLAlchemy: 2.1.4, and 2.0.54
- Python: 3.12
- OS: Linux
To Reproduce
No connection is needed:
import datetime
from importlib.metadata import version
import sqlalchemy as sa
from databricks.sqlalchemy import TIMESTAMP, TIMESTAMP_NTZ, TINYINT, DatabricksDialect
print("SQLAlchemy", sa.__version__)
print("databricks-sqlalchemy", version("databricks-sqlalchemy"))
dialect = DatabricksDialect()
expected = {
# The types reflection returns for timestamp, timestamp_ntz and tinyint.
"TIMESTAMP": (TIMESTAMP(), datetime.datetime),
"TIMESTAMP_NTZ": (TIMESTAMP_NTZ(), datetime.datetime),
"TINYINT": (TINYINT(), int),
# The types the dialect uses in place of Time and String.
"Time": (sa.Time().dialect_impl(dialect), datetime.time),
"String": (sa.String().dialect_impl(dialect), str),
}
for name, (type_, python_type) in expected.items():
print(f"{name:14} {type(type_).__name__:20} python_type: {type_.python_type}")
for name, (type_, python_type) in expected.items():
assert type_.python_type is python_type, name
Output
With SQLAlchemy 2.1.4:
SQLAlchemy 2.1.4
databricks-sqlalchemy 2.0.10
TIMESTAMP TIMESTAMP python_type: <class 'object'>
TIMESTAMP_NTZ TIMESTAMP_NTZ python_type: <class 'object'>
TINYINT TINYINT python_type: <class 'object'>
Time DatabricksTimeType python_type: <class 'object'>
String DatabricksStringType python_type: <class 'object'>
Traceback (most recent call last):
...
AssertionError: TIMESTAMP
With SQLAlchemy 2.0.54, the first python_type raises NotImplementedError.
Expected behaviour
Each type's python_type is the type of the values it returns: datetime for TIMESTAMP and TIMESTAMP_NTZ, int for TINYINT, time for DatabricksTimeType and str for DatabricksStringType.
Possible fix
Declare python_type on each type in src/databricks/sqlalchemy/_types.py, which already imports datetime and time from datetime:
class TIMESTAMP_NTZ(sqlalchemy.types.TypeDecorator):
...
@property
def python_type(self) -> type:
return datetime
The same goes for TIMESTAMP (datetime), TINYINT (int), DatabricksTimeType (time) and DatabricksStringType (str).
Additional context
-
DatabricksArray,DatabricksMapandDatabricksVariantalso reportobject, becauseUserDefinedTypehas the same default. That's right for all three today.VARIANTcan hold any value, andARRAYandMAPhave noresult_processor, so their values are whatever the connector returns, which depends on its settings:databricks-sql-connector 4.6.0 ARRAYMAPdefault numpy.ndarraylistof(key, value)tuplesdisable_pandas=Truelistlistof(key, value)tuples_use_arrow_native_complex_types=Falsestr(JSON)str(JSON)A
result_processorconverting the values tolistanddictwould let these two types declarepython_typetoo, but that changes the values users get, so it's not part of this request. -
Our workaround: when a
TypeDecoratorreportsobject, we use itsimpl'spython_type. That's correct for the five types above, but as the SQLAlchemy maintainers point out, it's only a guess in general.
Thank you!
- 主要语言
- Python
- 星标
- 24
- 派生
- 20
- PR 合并指标
- 30 天内没有已合并 PR
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
databricks/databricks-sqlalchemy 的其他 Issue
-
get_columns() missing @reflection.cache causes a warehouse round-trip on every reflection call可能已有人在做 @TangoEnSkai 于 50 天前认领。 未关闭
难度 1/5 1 小时以内 新手友好度 92/100
-
get_foreign_keys() missing @reflection.cache causes excessive DESCRIBE TABLE EXTENDED queries可能已有人在做 @TangoEnSkai 于 50 天前认领。 未关闭
难度 1/5 1 小时以内 新手友好度 90/100
-
TypeError when using Enum columns with the Databricks dialect (all 2.0.x releases)可能已有人在做 @TangoEnSkai 于 50 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 70/100
-
难度 3/5 1-2 天 新手友好度 64/100
databricks/databricks-sqlalchemy#77 · 3 条评论 ·
-
Unable to insert Python lists into ARRAY<STRING> columns using pandas to_sql可能已有人在做 @aminghadersohi 于 16 天前认领。 未关闭
难度 4/5 3-5 天 新手友好度 48/100
databricks/databricks-sqlalchemy#73 · 1 条评论 ·
查看 databricks/databricks-sqlalchemy 的全部 Issue
相似的 Issue
-
难度 1/5 1-3 小时 新手友好度 88/100
gradio-app/gradio#13964 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 93/100
BSData/horus-heresy-3rd-edition#3232 · 1 条评论 ·
维护者通常 1 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 78/100
jbacalso24/mimry#123 ·
维护者通常 1 天内回复
-
p:3-mid pydanty:bug pydanty:is-working
难度 2/5 1-3 小时 新手友好度 76/100
pydantic/pydantic-ai#10153 · 1 条评论 ·
维护者通常 1 天内回复
-
good first issue priority: medium size: S testing
难度 2/5 1-3 小时 新手友好度 85/100
cubrid-lab/cubrid-cookbook-python#316 ·
维护者通常 1 天内回复