Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix determination of UNNEST data types for array constructors with casted parameters #4034

Merged
merged 1 commit into from Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions h2/src/docsrc/html/changelog.html
Expand Up @@ -21,6 +21,11 @@ <h1>Change Log</h1>

<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #4033: Wrong array produced when using ARRAY_AGG() on UNNEST(ARRAY[CAST(? AS INT)]) expression
in a PreparedStatement
</li>
<li>Issue #3909: Maintenance taking too much resources since 2.2.222
</li>
<li>Issue #4010: org.h2.jdbc.JdbcConnection.getTypeMap() returns null
</li>
<li>PR #4007: Update pom.xml related to CVE-2024-1597
Expand Down
8 changes: 2 additions & 6 deletions h2/src/main/org/h2/command/Parser.java
Expand Up @@ -4414,12 +4414,8 @@ private ArrayTableFunction readUnnestFunction() {
do {
Expression expr = readExpression();
TypeInfo columnType = TypeInfo.TYPE_NULL;
boolean constant = expr.isConstant();
if (constant || expr instanceof CastSpecification) {
if (constant) {
expr = expr.optimize(session);
}
TypeInfo exprType = expr.getType();
TypeInfo exprType = expr.getTypeIfStaticallyKnown(session);
if (exprType != null) {
switch (exprType.getValueType()) {
case Value.JSON:
columnType = TypeInfo.TYPE_JSON;
Expand Down
11 changes: 11 additions & 0 deletions h2/src/main/org/h2/expression/Expression.java
Expand Up @@ -284,6 +284,17 @@ public Expression getNotIfPossible(@SuppressWarnings("unused") SessionLocal sess
return null;
}

/**
* Returns data type of this expression if it is statically known.
*
* @param session
* the session
* @return data type or {@code null}
*/
public TypeInfo getTypeIfStaticallyKnown(SessionLocal session) {
return null;
}

/**
* Check if this expression will always return the same value.
*
Expand Down
15 changes: 15 additions & 0 deletions h2/src/main/org/h2/expression/ExpressionList.java
Expand Up @@ -114,6 +114,21 @@ public int getCost() {
return cost;
}

@Override
public TypeInfo getTypeIfStaticallyKnown(SessionLocal session) {
int count = list.length;
TypeInfo[] types = new TypeInfo[count];
for (int i = 0; i < count; i++) {
TypeInfo t = list[i].getTypeIfStaticallyKnown(session);
if (t == null) {
return null;
}
types[i] = t;
}
return isArray ? TypeInfo.getTypeInfo(Value.ARRAY, list.length, 0, TypeInfo.getHigherType(types))
: TypeInfo.getTypeInfo(Value.ROW, 0, 0, new ExtTypeInfoRow(types));
}

@Override
public boolean isConstant() {
for (Expression e : list) {
Expand Down
10 changes: 10 additions & 0 deletions h2/src/main/org/h2/expression/Subquery.java
Expand Up @@ -161,6 +161,16 @@ public int getCost() {
return query.getCostAsExpression();
}

@Override
public TypeInfo getTypeIfStaticallyKnown(SessionLocal session) {
if (query.isConstantQuery()) {
query.prepare();
setType();
return expression.getType();
}
return null;
}

@Override
public boolean isConstant() {
return query.isConstantQuery();
Expand Down
5 changes: 5 additions & 0 deletions h2/src/main/org/h2/expression/ValueExpression.java
Expand Up @@ -114,6 +114,11 @@ public Expression getNotIfPossible(SessionLocal session) {
return getBoolean(!value.getBoolean());
}

@Override
public TypeInfo getTypeIfStaticallyKnown(SessionLocal session) {
return value.getType();
}

@Override
public boolean isConstant() {
return true;
Expand Down
5 changes: 5 additions & 0 deletions h2/src/main/org/h2/expression/function/CastSpecification.java
Expand Up @@ -90,6 +90,11 @@ public Expression optimize(SessionLocal session) {
return this;
}

@Override
public TypeInfo getTypeIfStaticallyKnown(SessionLocal session) {
return type;
}

@Override
public boolean isConstant() {
return left instanceof ValueExpression && (right == null || right.isConstant())
Expand Down
7 changes: 7 additions & 0 deletions h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
Expand Up @@ -1810,6 +1810,13 @@ private void testUnnestWithArrayParameter(Connection conn) throws SQLException {
}
assertFalse(rs.next());
}
prep = conn.prepareStatement(
"SELECT ARRAY_AGG(V) FROM UNNEST(ARRAY[CAST(? AS INTEGER), CAST(? AS INTEGER)]) T(V)");
prep.setInt(1, 1);
prep.setInt(2, 2);
ResultSet rs = prep.executeQuery();
assertTrue(rs.next());
assertEquals(new Integer[] { 1, 2 }, rs.getObject(1, Integer[].class));
}

}