No obvious way to do "IS (NOT) NULL" in a built query?

John Morahan - December 7, 2008 - 17:52
Project:Drupal
Version:7.x-dev
Component:database system
Category:bug report
Priority:normal
Assigned:Crell
Status:closed
Description

There doesn't seem to be a proper way to do an "IS NULL" or "IS NOT NULL" condition in a built query. I asked on IRC and DamZ suggested $condition->where($field . ' IS NOT NULL'); with the caveat that it is horrible, which I agree it is - the query builder is supposed to avoid the need for concatenating strings like this, isn't it?

#1

Crell - December 7, 2008 - 23:12

Yeah, we should provide a better mechanism there. I guess the knee-jerk suggestion would be $condition->isNull($fieldname) and $condition->isNotNull($fieldname), but something in me doesn't like having 2 methods like that as we could easily start ballooning our method count. I don't like $condition->null($fieldname, TRUE) either, though, because of the magic boolean.

Any DX input? Someone go find bjaspan and ask him. :-)

#2

alexanderpas - December 9, 2008 - 16:13

I personally like $condition->isNull($fieldname) and $condition->isNotNull($fieldname) as they're of the same category as the isTrue and isFalse functions, which are normally generally accepted, and SQL, just like some other parts of Drupal (hook_access() for example) uses Ternary logic: http://en.wikipedia.org/wiki/Ternary_logic

I only would go for $condition->null($fieldname, TRUE) if it was easy to negate, which isn't on the database layer without the use of a magic boolean (which is just not nice).

#3

Crell - December 9, 2008 - 16:17

Well I figured $condition->null($fieldname, TRUE) would mean "$fieldname IS NULL" and $condition->null($fieldname, FALSE) would mean "$fieldname IS NOT NULL", so it's nice and easy to negate that way. I'm still not totally convinced of either mechanism yet, though.

Either way we'd probably have the same internal operator handling, which will be annoying to write either way but is, I agree, necessary.

#4

Crell - December 9, 2008 - 16:35

I suppose another option would be $condition->condition($fieldname, NULL, "NULL") and $condition->condition($fieldname, NULL, "NOT NULL"), and then we special case those operators just as we special case LIKE, BETWEEN, IN, etc. That would be closest to the internal implementation, I believe. So that's three possible approaches.

#5

alexanderpas - December 9, 2008 - 23:21

and then we special case those operators just as we special case LIKE, BETWEEN, IN, etc.

I don't like "special cases".

That would be closest to the internal implementation, I believe.

afaik it's not nice to see internals dripping outside ;)

also, on a documentation side, it's nice to see short, clear and consistent methods, instead of big clunky functions that have several magic inputs.

also, if $condition->where($field . ' IS NOT NULL'); is ugly,
$condition->condition($fieldname, NULL, "NULL") and
$condition->condition($fieldname, NULL, "NOT NULL") are also ugly.

#6

Crell - December 10, 2008 - 07:02
Assigned to:Anonymous» Crell
Status:active» needs review

Well, here's a version using isNull() and isNotNull(), complete with unit tests.

In order to make it work, I had to introduce a new operator property in the conditional builder, use_value', which is a flag indicating if the conditional fragment should care about the value of the value field. Then we use the built in operator mapping (the special casing mechanism, sorry there's no way around it and it's there for a very good reason) to flag the IS NULL and IS NOT NULL operators to have that flag be false, so that we skip the placeholder after the operator.

That in turn allowed us to use the existing conditions array, which in turn means that ->condition($field, NULL, 'IS NULL') will work perfectly. isNull() is just a wrapper around it. That's good, because it simplifies the code and ensures that alter mechanisms will work properly. So we get two ways of handling NULLs for the price of one. Score. :-) (If we decided we wanted to use null($field, TRUE) instead, switching over to that should be a trivial front-end change now.

I also did something a little unconventional in the unit tests. I split off the table creation to a separate utility method that gets called for each unit test explicitly rather than implicitly. That reduces the number of tables that we have to create and destroy for every single unit test, and hopefully reduces the performance drain on simpletest. If webchick is cool with it then we'll probably switch other unit test over to this mechanism later, but for now this is a decent test test.

