[orm] Explicit ASTs (also sent to the DBIC list)
Matt S Trout
dbix-class at trout.me.uk
Sun Sep 3 09:09:32 EDT 2006
David E. Wheeler wrote:
> On Sep 2, 2006, at 13:53, Matt S Trout wrote:
>
>> DWIM AST -> explicit AST -> SQL
>
> Agreed. This way, the AST does not have to be specific to any one
> back end (PostgreSQL, SQLite, MySQL, LDAP, a object iterator, etc.).
Precisely. And we can have as many front-ends as we want (we'll need at
least an SQL::Abstract DWIM AST -> explicit AST for our and for Nate's
usage for a start, and I think figuring out how to compile Jifty::Query
to this is probably the way to enable its usage against DBIC, which I
know audrey wanted to happen).
>> I reckon the basic query structure can be a hashref since we have
>> @lots_of_clauses (and since we're bound to miss something first
>> time round)
>> but each clause should probably be in arrayref form, so we have
>> something like
>>
>> foo = 'yay' AND bar LIKE 'wooho%'
>
> I'll just assume that you're using explicit SQL here, since we're not
> discussing the DWIM part. IOW, You're saying, to get the above, one
> has to create an AST like the below, yes?
Right.
>> becoming
>>
>> [ -op, 'AND',
>> [ -op, '=', [ -name, 'foo' ], [ -bind, 'yay' ] ],
>> [ -op, 'LIKE', [ -name, 'bar' ], [ -bind, 'wooho%' ] ],
>> ],
>>
>> and a function call like
>>
>> COUNT( * )
>>
>> would become
>>
>> [ -func, 'COUNT', '*' ]
>
> Sure, that works for me.
>
>> the basic principle here is that an op generally takes a specific
>> number of
>> arguments, whereas a function is a containing clause that can take a
>> potentially arbitrary number. So a complete statement might be
>> something like
>>
>> SELECT my.a, my.b FROM my_table my WHERE spork = 1
>>
>> and that would become
>>
>> {
>> select => [ [ -name, 'my', 'a' ], [ -name, 'my', 'b' ] ],
>
> The '-name' key might be unnecessary, no? Unless there's something
> else that might be included? Oh, wait, there is. It should probably
> be something like:
>
> [ [ -name => 'my', -alias => 'a' ],
> [ -name => 'my', -alias => 'b' ],
> ]
Err, no, that's going the wrong way -
[ -name, 'my', 'a' ] => my.a
[ -alias, [ -name, 'my', 'a' ], 'a' ] => my.a AS a
I think.
> If we're going to be precise (and pedantic). No? And since there
> multiple keys per token, it should actually probably be:
>
> [ { -name => 'my', -alias => 'a' },
> { -name => 'my', -alias => 'b' },
> ]
No, not at all. The structure logically nests the way shown above - that
should enable things like
[ -alias,
[ -func, 'COUNT',
[ -func, 'DISTINCT',
[ -name, 'my', 'a' ]
]
],
'a_cnt'
]
for "COUNT(DISTINCT my.a) AS a_cnt"
>> from => [ [ -alias, 'my_table', 'my' ] ],
>
> That would have to be:
>
> from => [ { -name => 'my_table', -alias => 'my' } ]
More
[ [ -alias, [ -name, 'my_table' ], 'my' ] ]
to preserve the nesting (again that -name could be replaced with a
subselect, or a function invocation for SELECT * FROM my_sproc(...), or
etc.)
>> where => [ [ -op, '=', [ -name, 'spork' ], [ -bind, 1 ] ] ],
>
> And here:
>
> where => [ [ -op, '=', { -name => 'spork', -bind => 1 } ] ],
>
> No?
No, definitely not, that restricts us to <column reference> = <value>
which is one of our big bugbears with SQL::Abstract on its own - for
example keeping it as a pair of arrayrefs allows
[ [ -op, '=', [ -name, 'spork' ], [ -name, 'spoon' ] ] ]
to produce "spork = spoon" in the final SQL - or having a function on
the LHS or whatever.
>> }
>>
>> which is ... spectacularly ugly, but I -think- explicit enough. Fun
>> will be
>> handling specific things like
>
> Yes, I was making it even more explicit. Whether that's over the top
> or not I'm not sure. Also, I'm thinking that the -bind value ought to
> support a code ref, so that it could potentially bind a different
> value every time it's called.
I agree in places, but your hashref style results in data loss as
expanded above so I'm pretty much certain that's wrong.
We'll have to think carefully about how -bind works; Scalar::Defer might
be sufficient to the task but I think explicit support for subrefs and
for named bind parameters somehow will also be worthwhile.
>> MATCH (title,body) AGAINST ('database')
>>
>> but I guess that's just
>>
>> [ -func, 'MATCH',
>> [ [ -name, 'title' ], [ -name, 'body' ] ],
>> [ [ -bind, 'database' ] ]
>> ]
>
> Right.
>
>> and have something like "func_specific_$func" and "func_generic"
>> methods on
>> the emitter, so MATCH gets handled by func_specific_MATCH whereas
>> something
>> "normal" like COUNT can go through func_generic (we'd enumerate the
>> specific
>> methods at instantiation time for performance, I think).
>
> Well, I think that COUNT() is only in the select or group by clauses,
> never in where. But I'd also like to explicitly include support for
> MATCH(), so that one can use regular expressions in WHERE clauses,
> just as one can use LIKE.
True re count, but the point here is more one of enabling easy writing
of syntax generators, and COUNT and its ilk are a relatively standard
syntax whereas MATCH isn't. The func_generic/func_specific stuff would
be an implementation detail from an external POV.
As for supporting MATCH explicitly we'd have to do a run-through of what
other DBs provide by way of regex syntax, but I guess you can already
tell us that?
--
Matt S Trout Offering custom development, consultancy and support
Technical Director contracts for Catalyst, DBIx::Class and BAST. Contact
Shadowcat Systems Ltd. mst (at) shadowcatsystems.co.uk for more information
+ Help us build a better perl ORM: http://dbix-class.shadowcatsystems.co.uk/ +
More information about the orm
mailing list