jOOQ - support for UPDATE ... SET ... query with arbitrary degree - sql

I have two functions: one returns a list of fields, the other returns a select query (which selects the corresponding values of the fields).
private List<Field<?>> fields() {
....
}
private Select<?> select() {
...
}
Note that the degree is determined at runtime, it depends on the user input. Hence List<Field<?>> and Select<?>.
It is possible to insert into a table:
context.insertInto(table, fields()).select(select()))
It is not possible to update a table:
context.update(table).set(DSL.row(fields()), select())
Could this functionality be added to jOOQ 3.7?
Which workaround can we use for now?

Nice catch, there's a missing method on the UpdateSetFirstStep DSL API, which accepts RowN as a first argument, the type returned from DSL.row(Collection). This should be fixed for jOOQ 3.7:
https://github.com/jOOQ/jOOQ/issues/4475
As a workaround, and if you can live with the guilt of the hack, you could cast to raw types:
context.update(table).set((Row1) DSL.row(fields()), (Select) select())
You can cast DSL.row(fields()) to Row1, because the internal implementation type returned by DSL.row(fields()) implements all Row[N] types.

Related

How can I perform a regex/text search on a SUPER type?

What I'm doing now:
I have a table with one field that is a json value that is stored as a super type in my staging schema.
the field containing the json is called elements
In my clean table, I typecast this field to VARCHAR in order to search it and use string functions
I want to search for the string net within that json in order to determine the key/value that I want to use for my filter
I tried the following:
select
elements
, elements_raw
from clean.events
where 1=1
and lower(elements) like '%net%'
or strpos(elements,'net')
My output
When running the above query, I keep getting an empty set returned.
My issue
I tried running the above code and using the elements_raw value instead but I got an issue :ERROR: function strpos(super, "unknown") does not exist Hint: No function matches the given name and argument types. You may need to add explicit type casts.
I checked the redshift super page and it doesn't list any specifics on searching strings within super types
Desired result:
Perform string operations on super field
Cast super field to a string type
There are some super related idiosyncrasies that are being run into here:
You cannot change the type of a super field via :: or cast()
String functions like and strpos do not work on super types
To address both of these issues, you can use the function json_serialize to return your super as a string.

How to get a Row representation of a generated table?

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

Difference between using GetterUtils and ParamUtils

For instance, when to use
GetterUtil.getBoolean()
and when
ParamUtil.getBoolean()?
Are both same, or is it expected to be used differently according to a parameter, a variable, etc? Can you give some examples for both?
Both are util methods to avoid Null-Pointer Exceptions.
GetterUtil internally returns the default type and does the casting too. So in case where someone has passed a null value, it will return default value of the type.
Example:
Assume you have a String value "true", and you are expecting it will always be of type boolean. So you use GetterUtil.getBoolean("true") which will internally do the casting to boolen and return the value as boolean-true. Incase someone passes rubbish characters like "tr", it will be converted to boolean-false.
As mentioned ParamUtil does the same treatment with request parameters. ParamUtil internally uses the GetterUtil to have the above behaviour. It first retrieves the parameter (which always would be a string) and then passes it to GetterUtil.getType() method and in turn returns the proper type.
GetterUtil and ParmUtil both are different classes.
GetterUtil is to get the default values for basic Java data types.
ParamUtil is to retrive the values(of primitive data types) from the HttpReqeust.
Check the source code here for these two classes here
For GetterUtil
http://docs.liferay.com/portal/6.0/javadocs/src-html/com/liferay/portal/kernel/util/GetterUtil.html
For ParamUtil
http://docs.liferay.com/portal/5.1/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.java.html

query runs fine in sql developer, but crashes in hibernate

When I run this in sqldeveloper:
SELECT CASE
WHEN TABLE1.COL1 IS NOT NULL
THEN (
CASE
WHEN TABLE1.COL2 IS NOT NULL
THEN TABLE1.COL3
ELSE TABLE1.COL4
END)
WHEN TABLE1.COL5 IS NOT NULL
THEN TABLE1.COL6
ELSE TABLE1.COL7 END "C" FROM TABLE1
it runs fine.
When I run this through a Hibernate session, it gives:
No Dialect mapping for JDBC type: -101
All cols are of type varchar2. col1 is of type number.
The database is oracle 10g.
A workaround is also welcome, if a solution is not obvious :)
Also, how can I find out what is the JDBC type -101 referring to. I have seen -1 but not -101 before.
EDIT: I tried using a return scalar on top of the named query No luck.
EDIT#2: Is there a way to see what is the datatype returned for "C". Then I use one of the solutions provided in the answer.
This error comes when we pass a parameter through query rather than putting the value directly. In case of SQL you can regenerate the same by giving value as C= :S and entering the value in the new textbox generated. Solution to this problem comes in an upgrade patch as this is a bug.
I've never faced it on Oracle, but sometimes JDBC returns strange types for complex expressions in queries, and Hibernate can't resolve these types.
In such cases you can try to add explicit cast to the query:
SELECT CAST((CASE ... END) AS varchar2) "C" FROM TABLE1
if axtavt's answer doesn't work you can define your own type mapping following this pattern:
public class MySQL5Dialect extends org.hibernate.dialect.MySQL5Dialect
{
public MySQL5Dialect()
{
super(); // register additional hibernate types for default use in scalar sqlquery type auto detection
registerHibernateType(Types.LONGVARCHAR, Hibernate.TEXT.getName());
}
}
Of course you have to use the value you are getting instead ov longvarchar and the apropriate Hiberante types.
(I nicked the code sample here: http://opensource.atlassian.com/projects/hibernate/browse/HHH-1483

Lambdas with captured variables

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.