I recently encountered a parsing issue while using jOOQ to parse SQL queries containing Oracle's DUAL table with an alias. Here's a simplified version of the query that reproduces the issue:
private Pair<String, String> getPerEchDate(Connection dbConn) throws Exception {
String query="select 'dtPrEch' as lib , to_char(add_months(sysdate,12),'dd/mm/yyyy') as val from dual tab";
Pair<String, String> rtn =null;
try (final PreparedStatement pStmt = dbConn.prepareStatement(query); ResultSet rs= pStmt.executeQuery()) {
while(rs.next()){
rtn= Pair.of(rs.getString(1), rs.getString(2));
}
return rtn;
} catch (SQLException | ParserException e1) {
throw new Exception(e1);
}
}
jOOQ Settings:
private Settings createSettings() {
Settings settings = new Settings()
.withParseDialect(SQLDialect.ORACLE)
.withParseUnknownFunctions(ParseUnknownFunctions.IGNORE)
.withTransformTableListsToAnsiJoin(true) // transform (+) to left outer join
.withTransformUnneededArithmeticExpressions(TransformUnneededArithmeticExpressions.ALWAYS)
.withTransformRownum(Transformation.ALWAYS)
.withParamType(ParamType.INLINED)
.withParamCastMode(ParamCastMode.DEFAULT)
.withRenderOptionalAsKeywordForFieldAliases(RenderOptionalKeyword.ON)
.withRenderOptionalAsKeywordForTableAliases(RenderOptionalKeyword.ON)
.withRenderQuotedNames(RenderQuotedNames.EXPLICIT_DEFAULT_UNQUOTED)
.withRenderNameCase(RenderNameCase.UPPER)
// Oracle reads empty strings as NULLs, while PostgreSQL treats them as empty.
// Concatenating NULL values with non-NULL characters results in that character in Oracle, but NULL in PostgreSQL.
// So this parameter whenever there's a concat it applies coalesce(x ,'')
.withRenderCoalesceToEmptyStringInConcat(true);
return settings;
}
To provide more context, here are the relevant jOOQ settings I used:
Interestingly, the query works perfectly fine when the alias for the DUAL table is omitted. However, once an alias like tab is added to DUAL, the parsing fails, resulting in errors relation "dual" does not exist
.
I expected jOOQ to successfully parse the query(oracle dialect) and generate the corresponding jOOQ representation(PostgreSQL). However, I encountered a parsing error related to the DUAL table alias. I tried adjusting the query and removing the alias, and in those cases, jOOQ was able to parse the query without any issues.
I'm seeking suggestions or insights on why jOOQ struggles to parse DUAL with an alias and how to resolve or work around this issue. Any assistance, solutions, or recommendations would be greatly appreciated. Thank you.
As of jOOQ 3.19, the jOOQ parser currently only recognises single, unaliased DUAL
tables, such as:
SELECT 1 FROM dual
It doesn't recognise aliased DUAL
tables or DUAL
tables in JOIN
expressions, or table lists, e.g.
SELECT 1 FROM dual a;
SELECT 1 FROM dual, dual;
I've created an issue to address this:
You can work around this by post-processing your parsed Select
object via the Query Object Model (QOM
) API, if you're using the Parser
API directly, e.g.
// There are various ways to do this:
Query query = parser.parseQuery(sql);
if (query instanceof Select<?> s && s.$from().stream().anyMatch(...)) {
query = ...;
}
ParsingConnection
If you're using the ParsingConnection
, given your example from your question, then you can probably implement a ParseListener
, which intercepts all parsings of tables. It's not easy to remove the DUAL
table from the query this way, but you can at least transform it to something equivalent, such as (SELECT 'X' DUMMY) AS DUAL
DSLContext ctx = DSL.using(connection);
Configuration configuration = ctx.configuration();
configuration.set(ParseListener.onParseTable(c -> {
if (c.parseKeywordIf("DUAL"))
return select(DSL.inline("DUMMY").as("X")).asTable("DUAL");
return null;
}));
try (Connection c = ctx.parsingConnection();
Statement s = c.createStatement();
ResultSet rs = s.executeQuery("select 1 from dual t, dual")
) {
while (rs.next())
System.out.println(rs.getString(1));
}
As you can then see from the logs:
Translating from : select 1 from dual t, dual
Translating to : select 1 from (select 'DUMMY' "X") t, (select 'DUMMY' "X") "DUAL"