CREATE USER 和 CREATE ROLE 作为 PreparedStatement with ?占位符

CREATE USER and CREATE ROLE as PreparedStatement with ? placeholder

我正在尝试以允许使用参数的方式从 Java 代码针对 HSQLDB 数据库发出 CREATE USER 语句。

然而,下面一行:

connection.prepareStatement("CREATE USER ? PASSWORD ?;");

抛出异常:

java.sql.SQLSyntaxErrorException: unexpected token: ? in statement [CREATE USER ? PASSWORD ?;]
    at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
    at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
    at org.hsqldb.jdbc.JDBCPreparedStatement.<init>(Unknown Source)
    at org.hsqldb.jdbc.JDBCConnection.prepareStatement(Unknown Source)
    at (somewhere in my code)

也是如此
connection.prepareStatement("CREATE ROLE ?;");

当然,我可以 assemble 我的字符串而不 ?,方法是将值直接粘贴到语句中,尽管这会打开一些 SQL 注入的可能性。

所以我想知道为什么占位符不起作用。这些声明完全支持它们吗?还是我漏掉了什么?

一般情况下,数据库只允许DML 中的参数,不允许DDL 中的参数。而在 DML 中,只允许 values。在 CREATE USERCREATE ROLE 的情况下,您不是在处理值(至少不是用户或角色名称),因此参数化对它们来说是不可能的。这类似于在 select 语句中不允许 table 名称或列名称的参数。

理论上,CREATE PASSWORD 中的密码之类的东西是一个值,可以参数化,但实际上这是不可能的(至少,我不知道),因为所有 DDL被处理为不可参数化。

作为针对 SQL 注入的一种次要保护形式,自 JDBC 4.3(在 Java 9 中引入)以来,您可以将 Statement.enquoteIdentifier to quote identifiers, and Statement.enquoteLiteral 用于密码等文字。这些方法有一个默认实现,但如果您使用的平台带有非标准标识符引号(例如 MySQL),那么您必须确保它确实被覆盖(当前版本不是这种情况) MySQL Connector/J AFAIK)。