[Dbix-class] [OT][ANNOUNCE] SQL Generation with SQL::DB

Matija Grabnar matija at serverflow.com
Thu Sep 6 15:46:55 GMT 2007


Mark Lawrence wrote:
> Did you mean "Nor was it the easiest"?
No, I meant "Implementing the compile part for the NOT operator was the
hardest, because DBix::Class does not directly support not. Instead,
I had to implement De Morgan's law on the previously "compiled" hash.

>  And how readable is your code?
>   
That, naturally, is a matter of opinion. I don't want to post the whole 
thing here, since I'm writing a paper about it, but here is part of the 
grammar
that deals with the comparisons. (This is using Parse::RecDescent).

cond: ident '=' val { $return = { $item[1] => $item[3]}; }
cond: ident '!=' val { $return = { $item[1] => { '!=',$item[3] } } }
cond: ident '<=' val { $return = { $item[1] => { '<=',$item[3] } } }
cond: ident '>=' val { $return = { $item[1] => { '>=',$item[3] } } }
cond: ident '<' val { $return = { $item[1] => { '<', $item[3] } } }
cond: ident '>' val { $return = { $item[1] => { '>', $item[3] } } }
cond: '(' disjunction ')' { $return = $item[2]; }
cond: 'not' cond { $return = negate($item[2]) }

I find that quite readable. And considering that I had to re-awaken 
15-years old memories about syntax of grammars, *and* learn how 
Parse::RecDescent works, I think the work was done amazingly quickly.
> It is obvious to you at first glance what the query is? I'm curious,
> could you please compare the two versions of the following SQL:
>
>     WHERE lname LIKE '%son%' AND NOT ( age < 10 OR age > 20 )
>
> with SQL::DB:
>
>     where => $r->lname->like('%son%') & ! ($r->age < 10 | $r->age > 20 )
>
> with SQL::Abstract:
>
>     %where = (
>         lname => {like => '%son%'},
>         age   => [-and => {'>=', 10}, {'<=', 20}],
>     );
>   
Well, quite frankly, I don't see why I should write SQL::DB form instead 
of pure SQL. I mean, what does SQL::DB give you that pure SQL doesn't?

And yes, I do consider the SQL::Abstract form quite readable, 
particularly when
it is written out (as you did) with one column name per line. One can 
see much more quickly what is being searched.

Another incidental feature I found about SQL::Abstract is that is easy 
to "tack on" a few more conditions to a search. In any number of my 
programs I have a part where a detailed query is prepared by some 
complicated means, and then the more general condition is simply tacked 
on to the hash just before the search.

For instance, I have one program where I construct a search hash based 
on input from CGI parameters, then, just before the search, I add two 
more global conditions: a date constraint, and a constraint which 
reflects the user's authorization to see specific data.

Adding that to SQL::Abstract has is simple. Adding it to a SQL string 
would not be, nor do I think it would be easy to add to SQL::DB search 
pattern.
> Tell me which you would find easier to write, understand, and maintain? 
>   
I've tried pure SQL and DBIx::Class in projects large and small. So far, 
I found DBIx::Class superior.
> For an even closer approximation it is possible to do this:
>
>     my $age   = $r->age;
>     my $lname = $r->lname;
>     where => $lname->like('%son%') & ! ($age < 10 | $age > 20 )
>   
Which is why I mentioned that implementing NOT was a bit tricky -
you get:

    %where = (
        lname => {like => '%son%'},
        age   => [-and => {'>=', 10}, {'<=', 20}],
    );

by using De Morgan's law. (We studied De Morgan's law in high school. Didn't you?)
So I had to make a De Morgan's law engine inside my parser. Took me all of two hours, and it works just peachy.

Of course, you could also do

    %where = (
        lname => {like => '%son%'},
        age   => {-between => [10, 20]},
    );


Which would make the whole thing even more readable.

Matija



More information about the DBIx-Class mailing list