You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

H2兼容DB2 STRIP函数UDF时参数B被识别为列的报错问题

H2适配DB2 STRIP函数问题解决方案

环境信息

  • Java 8 JDK 321 64位
  • 生产数据库:DB2 12,DSN 12015
  • 单元测试数据库:H2 2.1.212,连接配置参数为MODE=DB2;DEFAULT_NULL_ORDERING=HIGH;

问题现象

在单元测试环境通过自定义UDF做DB2到H2的函数适配时,需要兼容DB2的STRIP函数,业务SQL中固定调用写法为STRIP(T2.ITEM_TYPE_NAME_GER, B, ' ')。已实现可接收第一、第三个入参的Java映射方法,可配置为H2 ALIAS调用,但第二个入参是不带引号的B/L/T等关键字,始终被H2引擎识别为表列,抛出列不存在的异常:

org.h2.jdbc.JdbcSQLSyntaxErrorException: Feld "B" nicht gefunden
Column "B" not found; SQL statement:
SELECT T1.ITEM_TYPE_KEY,T1.SUPER_ITM_TYPE_KEY,T2.ITM_TYPE_KEY_TRANS,T2.ITEM_TYPE_NAME,T2.COMPLEX_FLAG,T2.ITEM_CATEGORY,T2.HEADER_FLAG,T2.HEADER_NO ,T2.LEVEL_NO,strip(T2.ITEM_TYPE_NAME_GER, B, ' ') ,T2.LEVEL1_DISPLAY FROM      public.AA752T      T1,   public.AA743T            T2 WHERE          T1.ITEM_TYPE_KEY NOT IN ('F4CO', 'F4CB', 'F4RB', 'F4SO', 'F4SB', 'F4RO') AND T1.ITEM_TYPE_KEY = T2.ITEM_TYPE_KEY ORDER BY T2.HEADER_NO,T2.HEADER_FLAG DESC,T2.LEVEL_NO,T1.SUPER_ITM_TYPE_KEY,T2.LEVEL_PRIORITY [42122-212]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:502)
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:477)
    at org.h2.message.DbException.get(DbException.java:223)
    at org.h2.message.DbException.get(DbException.java:199)
    at org.h2.expression.ExpressionColumn.getColumnException(ExpressionColumn.java:244)
    at org.h2.expression.ExpressionColumn.optimizeOther(ExpressionColumn.java:226)
    at org.h2.expression.ExpressionColumn.optimize(ExpressionColumn.java:213)
    at org.h2.expression.function.JavaFunction.optimize(JavaFunction.java:59)
    at org.h2.command.query.Select.prepareExpressions(Select.java:1170)
    at org.h2.command.query.Query.prepare(Query.java:218)
    at org.h2.command.Parser.prepareCommand(Parser.java:574)
    at org.h2.engine.SessionLocal.prepareLocal(SessionLocal.java:631)
    at org.h2.engine.SessionLocal.prepareCommand(SessionLocal.java:554)
    at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1116)
    at org.h2.jdbc.JdbcPreparedStatement.(JdbcPreparedStatement.java:92)
    at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:288)
    at com.db.cib.gbd.gps.pbs.pricing.StaticItemDetails.retriveItemDisplayDetails(StaticItemDetails.java:920) 

原有UDF实现存在三个明显问题:

  1. 第二个参数使用H2内部类Expression作为类型,UDF应使用标准Java类型做参数,避免内部API版本兼容性问题
  2. Java字符串比较使用==而非equals,会出现逻辑判断失效
  3. BOTH分支的尾部裁剪规则写死匹配空格制表符,未使用传入的裁剪字符参数,不符合DB2原生行为
    原有错误代码如下:
public static String strip(String s, Expression loc, String trimConstant) {
        
    if (loc.toLowerCase() == "b" || loc.toLowerCase() == "both") {
        s = s.replaceAll("^[" + trimConstant + "]+|[ \t]+$", "");
    } else if (loc.toLowerCase() == "l" || loc.toLowerCase() == "leading") {
        s = s.replaceAll("^[" + trimConstant + "]+", "");
    } else if (loc.toLowerCase() == "t" || loc.toLowerCase() == "trailing") {
        s = s.replaceAll("[" + trimConstant + "]+$", "");
    }
    return s;
}

约束条件:不能修改任何业务SQL,必须通过函数别名配置的方式实现兼容。

可行解决方案

方案1:为位置关键字定义无参常量别名(实现成本最低)

H2支持无参ALIAS直接以函数名调用、不需要加括号,利用这个特性,在单元测试数据源初始化阶段(建表之后、执行业务SQL之前),先执行以下SQL把裸写的B/L/T/BOTH/LEADING/TRAILING关键字定义为返回对应字符串的无参函数,H2解析到这些标识符时会优先匹配别名,不会再去查找表列:

-- 定义位置关键字常量别名
CREATE ALIAS IF NOT EXISTS B AS $$ String b() { return "B"; } $$;
CREATE ALIAS IF NOT EXISTS L AS $$ String l() { return "L"; } $$;
CREATE ALIAS IF NOT EXISTS T AS $$ String t() { return "T"; } $$;
CREATE ALIAS IF NOT EXISTS BOTH AS $$ String both() { return "BOTH"; } $$;
CREATE ALIAS IF NOT EXISTS LEADING AS $$ String leading() { return "LEADING"; } $$;
CREATE ALIAS IF NOT EXISTS TRAILING AS $$ String trailing() { return "TRAILING"; } $$;

-- 注册修正后的STRIP函数
DROP ALIAS IF EXISTS STRIP;
CREATE ALIAS STRIP FOR "你自己的类全限定名.strip";

同时修正UDF的Java实现,全部使用标准Java类型,修复原有逻辑bug:

import java.util.regex.Pattern;

public class H2Udf {
    public static String strip(String s, String loc, String trimConstant) {
        // 空值快速返回,和DB2行为对齐
        if (s == null || loc == null || trimConstant == null) {
            return s;
        }
        String locFlag = loc.toLowerCase();
        // 转义裁剪字符,避免正则特殊字符导致匹配异常
        String trimPattern = Pattern.quote(trimConstant);
        if ("b".equals(locFlag) || "both".equals(locFlag)) {
            return s.replaceAll("^" + trimPattern + "+|" + trimPattern + "+$", "");
        } else if ("l".equals(locFlag) || "leading".equals(locFlag)) {
            return s.replaceAll("^" + trimPattern + "+", "");
        } else if ("t".equals(locFlag) || "trailing".equals(locFlag)) {
            return s.replaceAll(trimPattern + "+$", "");
        }
        // 非法位置参数直接返回原字符串
        return s;
    }
}

这个方案不需要修改H2内核,所有逻辑都通过标准ALIAS配置实现,适配工作量最小,适合单元测试场景使用。

方案2:自定义H2解析器规则(稳定性更高)

如果方案1的别名和业务表字段存在命名冲突,可以在H2初始化时通过自定义Parser实现,在词法解析阶段把裸的B/L/T/BOTH/LEADING/TRAILING直接转为字符串字面量,不需要单独定义函数别名。这个方案需要依赖H2的内部Parser API,适合对H2适配层有统一封装的场景,不会存在别名冲突问题。

内容的提问来源于stack exchange,提问作者christian s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 10:21:23