2013/6/26 Durchholz, Joachim <[email protected]>
> > Yes, it would certainly be possible to add a sql()
> > method to a top-level type, e.g. QueryPart. But
> > the huge drawback of this is that the whole
> > QueryPart type tree would need to be generified to
> > allow for:
> >
> > interface QueryPart<Q extends QueryPart> {
> > Q sql(String sql);
> > }
>
> That's indeed necessary.
> Pervase change: Yes.
> Huge Problem? I don't see that.
I'll get to that...
> > Q is necessary to allow for fluent injection of SQL.
>
> Ack.
>
> > We'd probably have to check whether appending or
> > prepending is more useful (or both?).
>
> For comments, appending would be enough.
> Other use cases that I can see off the top of my head are appending, too,
> so that is the default case.
>
> However, to deal with the odd "but I need something prepended" case, I'd
> offer a variant of sql() that wraps the SQL between a prefix and a postfix.
> sql(String) would then be
>
> Q sql(String after) {
> return sql("", after);
> }
> abstract Q sql(String before, after);
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. 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?
This shows that before/after injection is not useful in more complex
QueryPart chains.
> > In the context of a SELECT statement, it could only
> > be "appending".
>
> 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. Example:
Field<?> f = DSL.field("/* custom stuff */ {0} /* surrounding... */",
MY_EXPRESSION);
> > 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). 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 :-)
// here we create that odd DSLContext
DSL.using(...)
// let's project stuff
.select(
// Oh, and the ID is important
MY_TABLE.ID
// And this needs aliasing bla bla
MY_TABLE.TITLE.as("x")
)...
> > 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.
> > That would be a very "unfriendly" addition for the end-user :-)
>
> Yes, the dependent-typing syntax of Java sucks. Mainly because you can't
> name type subexpressions, so it gets repetitive ad nauseam.
> For application programmers implementing these interfaces, it's
> essentially still
>
> interface Field<T, Q>
> interface TableField<R, T, Q>
>
> With the additional constraint that Q must be a QueryPart - that's doable
> for an application programmer.
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 :-)
And there's no concrete type to bind Q to. You're always stuck with
wildcards, unless you subtype Field yourself
> Maybe a paragraph "reading generic declarations" in the Jooq docs is in
> order, saying that
> a) One should read a generic declaration twice
> b) First pass: just collect the type names (R, T, Q for TableField)
> c) Second pass: for each type, read its constraint
> d) Just fill all type parameters you need filled. Something that
> implements QueryPart<String> won't work, but Jooq doesn't declare classes
> with that kind of nonsensical definition (nor would Java allow the
> existence of such a class).
>
> These interfaces are actually quite straightforward. The Q extends
> QueryPart<Q> thing is a bit hairy because it's circular, but then
> application programmers aren't supposed to write subclasses of QueryPart.
>
> 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 ;-)
My take is: I don't want to unleash unnecessary generic typing upon my
users. It is easy to understand Field<T>. It is OK to understand Select<R
extends Record>. It is hard to understand TableField<R extends Record, T, Q
extends QueryPart<Q>>.
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 personall have no clue what the true problem really was, according to the
JLS
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. There are lots of blunders in the jOOQ API,
regarding generics. Adding <Q extends QueryPart<Q>> risks being another
one. Adding it must be considered extremely carefully. But my gut feeling
clearly says: little value addition, huge impact.
> > Should be simple:
> >
> > // Construct your statement:
> > Select<?> select = //...
> >
> > // Render it with inlined bind values:
> > String sql = DSL
> > .using((Connection) null, dialect, new Settings()
> > .withStatementType(STATIC_STATEMENT)
> > .withRenderFormatted(true) // possibly?
> > ).render(select);
> >
> > String view = "CREATE OR REPLACE VIEW my_view AS " + sql;
>
> Yes.
> The point was that I'd want to insert comments, sometimes at the field
> level.
DSL.field("/* comment before */ {0} \n--comment after\n", MY_FIELD);
> 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 have other views where the join conditions have comments. Or where the
> selection of tables that got joined warrants an explanation.
I understand this use-case, but I fear that using sql() methods might make
this statement slightly unmaintainable on the Java side. And if this is a
use-case, a custom Field implementation adding comments to the projection
might not be too tough.
> 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...? Just create your
DSL.using(...).resultQuery(
"SELECT a, b, /* complex field ahead */ {0} " +
"FROM t JOIN {1} /* and s jOOQ table */",
myField, myTable
).fetch();
--
You received this message because you are subscribed to the Google Groups "jOOQ
User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.