AttachmentSizeStatusTest resultOperations
condition-is-null.patch9.73 KBIdleFailed: 9364 passes, 1 fail, 0 exceptionsView details | Re-test

#7

alexanderpas - December 10, 2008 - 12:52
Status:needs review» needs work

In order to make it work, I had to introduce a new operator property in the conditional builder, use_value', which is a flag indicating if the conditional fragment should care about the value of the value field. Then we use the built in operator mapping (the special casing mechanism, sorry there's no way around it and it's there for a very good reason) to flag the IS NULL and IS NOT NULL operators to have that flag be false, so that we skip the placeholder after the operator.

well, i said i don't like "special cases", but what i really meant was that i don't like *exposed* "special cases".

If we decided we wanted to use null($field, TRUE) instead, switching over to that should be a trivial front-end change now.

and that's a thing i like, don't let the internals drip out.

also i seems to be missing the havingIsNull documentation

talk to webchick about the unit tests, afaik simpletest always does setUp and tearDown before and after respectively each test, in order to have a proper enviroment for each test.

#8

Crell - December 10, 2008 - 16:20
Status:needs work» needs review

Yes, but the DB tests setUp routine is very expensive. That's why I want to break it out into separate routines, because there's no reason to load every DB test table up for every single unit test, since most only use one or two of them (or none).

All of the having conditional methods (havingConditions(), havingArguments(), having()) are documented by the comment above them as being identical to the QueryConditionalInterface, but for having. None of them have their own docblocks either. Perhaps they should, but that would be a separate patch.

#9

alexanderpas - December 11, 2008 - 00:19

we should at least have a small docblock about having, but that's indeed another issue.

#10

System Message - February 2, 2009 - 23:50
Status:needs review» needs work

The last submitted patch failed testing.

#11

Crell - February 3, 2009 - 04:41

I'm not rerolling this until the extenders patch is done, as they conflict and that one is bigger. :-)

#12

chx - March 7, 2009 - 19:58
Status:needs work» needs review
AttachmentSizeStatusTest resultOperations
condition-is-null.patch9.05 KBIdleFailed: Failed to install HEAD.View details | Re-test

#13

System Message - March 7, 2009 - 20:10
Status:needs review» needs work

The last submitted patch failed testing.

#14

chx - March 7, 2009 - 20:58
Status:needs work» needs review

Ah ha!

AttachmentSizeStatusTest resultOperations
condition-is-null.patch9.05 KBIdleFailed: Failed to install HEAD.View details | Re-test

#15

chx - March 7, 2009 - 20:58
AttachmentSizeStatusTest resultOperations
condition-is-null.patch9.39 KBIdlePassed: 10568 passes, 0 fails, 0 exceptionsView details | Re-test

#16

chx - March 7, 2009 - 21:02

I only did SelectQueryExtender not SelectQueryInterface. Fixed.

AttachmentSizeStatusTest resultOperations
condition-is-null.patch10 KBIdleFailed: Failed to install HEAD.View details | Re-test

#17

Crell - March 7, 2009 - 21:18
Status:needs review» needs work

OK, I think I misled chx. SelectQueryInterface extends QueryConditionalInterface, so there's no need to define the methods a second time. Otherwise I think it looks good, bot pending.

#18

chx - March 9, 2009 - 09:59
Status:needs work» needs review
AttachmentSizeStatusTest resultOperations
condition-is-null.patch9.39 KBIdlePassed: 10568 passes, 0 fails, 0 exceptionsView details | Re-test

#19

alexanderpas - March 10, 2009 - 22:15
Status:needs review» reviewed & tested by the community

you forgot The Swedish Chef... Bork, bork, bork!

#20

webchick - March 14, 2009 - 17:49
Status:reviewed & tested by the community» needs work

Cool, committed this to HEAD with a couple adjustments to the NOT NULL assertion messages.

This change should be reflected in the DBTNG and upgrade documentation.

#21

webchick - March 14, 2009 - 17:52

tagging.

#22

Crell - July 2, 2009 - 22:46
Status:needs work» fixed

Documentation added, finally.

#23

System Message - July 16, 2009 - 22:50
Status:fixed» closed

Automatically closed -- issue fixed for 2 weeks with no activity.

 
 

Drupal is a registered trademark of Dries Buytaert.