我最近在使用 jOOQ 解析包含带有别名的 Oracle DUAL 表的 SQL 查询时遇到了解析问题。这是重现该问题的查询的简化版本:
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 设置:
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;
}
为了提供更多上下文,以下是我使用的相关 jOOQ 设置:
有趣的是,当省略 DUAL 表的别名时,查询工作得非常好。但是,一旦在 DUAL 中添加类似 tab 的别名,解析就会失败,导致错误
relation "dual" does not exist
。
我期望jOOQ能够成功解析查询(oracle方言)并生成相应的jOOQ表示(PostgreSQL)。但是,我遇到了与 DUAL 表别名相关的解析错误。我尝试调整查询并删除别名,在这些情况下,jOOQ 能够毫无问题地解析查询。
我正在寻求有关为什么 jOOQ 难以使用别名解析 DUAL 以及如何解决或解决此问题的建议或见解。任何帮助、解决方案或建议将不胜感激。谢谢你。
从 jOOQ 3.19 开始,jOOQ 解析器当前仅识别单个、无别名的
DUAL
表,例如:
SELECT 1 FROM dual
它无法识别
DUAL
表达式中的别名 DUAL
表或 JOIN
表,或表列表,例如
SELECT 1 FROM dual a;
SELECT 1 FROM dual, dual;
我创建了一个问题来解决这个问题:
如果您直接使用
Select
API,例如,,您可以通过 查询对象模型 (
QOM
) API后处理已解析的
Parser
对象来解决此问题
// There are various ways to do this:
Query query = parser.parseQuery(sql);
if (query instanceof Select<?> s && s.$from().stream().anyMatch(...)) {
query = ...;
}
ParsingConnection
ParsingConnection
,给出问题中的示例,那么您可能可以实现 ParseListener
,它会拦截所有表的解析。通过这种方式从查询中删除 DUAL
表并不容易,但您至少可以将其转换为等效的内容,例如 (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 u")
) {
while (rs.next())
System.out.println(rs.getString(1));
}
您可以从日志中看到:
Translating from : select 1 from dual t, dual u
Translation cache miss : select 1 from dual t, dual u
Translating to : select 1 from (select 'DUMMY' "X") t, (select 'DUMMY' "X") u