> I had thought about this at first, but the meaning
> of "before" is very ambiguous. Consider:
>
> SelectFromStep s1 = DSL.select(a, b);
> SelectWhereStep s2 = DSL.select(a, b);
>
> We know that the SELECT clause has already been added.
>
> Now we have APIs to append FROM and/or WHERE clauses.
> Since the above assignments have no effect on the actual
> implementation, sql(...) would clearly add stuff
> immediately after the SELECT clause, i.e. after the
> previously added clause.
I'm having trouble relating to "before" and "after" here.
Is this an implementation issue? Then before/after make sense, but I can't comment, just accept that reworking the SQL generator would be too much work (which I could well understand).
From a specification perspective, the DSL just specifies an AST.
In that sense, "before" and "after" don't make sense, not as a description of when things are done anyway, but I don't quite understand what you're describing here.
> From a fluent point of view,
> this makes sense, as the following two would intuitively be the same:
>
> s1.sql("/* some comment */").where(...)
> s2.sql("/* some comment */").where(...)
>
> But "before"? Is it before the SELECT keyword? How would
> you know the semantics of "before" given only the type of s2?
You don't know it with "after" either.
Imagine s1.sql("ORDER BY foo"). That's not a Condition anymore.
> This shows that before/after injection is not useful in more
> complex QueryPart chains.
It shows that it's losing some desirable properties.
Assuming proper warnings in the docs, that's acceptable I think.
>> A use case for wrapping over appending could be that the
>> programmer wants to wrap an expression or select in a subselect,
>> using database-specific functionality that Jooq doesn't support yet.
>
> In my opinion, jOOQ offers enough capabilities of wrapping
> tables, fields, selects in plain SQL or custom QueryParts.
I'm just seeing an opportunity to replace a rather large mixture of ad-hoc customization features with a single, semantically straightforward feature. Anything that makes the API narrower without restricting expressiveness is a net win for new code.
It's possible that sql(l, r) isn't as expressive, or that it has other undesirable side effects, and shouldn't be done. Ultimately, it's a judgement call, and I certainly don't have the whole picture here.
>> But the slim added value clearly doesn't pull the weight
>> of the new generic type parameter.
>
> Ehm... replacing all of hint() and similar extension points
> with a single, uniform-semantics function isn't "slim" in my
> book.
> It's taking the pressure out of "this database-specific
> isn't implemented yet", which is a huge bonus for
> programmers who live with a not-yet-well-supported-by-Jooq
> database.
>
> Feel free to list 1-2 examples from your book, adding
> "huge bonuses" (which aren't covered by existing API).
Anything not already in Jooq.
There was that guy who recently wanted to leverage Jooq for his almost-SQL database; I suspect he could get a stopgap solution up and running far easier if he could simply use whatever non-SQL contortion his database wants without having to hack Jooq itself.
Imagine Sqlite introducing something that's similar to windowed functions, but requires some extra keywords in outlandish places that Jooq doesn't cover (yet).
> And, no, I don't consider putting hundreds of comments into
> your SQL statement a huge bonus. You can comment your jOOQ
> SQL statement in Java, already :-)
The issue with views is that their sources get read by people who know and do SQL, not Java.
They'd have trouble even finding the class source that generates the SQL. They'd be unable to interpret the Jooq calls that code would be doing. They'd be unable to do any data flow analysis if the SQL generation is spread over several functions.
That's not an option. And yes the ability to add comments at the point where the code decides to include the code-requiring SQL would be a real enabler.
>> Think about Field:
>>
>> interface Field<T, Q extends QueryPart> extend QueryPart<Q> {
>> }
>>
>> interface TableField<R extends Record, T, Q extends QueryPart> extends Field<T, Q> {
>> }
>
>
> Maybe more like
>
> interface Field<T, Q extends QueryPart<Q>> extends QueryPart<Q>
> interface TableField<R extends Record, T, Q extends QueryPart<Q>> extends Field<T, Q>
>
> You're right, minor glitch on my side.
Thought so :-)
> Let's review actual use-site types. Today, I can write:
>
> Field<Integer> i = DSL.val(1);
>
> This would become...
>
> Field<Integer, ?> i1; // if you're lazy
> Field<Integer, ? extends Field<Integer, ?>> i2;
>
> So many generics for so little effect? I'd rather not :-)
One could do
interface Field<T> extends QueryPart<Field<T>>
This works that the subclasses of Field do not need to vary the return type of sql().
> And there's no concrete type to bind Q to.
> You're always stuck with wildcards,
> unless you subtype Field yourself
Always having a wildcard can point to something that could be redesigned to avoid that type parameter.
I'm not sure how the use case for that would look like so I don't know whether that applies in this particular case.
>> Mastering generics at that level is a bit of a wild
>> ride I'll admit. I did a bit of that recently and
>> don't find it THAT esoteric anymore. It would be an
>> experiment I guess, and it could fail.
>
> I won't keep you from doing an experiment ;-)
I'd have to get reasonably intimate with the Jooq codebase first, which isn't going to happen this year :-(
> My take is: I don't want to unleash unnecessary
> generic typing upon my users.
"Unnecessary" is an overly broad term here.
> It is easy to understand Field<T>.
Yup.
> It is OK to understand Select<R extends Record>.
That already depends on your level of generics expertise.
> It is hard to understand
> TableField<R extends Record, T, Q extends QueryPart<Q>>.
It's just as hard as grokking Comparable<T extends Comparable<T>>.
> During my jOOQ developments, I've discovered and reported
> a couple of bugs to both javac and Eclipse compilers.
> I found no one on Stack Overflow who could prove that one
> or the other (or even the specs) was wrong in the odd corner
> case:
http://stackoverflow.com/q/5361513/521799
I doubt that there's even a good semantics for overloading if generics come into play.
Similar issues exist with
func(Object... o)
func(long... o)
and pass in an array of longs. Java has disambiguation rules for that, but that's got a decidedly PL/I feel to it - it's an ambiguity that most code maintainers won't even spot because definition and call are so far apart, and even if they spot it they usually won't know the disambiguation rule.
> I personall have no clue what the true problem really was,
From a language designer's point of view, combining multiple methods into the same signature is always messy. Java now has three: Run-time polymorphism, overloading, and generics, plus language design space restrictions introduced by type erasure.
Such a mess always requires disambiguation, and disambiguation tends to have counterintuitive results - that's what I meant with "PL/I feel" above.
> Java generics are not easy. Some may understand them to a
> certain extent. But getting them right in an API is
> challenging, in my opinion. Getting them right in a DSL is
> extremely tough, as varargs, overloading and generics
> aren't best friends.
Totally agree with that.
I have stopped using varargs in general, and have made all my varargs functions into Collection ones:
+ No more design hassles if I suddenly need two varargs parameters.
+ No more explosion of API signatures to cover all of Integer..., Collection<Integer>, Iterator<Integer>, and whatnot.
+ No more warnings for creating a generic varargs array.
- Programmers need to call Arrays.asList(T...) for what was previously a varargs call.
* If they feel that's uncomfortable, they can always import asList statically. Or wrap it in a function with a one-letter name (I once defined an l(Object...), but I removed it because I didn't think it was worth the extra indirection for code readers).
> There are lots of blunders in the jOOQ API, regarding generics.
> Adding <Q extends QueryPart<Q>> risks being another one.
Agreed.
> Adding it must be considered extremely carefully.
> But my gut feeling clearly says: little value addition, huge impact.
I think it's a considerable value addition.
Impact can't be eliminated, but it can be mitigated.
My gut feeling is that the effort-to-effect ratio isn't overwhelming but far more favorable than your initial assessment indicates. But then I don't think my guts have much say in this :-)
>> Here's an example:
>>
>> CREATE OR REPLACE FORCE VIEW HF_LAGERMENGEN AS
>> select
>> -- Used in:
>> -- <modules that need to be updated if this view changes>
>> fi_nr,
>> lgnr,
>> identnr,
>> lamenge,
>> -- nondispositive (<some more explanations>)
>> nd,
>> -- reserved (not in arrival)
>> nvl (res, 0) as res,
>> -- in arrival (not reserved)
>> arr - nvl (res_arr, 0) as arr,
>> -- reserved, in arrival
>> nvl (res_arr, 0) as res_arr
>> from
>> ... etc. pp. ...
>
> I understand this use-case, but I fear that using sql() methods
> might make this statement slightly unmaintainable on the Java side.
How that?
It should be something like
.projection("nd").sql("\n-- nondispositive")
> Heh. Here's an idea to toss around:
> For a single-database developer, Jooq would become
> even more useful if he could simply plop in some SQL as
> string constant, using Jooq just to generate those parts
> that are variable. He'd use strings where the SQL is
> constant, and build AST fragments where he needs to be
> variable. It would take the syntactic pain out of
> generated SQL, without imposing the somewhat higher
> verbosity of the DSL on those parts that could simply be
> written in SQL directly.
>
> But you can do that already...?
At arbitrary points in the AST?
If that's the case, you don't need sql(), you have it already.
> Just create your
>
> DSL.using(...).resultQuery(
> "SELECT a, b, /* complex field ahead */ {0} " +
> "FROM t JOIN {1} /* and s jOOQ table */",
> myField, myTable
> ).fetch();
This can insert DSL-generated SQL into a string, but not vice versa.