在 Oracle 的 DUAL 表上使用别名时 jOOQ 的解析问题

问题描述 投票:0回答:1

我最近在使用 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 设置:

  1. 数据库配置:
    数据库源方言:Oracle
    目标方言:PostgreSQL
  2. jOOQ版本:3.19.6
  3. jOOQ 版本: 开源。

有趣的是,当省略 DUAL 表的别名时,查询工作得非常好。但是,一旦在 DUAL 中添加类似 tab 的别名,解析就会失败,导致错误

relation "dual" does not exist

我期望jOOQ能够成功解析查询(oracle方言)并生成相应的jOOQ表示(PostgreSQL)。但是,我遇到了与 DUAL 表别名相关的解析错误。我尝试调整查询并删除别名,在这些情况下,jOOQ 能够毫无问题地解析查询。

我正在寻求有关为什么 jOOQ 难以使用别名解析 DUAL 以及如何解决或解决此问题的建议或见解。任何帮助、解决方案或建议将不胜感激。谢谢你。

java sql jooq sql-parser
1个回答
0
投票

从 jOOQ 3.19 开始,jOOQ 解析器当前仅识别单个、无别名的

DUAL
表,例如:

SELECT 1 FROM dual

它无法识别

DUAL
表达式中的别名
DUAL
表或
JOIN
表,或表列表,例如

SELECT 1 FROM dual a;
SELECT 1 FROM dual, dual;

我创建了一个问题来解决这个问题:

使用解析器 API 时的解决方法

如果您直接使用

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
© www.soinside.com 2019 - 2024. All rights reserved.