Putting this in context a little… I’m dealing with a NoSQL store with a native ‘java.lang.Character’ type, that gets naturally mapped to CHAR(1). Unfortunately, this nuance of the EnumerableConvention behavior means prepared statements that interact with these columns behave differently depending on how much push-down is achieved on a query. The pushed-down query will have that value mapped into the native character type, whereas the enumerable code essentially treats the parameter as ‘dynamically typed’
Given that there may be bigger problems in this area would a PR to fix this class of CHAR(N) issues for dynamic parameters be welcomed, or would it be pushed out in the hopes of a more holistic approach in the future? From: Mihai Budiu <mbu...@gmail.com> Date: Wednesday, July 30, 2025 at 2:02 PM To: dev@calcite.apache.org <dev@calcite.apache.org> Subject: [EXTERNAL] Re: Dynamic parameter typing & code generation I am not sure this is only a problem with dynamic parameters; I haven't noticed any code that enforces CHAR(N) limits at runtime, but maybe I haven't looked carefully enough. There are certainly known problems related to CHAR(N): https://issues.apache.org/jira/browse/CALCITE-4493 Mihai ________________________________ From: Chris Dennis <chris.w.den...@ibm.com.INVALID> Sent: Tuesday, July 29, 2025 12:07 PM To: dev@calcite.apache.org <dev@calcite.apache.org> Subject: Dynamic parameter typing & code generation I’m trying to figure out if the following test represents an error in the rel graph generation, the enumerable code generation, or just a grey area in the SQL spec/Calcite implementation: @Test void bindCharParameter() { final String sql = "select * from (values ('a'), ('b')) as tab (foo) where foo = ?"; CalciteAssert.that() .query(sql) .convertMatches(root -> { RelNodes.findRex(root, RexUtil.find(SqlKind.EQUALS), null, (rel, rex) -> { ((RexCall) ((Filter) rel).getCondition()).getOperands().forEach(o -> { assertThat(o.getType().getPrecision(), is(1)); }); }); }) .consumesPreparedStatement(p -> { p.setString(1, "a"); }) .returnsUnordered("FOO=a") .consumesPreparedStatement(p -> { p.setString(1, "aa"); }) .returnsUnordered(""); } In the test above the Rel graph generated from the query types both the column ‘foo’ and the RelDynamicParam as CHAR(1). The code generated by Calcite however treats the values passed through the prepared statement as simple Java Strings of arbitrary length. This means the second invocation returns no rows. Is this correct? Would it also be correct for an implementation of the query to truncate the value of the dynamic parameter to one character before performing the comparison, and thereby return a non-empty result set? Thanks, Chris