If I declare a user defined java expression (which uses janino) in Kettle like so:
new java.util.Date(agent_start_time.getTime())
(Where agent_start_time is defined as a Timestamp)
I get this error:
2015/07/23 16:25:10 - [test-timestamp].User Defined Java Expression.0 - Caused by: org.codehaus.janino.CompileException: Line 1, Column 44: A method named "getTime" is not declared in any enclosing class nor any supertype, nor through a static import
Which is strange as the doco states clearly that Timestamps support getTime just like java.util.Dates do (Albeit at a different granularity)
Looks like the User-Defined Java Expression step doesn't support Timestamp parameters (bug PDI-14347).
You can cast it to a Date, via a Select Values step, which appears to resolve the issue in my testing.
Related
I want to get Row[N]<...> representation of a generated JOOQ table type. I want to use it in this context:
val p = PROJECTS.`as`("p")
val pmu = PROJECTMEMBERUSERS.`as`("pmu")
val query = db
.select(p.asterisk(), DSL.arrayAgg(DSL.rowField(<-- insert Row[N]<...> here -->)))
.from(p.join(pmu).on(p.ID.eq(pmu.PROJECTID)))
.groupBy(p.ID)
I already tried inserting pmu.fieldsRow(), but DSL.rowField(...) expects another parameter type.
Error:(39, 58) Kotlin: None of the following functions can be called with the arguments supplied [...]
This question is a follow up question to Using PosgreSQL array_agg with join alias in JOOQ DSL but should be self contained.
Missing feature in jOOQ 3.11
There seems to be a missing feature in the jOOQ code generator, a generated Table.fieldsRow() overridden method that provides a more narrow, covariant Row[N]<...> return type. I've created a feature request for this, to be implemented in jOOQ 3.12:
https://github.com/jOOQ/jOOQ/issues/7809
Also missing, an overloaded DSL.rowField(RowN) method:
https://github.com/jOOQ/jOOQ/issues/7810
Workaround, list columns explicitly
This is the most obvious workaround, which you obviously want to avoid: Listing all the column names explicitly:
row(pmu.COL1, pmu.COL2, ..., pmu.COLN)
Workaround, use generated records
There already is such a generated method in generated records. As a workaround, you could use
new ProjectMembersUsersRecord().fieldsRow();
Workaround, extend the code generator
You can implement #7809 yourself already now, by extending the JavaGenerator with a custom code section:
https://www.jooq.org/doc/latest/manual/code-generation/codegen-custom-code
In this simple code example...
fun testLocalFunctions() {
aLocalFun() //compiler error: unresolved reference at aLocalFun
fun aLocalFun() {}
aLocalFun() //no error
}
Elsewhere in the language, using a function before definition is allowed. But for local functions, that does not appear to be the case. Refering to the Kotlin Language Specification, the section on Local Functions is still marked "TODO".
Since this sort of constraint does not hold for other types of functions (top-level and member functions), is this a bug?
(Granted, local variable declarations must occur before use, so the same constraint on local functions is not unreasonable. Is there a definitive, preferably authoritative source document that discusses this behavior?)
It's not a bug, it is the designed behavior.
When you use a symbol (variable, type or function name) in an expression, the symbol is resolved against some scope. If we simplify the scheme, the scope is formed by the package, the imports, the outer declarations (e.g. other members of the type) and, if the expression is placed inside a function, the scope also includes the local declarations that precede the expression.
So, you can't use a local function until it's declared just like you cannot use a local variable that is not declared up to that point: it's just out of scope.
I am filtering PrimeFaces DataTables using dynamic filters.I have this working using Spring org.springframework.data.jpa.domain.Specification.Now I am wondring how to do the same using QueryDSL.
Using specification I can use javax.persistence.criteria.Root to get a javax.persistence.criteria.Join, use javax.persistence.criteria.Expression.as(Class<String> type) to cast it to String and finally use javax.persistence.criteria.CriteriaBuilder.like(Expression<String> x, String pattern, char escapeChar).
How do I do the same in QueryDSL ? I can get PathBuilder using new PathBuilder<T>(clazz, "entity") (do you really have to use the variable here? I would like my class to be generic...) but then the com.mysema.query.types.path.PathBuilder.get(String property) returns new PathBuilder instead of an Expression.
If I try to use com.mysema.query.types.path.PathBuilder.getString(String property) I get java.lang.IllegalArgumentException: Parameter value [1] did not match expected type [java.lang.Integer].
Seems like the part I am missing is the cast.
I'm quite sure someone was dealing with the same thing already.
Thanks.
Edit: Stack trace for IllegalArgumentException
Trying to search for text "1" inside integer column using com.mysema.query.types.path.PathBuilder.getString(String property) - that's where I need the cast to happen :
Caused by: java.lang.IllegalArgumentException: Parameter value [1] did not match expected type [java.lang.Integer]
at org.hibernate.ejb.AbstractQueryImpl.validateParameterBinding(AbstractQueryImpl.java:375)
at org.hibernate.ejb.AbstractQueryImpl.registerParameterBinding(AbstractQueryImpl.java:348)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:375)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:442)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:72)
at com.mysema.query.jpa.impl.JPAUtil.setConstants(JPAUtil.java:44)
at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:130)
at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:97)
at com.mysema.query.jpa.impl.AbstractJPAQuery.list(AbstractJPAQuery.java:240)
at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.findAll(QueryDslJpaRepository.java:102)
...
To get a valid condition you will need to take the types of the properties into account, e.g.
pathBuilder.getNumber(Integer.class, property).stringValue().like(likePattern)
I was researching a similar topic until I came across this aged question. I hope my answer will be helpful to some.
I think the possible solution lies (exclusively) outside of QueryDSL. You can use common Java reflection to obtain the field type, something like
Class type = clazz.getDeclaredField(criteria.getKey()).getType();
// don't forget to catch exception if field doesn't exist ..
switch(type.getSimpleName()) {
case "String":
StringExpression exp = entityPath.getString(...);
}
That way you can have a reasonably dynamic implementation.
I don't own the SP, but it returns a dataset with columns with names like:
FileID, SomethingNormal, Foo, Bar, SUM(bleh)
SUM(bleh) is unnamed. I add the stored proc using the dbml tool in VS2010 and it detects SUM(bleh) as Column1 and gives it an int type, which sounds alright.
However, when trying to run, the moment it tries to get the IEnumerable, it breaks by giving an error:
Specified cast is not valid.
Unhandled Exception: System.InvalidCastException: Specified cast is
not valid. at System.Data.SqlClient.SqlBuffer.get_Int32() at
System.Data.SqlClient.SqlDataReader.GetInt32(Int32 i) at
Read_RejectedRecordsHourlyBySubscriptionResult(ObjectMaterializer1 )
at
System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader2.MoveNext()
That happens the moment I try to iterate or if I get the enumerate manually.
In a dev environment I did SUM(bleh) as TotalBleh (rest assured all this names are fake >_>) and I had no issues with what I've done.
Anything I should know? Thanks!
EDIT:
This appears to be a bug with Linq-to-SQL. Strangely, the bug report says it supports -ONE- anonymous column (although no more) and in this case there is only one.
I will do it with basic SqlCommand and stuff. So much for commodity.
Consider the following line of code:
private void DoThis() {
int i = 5;
var repo = new ReportsRepository<RptCriteriaHint>();
// This does NOT work
var query1 = repo.Find(x => x.CriteriaTypeID == i).ToList<RptCriteriaHint>();
// This DOES work
var query1 = repo.Find(x => x.CriteriaTypeID == 5).ToList<RptCriteriaHint>();
}
So when I hardwire an actual number into the lambda function, it works fine. When I use a captured variable into the expression it comes back with the following error:
No mapping exists from object type
ReportBuilder.Reporter+<>c__DisplayClass0
to a known managed provider native
type.
Why? How can I fix it?
Technically, the correct way to fix this is for the framework that is accepting the expression tree from your lambda to evaluate the i reference; in other words, it's a LINQ framework limitation for some specific framework. What it is currently trying to do is interpret the i as a member access on some type known to it (the provider) from the database. Because of the way lambda variable capture works, the i local variable is actually a field on a hidden class, the one with the funny name, that the provider doesn't recognize.
So, it's a framework problem.
If you really must get by, you could construct the expression manually, like this:
ParameterExpression x = Expression.Parameter(typeof(RptCriteriaHint), "x");
var query = repo.Find(
Expression.Lambda<Func<RptCriteriaHint,bool>>(
Expression.Equal(
Expression.MakeMemberAccess(
x,
typeof(RptCriteriaHint).GetProperty("CriteriaTypeID")),
Expression.Constant(i)),
x)).ToList();
... but that's just masochism.
Your comment on this entry prompts me to explain further.
Lambdas are convertible into one of two types: a delegate with the correct signature, or an Expression<TDelegate> of the correct signature. LINQ to external databases (as opposed to any kind of in-memory query) works using the second kind of conversion.
The compiler converts lambda expressions into expression trees, roughly speaking, by:
The syntax tree is parsed by the compiler - this happens for all code.
The syntax tree is rewritten after taking into account variable capture. Capturing variables is just like in a normal delegate or lambda - so display classes get created, and captured locals get moved into them (this is the same behaviour as variable capture in C# 2.0 anonymous delegates).
The new syntax tree is converted into a series of calls to the Expression class so that, at runtime, an object tree is created that faithfully represents the parsed text.
LINQ to external data sources is supposed to take this expression tree and interpret it for its semantic content, and interpret symbolic expressions inside the tree as either referring to things specific to its context (e.g. columns in the DB), or immediate values to convert. Usually, System.Reflection is used to look for framework-specific attributes to guide this conversion.
However, it looks like SubSonic is not properly treating symbolic references that it cannot find domain-specific correspondences for; rather than evaluating the symbolic references, it's just punting. Thus, it's a SubSonic problem.