Opened 8 months ago

Closed 6 months ago

Last modified 6 months ago

#36078 closed Cleanup/optimization (fixed)

Document that Postgres normalizes a range field with no points normalizes to empty

Reported by: Mapiarz Owned by: Clifford Gama
Component: contrib.postgres Version: 4.2
Severity: Normal Keywords: postgres
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Mapiarz)

This short test exemplifies the problem:

now = timezone.now()
empty_range = psycopg2_range.DateTimeTZRange(empty=True)

test_model = TestModel(range=psycopg2_range.DateTimeTZRange(now, now))

self.assertFalse(test_model.range.isempty)
self.assertNotEqual(test_model.range, empty_range)

test_model.save()
test_model.refresh_from_db()

self.assertTrue(test_model.range.isempty)
self.assertEqual(test_model.range, empty_range)

I would expect the field value not to change after saving, and it should continue to have the lower, upper and boundary fields set.

Repro project: https://github.com/Mapiarz/django_postgres_range_repro/tree/master

The example is for DateTimeRange, but DateRange Is also affected. Other ranges possibly too, I haven't tested.

Change History (14)

comment:1 by Mapiarz, 8 months ago

Description: modified (diff)

comment:2 by Sarah Boyce, 8 months ago

Resolution: invalid
Status: newclosed

Thank you for the careful report I was able to replicate and had a look into this

From what I can tell, this is Postgres behavior and so I don't think this is a bug in Django
See https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO

 -- includes no points (and will be normalized to 'empty')
 SELECT '[4,4)'::int4range;

comment:3 by Claude Paroz, 8 months ago

comment:4 by Sarah Boyce, 8 months ago

Resolution: invalid
Status: closednew
Summary: Postgres date and time range fields change after saving objectDocument that Postgres normalizes a range field with no points normalizes to empty
Triage Stage: UnreviewedAccepted
Type: BugCleanup/optimization

Yeah, I think it was surprising and would help to be pointed out in the docs

comment:5 by Mapiarz, 8 months ago

I agree. There should be an explicit warning about this. It's really easy to be caught of guard by this and lose data.

I do not understand why psycopg opted out of normalizing the value to empty, just like postgres does. I have raised this issue here: https://github.com/psycopg/psycopg/discussions/993

comment:6 by Daniele Varrazzo, 8 months ago

I agree. There should be an explicit warning about this. It's really easy to be caught of guard by this and lose data.

You are already losing data, because if you are calling DateTimeTZRange('2025-01-01', '2025-01-31') to mean the whole month of January you are losing a day, because the default bounds are [) so the 31st is not in the range, the same way that 4 is not in the [4,4) range. The data loss comes from using the wrong bounds, not from the normalization rules, which are the same whether implemented by Python or by Postgres.

In the above example, DateTimeTZRange(now, now, '[]') might have been what you wanted.

I do not understand why psycopg opted out of normalizing the value to empty, just like postgres does. I have raised this issue here: ​https://github.com/psycopg/psycopg/discussions/993

I have left a response on the psycopg tracker. I am -0.5 to introduce normalization in Python, but, regardless from that, I don't think it would have resulted in any difference w.r.t. the issue explained here. If we had returned what the OP expected to receive it would have meant we have a bug as Psycopg normalization would disagree with Postgres one.

comment:7 by Clifford Gama, 8 months ago

Owner: set to Clifford Gama
Status: newassigned

comment:8 by Clifford Gama, 8 months ago

Has patch: set

comment:9 by Sarah Boyce, 6 months ago

Patch needs improvement: set

comment:10 by Sarah Boyce, 6 months ago

Patch needs improvement: unset

comment:11 by Sarah Boyce, 6 months ago

Triage Stage: AcceptedReady for checkin

comment:12 by Sarah Boyce <42296566+sarahboyce@…>, 6 months ago

Resolution: fixed
Status: assignedclosed

In 611e7bc3:

Fixed #36078 -- Doc'd that Postgres normalizes a range field with no points to empty.

Co-authored-by: Sarah Boyce <42296566+sarahboyce@…>

comment:13 by Sarah Boyce <42296566+sarahboyce@…>, 6 months ago

In 428cb3af:

[5.2.x] Fixed #36078 -- Doc'd that Postgres normalizes a range field with no points to empty.

Co-authored-by: Sarah Boyce <42296566+sarahboyce@…>

Backport of 611e7bc3a0633a35ae3430e359c646e02fa3801d from main.

comment:14 by Sarah Boyce <42296566+sarahboyce@…>, 6 months ago

In d05cf7c3:

[5.1.x] Fixed #36078 -- Doc'd that Postgres normalizes a range field with no points to empty.

Co-authored-by: Sarah Boyce <42296566+sarahboyce@…>

Backport of 611e7bc3a0633a35ae3430e359c646e02fa3801d from main.

Note: See TracTickets for help on using tickets.
Back to Top