jdbcparametersprepared-statement

CREATE USER and CREATE ROLE as PreparedStatement with ? placeholder


I am trying to issue a CREATE USER statement against a HSQLDB database from Java code in a way that allows the use of parameters.

However, the following line:

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

throws an exception:

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)

The same happens for

connection.prepareStatement("CREATE ROLE ?;");

Of course, I could assemble my string without ?, by pasting the values directly into the statement, though that would open up some potential for SQL injection.

So I am wondering why placeholders are not working. Are they supported in these statements at all? Or am I missing something else?


Solution

  • In general, databases only allow parameters in DML, not in DDL. And in DML, it is only allowed for values. In the case of CREATE USER and CREATE ROLE, you aren't dealing with values but identifiers (at least not for the user or role name), so parameterization is not possible for those. This is similar to not allowing parameters for the table name or column name in a select statement.

    In theory, something like the password in the PASSWORD clause is a value and could be parameterized, but in practice this isn't possible (at least, not that I'm aware of), as all DDL is handled as non-parameterizable.

    As a minor form of protection against SQL injection, since JDBC 4.3 (introduced in Java 9), you can use Statement.enquoteIdentifier to quote identifiers, and Statement.enquoteLiteral for literals like passwords. These methods have a default implementation, but if you're using a platform with non-standard identifier quotes (e.g. like MySQL), then you must make sure it is actually overridden (which isn't the case in current versions of MySQL Connector/J AFAIK).