Skip to content

Commit 29c3911

Browse files
committed
fix: match UDT names for prepared cache invalidation
Prepared statement cache invalidation was comparing UDTs with UserDefinedType.equals(), which includes field definitions. That is too strict for schema-change invalidation: a prepared statement can carry a UDT shape from result metadata that is not field-equal to the schema event's old type, especially when the UDT is nested in a collection. Invalidate by UDT identity instead: keyspace and type name are enough to know that a prepared statement references the changed type. Created-type events still have no old type, so ignore them before matching. Add a unit regression for a collection UDT whose field definitions differ but keyspace and type name match.
1 parent 9e8d5ae commit 29c3911

2 files changed

Lines changed: 30 additions & 2 deletions

File tree

core/src/main/java/com/datastax/oss/driver/internal/core/cql/CqlPrepareAsyncProcessor.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,12 @@ protected CqlPrepareAsyncProcessor(
8383
});
8484
}
8585

86-
private static boolean typeMatches(UserDefinedType oldType, DataType typeToCheck) {
86+
static boolean typeMatches(UserDefinedType oldType, DataType typeToCheck) {
8787

8888
switch (typeToCheck.getProtocolCode()) {
8989
case ProtocolConstants.DataType.UDT:
9090
UserDefinedType udtType = (UserDefinedType) typeToCheck;
91-
return udtType.equals(oldType)
91+
return sameTypeName(udtType, oldType)
9292
? true
9393
: Iterables.any(udtType.getFieldTypes(), (testType) -> typeMatches(oldType, testType));
9494
case ProtocolConstants.DataType.LIST:
@@ -110,7 +110,14 @@ private static boolean typeMatches(UserDefinedType oldType, DataType typeToCheck
110110
}
111111
}
112112

113+
private static boolean sameTypeName(UserDefinedType left, UserDefinedType right) {
114+
return left.getKeyspace().equals(right.getKeyspace()) && left.getName().equals(right.getName());
115+
}
116+
113117
private void onTypeChanged(TypeChangeEvent event) {
118+
if (event.oldType == null) {
119+
return;
120+
}
114121
for (Map.Entry<PrepareRequest, CompletableFuture<PreparedStatement>> entry :
115122
this.cache.asMap().entrySet()) {
116123

core/src/test/java/com/datastax/oss/driver/internal/core/cql/CqlPrepareAsyncProcessorTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
import com.datastax.oss.driver.api.core.cql.PrepareRequest;
2323
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
24+
import com.datastax.oss.driver.api.core.type.DataTypes;
25+
import com.datastax.oss.driver.api.core.type.UserDefinedType;
26+
import com.datastax.oss.driver.internal.core.type.UserDefinedTypeBuilder;
2427
import com.datastax.oss.driver.shaded.guava.common.cache.Cache;
2528
import java.util.Optional;
2629
import java.util.concurrent.CompletableFuture;
@@ -85,4 +88,22 @@ public void should_return_defensive_copy_when_future_is_in_flight() throws Excep
8588
returned.toCompletableFuture().cancel(false);
8689
assertThat(inFlight.isCancelled()).isFalse();
8790
}
91+
92+
@Test
93+
public void should_match_udt_by_name_when_field_definitions_differ() {
94+
UserDefinedType oldType =
95+
new UserDefinedTypeBuilder("ks", "test_type_2")
96+
.withField("c", DataTypes.INT)
97+
.withField("d", DataTypes.TEXT)
98+
.build();
99+
UserDefinedType resultType =
100+
new UserDefinedTypeBuilder("ks", "test_type_2")
101+
.withField("c", DataTypes.INT)
102+
.withField("d", DataTypes.TEXT)
103+
.withField("i", DataTypes.BLOB)
104+
.build();
105+
106+
assertThat(CqlPrepareAsyncProcessor.typeMatches(oldType, DataTypes.listOf(resultType)))
107+
.isTrue();
108+
}
88109
}

0 commit comments

Comments
 (0)