Hi all,

I have a design issue on ticket #7210 that requires some feedback from
the community.

For those that haven't been following along, Ticket #7210 is about
adding the ability to reference fields during a query. Two quick
examples of common use cases:

Find all the hotel rooms that have the same number of chairs and beds:
>>> HotelRoom.objects.filter(n_chairs=F('n_beds'))
=> SELECT * from HotelRoom WHERE n_chairs=n_beds;

Update the number of occupants in room 319 the room by 1:
>>> HotelRoom.objects.filter(room=319).update(n_occupants=F('n_occupants') + 1)
=> UPDATE HotelRoom SET n_occupants=n_occupants+1 WHERE id=319;

Nicolas Lara did some work on this ticket in the scope of his
Aggregates work, leading off an earlier patch from Sebastian Noack.
The ticket was accepted for v1.1 as a must-have. My Github repository
contains the work-in-progress [1]; from my testing, it appears
reasonably solid, although there is still one feature addition
pending.

However, the test cases don't currently pass for all backends, which
leads me to a problem of design and scope. The test suite for F()
notation has revealed some interesting inconsistencies with the
handling of various operators on the various database backends.

Consider integer division. 42/15 returns 2 under SQLite and Postgres.
MySQL, however, returns 2.8, which is rounded into 3 if you try to
assign the division result to an integer column. (e.g., UPDATE person
SET age = 42/15 ...).

Postgres doesn't allow modulo arithmetic on floats; MySQL and SQLite do.

Bitwise operators (& and |) on floats are also inconsistently handled.
SQLite and MySQL are in agreement, but Postgres won't allow bitwise
operations on floating point values without explicit casts to integer
types.

This doesn't even get into the issue of handling dates and strings
using overloaded arithmetic operations.

I can see several possible ways forward:

1) Expose the underlying operations, warts and all. Provide a minimal
test suite that checks that it is possible to do each of the
arithmetic operations, but studiously avoid edge cases known to cause
problems. This is the ostrich solution, but it also acknowledges that
there are differences between backends. Documentation would be updated
to note that some operations are not necessarily portable, and users
should check local guides for details.

2) As for (1), but provide a comprehensive test suite that checks the
different expected results for each backends. This doesn't change
anything in the way the feature would work, but it would explicitly
check that the problematic cases return the expected results. This
would improve test coverage, but would make the test suite much more
complicated, and I'm not convinced it would actually improve the
quality of the code. The bits of code that need testing are in the way
queries are rolled into SQL, not in the way that the backends
interpret that SQL.

3) Try to find the common ground and only provide the subset of
operations that actually behave the same under all backends. I suspect
this would mean we would ultimately end up with no operators
available.

4) Put the infrastructure in place to ensure that integer division is
always handled the same way, regardless of backend handling, and do
strong type checking of operators and operations to make sure the
resultant expression is legal and meaningful. I'm not even sure this
option is feasible for all cases (or even possible), but if we want to
maintain complete database independence, we may need to look down this
road.

Personally, I'm leaning towards option 1. We already acknowledge some
inconsistencies between backend (e.g., fixture ordering issues with
InnoDB, case sensitivity options with some collations). I suspect that
by trying to bend the rules to make things consistent, we will end up
making things a lot more brittle.

Opinions? Other options?

[1] http://github.com/freakboy3742/django/tree/query-expressions

Yours,
Russ Magee %-)

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to