如果有一种方法可以使用 JDBC 在语法和语义上编译(而不是执行)查询?

If there a way to compile (not execute) a query syntaxicly and semanticly with JDBC?

我有很多包含语法错误的查询(没有单元测试,但这是另一个问题),我想大量检查是否没有错误。

为此,我首先做了以下工作:

String q = ...; // some query
try (PreparedStatement stmt = connection.prepareStatement(q)) {
  final ParameterMetaData pmd = stmt.getParameterMetaData();
  for (int i = 1; i <= pmd.getParameterCount(); ++i) {
    stmt.setNull(i, java.sql.Types.NULL);
  }
  stmt.execute();
} catch (SQLException e) {
  ...
} finally {
  connection.rollback();
}

它有效,但后来我遇到了这样的错误:http://www.oracle-error.com/11g/ORA-30081.html

基本上,在我查询的某处,我有:

select * 
from   table T
where  id = ? or ( ? - INTERVAL '1' DAY ) between date_start and date_end

如果我执行相同的查询,将 ? 替换为 NULL,在 TOAD 中,我会遇到同样的错误。

ParameterMetaData 也没有帮助,因为它不存储我想要的信息(例如:Oracle 期望的参数)。

是否有一些解决方案可以在语法和语义上编译查询(检查是否缺少列等)并忽略参数?

截至目前,我将 ? 替换为 NULL,除非在“?”之后我发现了一些约会的东西,我用 sysdate.

例如:

select * 
from   table T
where  id = NULL or ( sysdate - INTERVAL '1' DAY ) between date_start and date_end

不是直接通过JDBC,而是可以间接的; heavily inspired by this,你可以这样做:

String q = ...; // some query
try (PreparedStatement stmt = connection.prepareStatement("declare c integer;
    begin
      c := dbms_sql.open_cursor;
      dbms_sql.parse(c,?,dbms_sql.native);
      dbms_sql.close_cursor(c);
    end;")) {
  stmt.setString(1, q.replace("?", ":b0"));
  stmt.execute();
} catch (SQLException e) {
  ...
}

您准备的语句现在是一个匿名块,单个绑定变量现在是您要验证的原始查询。您不需要了解有关查询参数的任何信息。 replace 将 JDBC ? 占位符转换为通用 :b0 绑定变量,因此解析器不会反对它们。

您可以更高级,将每个占位符替换为不同的绑定变量(:b0:b1)等,但我认为通常没有必要这样做。当然,这种粗略的替换也可能会修改字符串文字,这可能是您需要考虑的事情;正则表达式方法会更健壮。

另一种尝试方法可能是使用 Oracle 和其他一些 DBMS 中可用的 EXPLAIN PLAN 语句(形式可能略有不同)。在你的声明前添加 'EXPLAIN PLAN FOR 'execute()(无需准备)。原来的语句实际上不会运行,但是会被解析编译,不需要绑定任何参数

Proof.

但在某些情况下,它可能仍会因未类型化的参数标记而阻塞。