Hey Mike and company,
I've run into what I assume is a bug but hope that someone could correct
this
assumption.
A where clause of whether a tstzrange (resolved to a DateTimeTZRange)
contains
"now" raises an exception because an incorrect "contains" function is
associated
with it during the translation to SQL. Rather than resolving to an
appropriate "@>" containment operator, sqlalchemy / psycopg2 are resolving
to something that seems to be appropriate for the interval type within the
tstzrange
expression.
The following valid SQL lets you create a tstzrange from two timestamps.
Based on
the traceback, it appears that SQLAlchemy is failing to recognize the
parameters as
timestamps, recognizing them as intervals instead:
```
select tstzrange((current_date + '8 hours'::interval), (current_date +
'12 hours'::interval), '[]');
```
The line that triggers the exception using sqlalchemy is:
where(sched_sel.c.timeslot.contains(func.now())))
For the sake of keeping the code "alive" indefinitely, I'm pasting all of
it here.
If you want to run the example, update db_url with your postgresql
connection string.
The traceback is at the end.
I'm using Postgresql v10, SQLAlchemy v1.1.14, psycopg2 v2.7.3.2, and Python
3.6.1.
Thanks for considering!
Regards
Darin
github profile: (https://github.com/Dowwie)
-------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
"""
The following example illustrates a recurring meeting/appointment schedule
data
model using Postgresql's date, time, and range features. A query confirms
whether there is a scheduled event currently ongoing -- now.
This example currently does not work, but the issue may likely be due to a
bug
rather than the implementation presented.
"""
from datetime import datetime, timedelta
import pytz
from psycopg2.extras import DateTimeTZRange
from sqlalchemy.dialects.postgresql import INTERVAL, ARRAY
from sqlalchemy.dialects.postgresql.ranges import TSTZRANGE
from sqlalchemy import (Integer, Table, Column, create_engine, MetaData,
select, func, insert, literal, ForeignKey, join,
and_)
db_url = "postgres://username:password@db:5432/yourdb"
engine = create_engine(db_url, echo=True)
metadata = MetaData(engine)
t_schedule = Table('tmp_schedule', metadata,
Column('id', Integer, primary_key=True,
autoincrement=True),
Column('time_period', TSTZRANGE, nullable=False))
t_timeslot = Table('tmp_timeslot', metadata,
Column('id', Integer, primary_key=True,
autoincrement=True),
Column('schedule_id', ForeignKey('tmp_schedule.id')),
Column('weekdays', ARRAY(Integer)),
Column('start_time', INTERVAL),
Column('end_time', INTERVAL))
metadata.drop_all()
metadata.create_all()
now = datetime.now(pytz.utc)
later = now + timedelta(days=60)
time_period = DateTimeTZRange(now, later, bounds='[]')
conn = engine.connect()
with conn.begin() as trans:
schedule_id = conn.scalar(insert(t_schedule).
values({'time_period': time_period}).
returning(t_schedule.c.id))
conn.execute(insert(t_timeslot).
values({'schedule_id': schedule_id,
'weekdays': [6],
'start_time': timedelta(hours=9),
'end_time': timedelta(hours=12)}))
conn.execute(insert(t_timeslot).
values({'schedule_id': schedule_id,
'weekdays': [1, 2, 3, 4, 5],
'start_time': timedelta(hours=17),
'end_time': timedelta(hours=20)}))
now_weekday = datetime.now().weekday()
thejoin = join(t_schedule, t_timeslot,
t_schedule.c.id == t_timeslot.c.schedule_id)
timeslot = func.tstzrange(func.current_date() + t_timeslot.c.start_time,
func.current_date() + t_timeslot.c.end_time,
'[]')
sched_sel = (select([timeslot.label('timeslot')]).
select_from(thejoin).
where(and_(t_timeslot.c.weekdays.contains([now_weekday]),
t_schedule.c.time_period.contains(func.now())))).alias('z')
ongoing_sel = (select([literal(True)]).
where(sched_sel.c.timeslot.contains(func.now())))
print('\n\n', ongoing_sel.compile(engine))
result = conn.scalar(ongoing_sel)
print('A scheduled timeslot is ongoing: {}'.format(result))
trans.rollback()
-------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
sqlalchemy.exc.DataError: (psycopg2.DataError) invalid input syntax for
type interval: "%"
LINE 5: WHERE (z.timeslot LIKE '%' + now() || '%')
^
[SQL: "SELECT %(param_1)s AS anon_1 \nFROM (SELECT tstzrange(CURRENT_DATE
+ tmp_timeslot.start_time, CURRENT_DATE + tmp_timeslot.end_time,
%(tstzrange_1)s) AS timeslot \nFROM tmp_schedule JOIN tmp_timeslot ON
tmp_schedule.id = tmp_timeslot.schedule_id \nWHERE tmp_timeslot.weekdays @>
%(weekdays_1)s AND (tmp_schedule.time_period @> now())) AS z \nWHERE
(z.timeslot LIKE '%%' + now() || '%%')"] [parameters: {'param_1': True,
'tstzrange_1': '[]', 'weekdays_1': [5]}]
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable
Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.