Skip to content

Commit c425a61

Browse files
committed
Add support for sa.TIME alongside sa.Time, ensuring consistent handling and round-trip behavior
1 parent 2f00b1c commit c425a61

4 files changed

Lines changed: 24 additions & 9 deletions

File tree

src/sqlalchemy_cratedb/compiler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def visit_datetime(self, type_, **kw):
246246
def visit_date(self, type_, **kw):
247247
return "TIMESTAMP"
248248

249-
def visit_time(self, type_, **kw):
249+
def visit_TIME(self, type_, **kw):
250250
"""
251251
CrateDB has no storable `TIME` column type. Plain `TIME` does not exist,
252252
and `TIME WITH TIME ZONE` (TIMETZ) is literal/cast-only ("does not support

src/sqlalchemy_cratedb/dialect.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ class Time(sqltypes.Time):
171171
def bind_processor(self, dialect):
172172
def process(value):
173173
if value is not None:
174+
assert isinstance(value, time) # noqa: S101
174175
return value.isoformat()
175176
return None
176177

@@ -180,8 +181,6 @@ def result_processor(self, dialect, coltype):
180181
def process(value):
181182
if value is None:
182183
return None
183-
if isinstance(value, time):
184-
return value
185184
return time.fromisoformat(value)
186185

187186
return process

tests/create_table_test.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,26 @@ def test_table_time_type(self):
8484
8585
Validates the fix for https://github.com/crate/sqlalchemy-cratedb/issues/206,
8686
where `sa.Time` previously compiled to `TIME`, which CrateDB rejects with
87-
`SQLParseException[Cannot find data type: time]`.
87+
`SQLParseException[Cannot find data type: time]`. Both the generic
88+
`sa.Time` and the SQL-standard capitalised `sa.TIME` must render `STRING`.
8889
"""
8990

9091
class Schedule(self.Base):
9192
__tablename__ = "schedule"
9293
name = sa.Column(sa.String, primary_key=True)
93-
time_col = sa.Column(sa.Time)
94+
time_lower = sa.Column(sa.Time)
95+
time_upper = sa.Column(sa.TIME())
96+
97+
# Exercise both spellings: `sa.TIME` is a subclass of `sa.Time`.
98+
assert isinstance(Schedule.__table__.c.time_lower.type, sa.Time)
99+
assert isinstance(Schedule.__table__.c.time_upper.type, sa.TIME)
94100

95101
self.Base.metadata.create_all(bind=self.engine)
96102
fake_cursor.execute.assert_called_with(
97103
(
98104
"\nCREATE TABLE schedule (\n\tname STRING NOT NULL, "
99-
"\n\ttime_col STRING, "
105+
"\n\ttime_lower STRING, "
106+
"\n\ttime_upper STRING, "
100107
"\n\tPRIMARY KEY (name)\n)\n\n"
101108
),
102109
sa.util.immutabledict({}),

tests/datetime_test.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class FooBar(Base):
106106
datetime_notz = sa.Column(DateTime(timezone=False))
107107
datetime_tz = sa.Column(DateTime(timezone=True))
108108
time = sa.Column(sa.Time)
109+
time_upper = sa.Column(sa.TIME())
109110

110111

111112
@pytest.fixture
@@ -234,10 +235,11 @@ def test_datetime_date(session):
234235
@pytest.mark.skipif(SA_VERSION < SA_1_4, reason="Test case not supported on SQLAlchemy 1.3")
235236
def test_time(session):
236237
"""
237-
An integration test for `sa.Time`.
238+
An integration test for `sa.Time` and the SQL-standard `sa.TIME`.
238239
239240
CrateDB has no native `TIME` type, so the dialect stores it as a `STRING`
240-
in ISO 8601 format and parses it back into a `dt.time` object on read.
241+
in ISO 8601 format and parses it back into a `dt.time` object on read. Both
242+
spellings resolve to the same colspec, so both must round-trip.
241243
242244
Validates the fix for https://github.com/crate/sqlalchemy-cratedb/issues/206.
243245
"""
@@ -246,14 +248,21 @@ def test_time(session):
246248
foo_item = FooBar(
247249
name="foo",
248250
time=dt.time(19, 0, 30, 123456),
251+
time_upper=dt.time(19, 0, 30, 123456),
249252
)
250253
session.add(foo_item)
251254
session.commit()
252255
session.execute(sa.text("REFRESH TABLE foobar"))
253256

254257
# query
255-
result = session.execute(sa.select(FooBar.name, FooBar.time)).mappings().first()
258+
result = (
259+
session.execute(sa.select(FooBar.name, FooBar.time, FooBar.time_upper))
260+
.mappings()
261+
.first()
262+
)
256263

257264
# compare
258265
assert result["time"] == dt.time(19, 0, 30, 123456)
259266
assert isinstance(result["time"], dt.time)
267+
assert result["time_upper"] == dt.time(19, 0, 30, 123456)
268+
assert isinstance(result["time_upper"], dt.time)

0 commit comments

Comments
 (0)