JOOQ 和 Firebird - 超出实施限制

JOOQ & Firebird - Implementation Limit Exceeded

背景:我正在使用 jOOQ 访问 Firebird 数据库。 Firebird 2.x 的行大小限制为 64KB。我以前从未达到过限制,但是这个特定的数据库使用 UTF8,这意味着限制缩小到大约 16K 个字符。

这就是我使用 jOOQ 的方式:

  1. 根据需要加载或创建 POJO(已生成)。例如:

    Book book = context.fetchOne("select * from book where book_id = ?",  1).into(Book.class);
    
  2. 根据需要使用书籍对象。

  3. 如果用户保存更改,将其作为记录存回。

    BookRecord rec = context.newRecord(Tables.BOOK, book); 
    context.executeUpdate(rec);
    

步骤 3 在 executeUpdate() 方法上失败,因为 jOOQ 以某种方式将所有空的 VARCHAR 字段转换为 VARCHAR(4000)。错误信息是

"SQL error code = -204. Implementation limit exceeded. block size exceeds implementation restriction".

这是一个已知的 Firebird 限制,不幸的是,我们对此无能为力。

在 table 我有大约 8 个空的 VARCHAR(512) 字段,在 UTF8 中应该是最大 8x4x512(或 16KB),但由于 jOOQ 将其解释为 VARCHAR(4000) ,那是 8x4x4000 (128KB),这显然超出了限制。

如果我将 NULL 或空字段设置为某个随机字符串,那么 jOOQ 会将其转换为准确的字符串长度。 ("ABC" 将转换为 varchar(3)

所以我的问题是:如何让 executeUpdate() 在没有 jOOQ 将我的空字段转换为 VARCHAR(4000) 的情况下工作?

这些转换在 jOOQ 中是历史性的,因为一些数据库很难单独从绑定变量推断数据类型,因为它们不能延迟任何决定,直到查询被执行:

SELECT ? -- What's the type? Unknown at parse time.

这就是 jOOQ 为这些数据库生成显式转换的原因,其中包括 Firebird:

SELECT cast(? as varchar(4000)) -- Now, the type is clear

目前甚至在语句/子句中也这样做,其中类型可以从上下文中推断出来,包括

INSERT INTO t(a, b) VALUES (?, ?)
--            ^  ^          |  |
--            +--|----------+  |
--               +-------------+ Bind types can be inferred from column type

在 Firebird 的情况下,不幸的是这种做法 运行 很快就进入了上述大小限制。在 jOOQ 中有一些未决的功能请求来改进这一点,但尚未在 jOOQ 3.9 中实现:

解决方法

如果你想在你这边修补 jOOQ,演员决定在 DefaultBinding.sql(BindingSQLContext) 中做出。你会看到有几个选项如何覆盖当前行为:

  1. 使用 Setting.paramCastMode == NEVER 关闭投射(可从 jOOQ 3.10 使用 #1735
  2. 重写 RenderContext.castMode() 以始终产生 NEVER
  3. 在您自己的自定义绑定中覆盖整个方法
  4. 修补逻辑以从不应用任何强制转换
  5. 实施 ExecuteListener,在您的 SQL 字符串中用 ? 替换 cast\(\?[^)]*\)