Repository: eclipse-vertx/vertx-sql-client
Issue: Type pollution agent report

### Version

Using version `4.3.4`.

### Context

I've been running the Techempower benchmark with Quarkus + Hibernate Reactive + Vert.x `vertx-pg-client` and attaching the [agent to detect the type pollution cases](https://github.com/RedHatPerf/type-pollution-agent).

This agent is able to report cases of [JDK-8180450](https://bugs.openjdk.org/browse/JDK-8180450) in our libraries, which are very hard to spot otherwise but result in extreme performance penalties, so I'd recommend treating alerts from it at high priority - especially as fixes might be trivial, while returning very high performance impact.

This is the report output using today's Quarkus snapshot:

```
4:	io.vertx.sqlclient.impl.ListTuple
Count:	14491599
Types:
	io.vertx.sqlclient.impl.TupleInternal
	io.vertx.sqlclient.Tuple
Traces:
	io.vertx.sqlclient.impl.command.ExtendedQueryCommand.prepare(ExtendedQueryCommand.java:122)
		class: io.vertx.sqlclient.impl.TupleInternal
		count: 7070022
	io.vertx.sqlclient.impl.command.ExtendedQueryCommand.params(ExtendedQueryCommand.java:160)
		class: io.vertx.sqlclient.Tuple
		count: 7029308
	io.vertx.sqlclient.impl.command.ExtendedQueryCommand.prepare(ExtendedQueryCommand.java:115)
		class: io.vertx.sqlclient.impl.TupleInternal
		count: 193981
	io.vertx.sqlclient.impl.command.ExtendedQueryCommand.prepare(ExtendedQueryCommand.java:114)
		class: io.vertx.sqlclient.Tuple
		count: 179589
	io.vertx.pgclient.impl.codec.ExtendedQueryCommandCodec.encode(ExtendedQueryCommandCodec.java:51)
		class: io.vertx.sqlclient.Tuple
		count: 18699
```

Interpreting this, the problem is trivial: the same concrete type `io.vertx.sqlclient.impl.ListTuple` is occasionally being tested for being of type `io.vertx.sqlclient.impl.TupleInternal`, and occasionally for type `io.vertx.sqlclient.Tuple`.
Such alternating checks cause a problem with the internal type cache in hotspot, the simplest way to fix it is to always check for the same type.

Perusing the object hierarchy it seems any implementation of `Tuple` is always also a valid cast to `TupleInternal`, so my suggestion would be for all internal code to consistently cast to `TupleInternal`, and let the type get widened as needed (which doesn't need a check).

cc/ @tsegismont @vietj @franz1981 

Changed files:
vertx-oracle-client/src/main/java/io/vertx/oracleclient/impl/commands/OraclePreparedBatch.java
vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java
vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java
vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/ExtendedQueryCommandCodec.java
vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/TupleInternal.java
vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedCursorQueryCommandCodec.java
vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ArrayTuple.java
vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedQueryCommandCodec.java
vertx-db2-client/src/main/java/io/vertx/db2client/impl/codec/ExtendedBatchQueryCommandCodec.java
vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/command/ExtendedQueryCommand.java
vertx-sql-client-templates/src/main/java/io/vertx/sqlclient/templates/impl/JsonTuple.java
vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ListTuple.java

diff --git a/vertx-db2-client/src/main/java/io/vertx/db2client/impl/codec/ExtendedBatchQueryCommandCodec.java b/vertx-db2-client/src/main/java/io/vertx/db2client/impl/codec/ExtendedBatchQueryCommandCodec.java
index 1190c575..cd405d47 100644
--- a/vertx-db2-client/src/main/java/io/vertx/db2client/impl/codec/ExtendedBatchQueryCommandCodec.java
+++ b/vertx-db2-client/src/main/java/io/vertx/db2client/impl/codec/ExtendedBatchQueryCommandCodec.java
@@ -27,6 +27,7 @@ import io.vertx.db2client.impl.codec.DB2PreparedStatement.QueryInstance;
 import io.vertx.db2client.impl.drda.DRDAQueryRequest;
 import io.vertx.db2client.impl.drda.DRDAQueryResponse;
 import io.vertx.sqlclient.Tuple;
+import io.vertx.sqlclient.impl.TupleInternal;
 import io.vertx.sqlclient.impl.command.CommandResponse;
 import io.vertx.sqlclient.impl.command.ExtendedQueryCommand;
 
@@ -34,7 +35,7 @@ class ExtendedBatchQueryCommandCodec<R> extends ExtendedQueryCommandBaseCodec<R,
 
   private static final Logger LOG = LoggerFactory.getLogger(ExtendedBatchQueryCommandCodec.class);
 
-  private final List<Tuple> params;
+  private final List<TupleInternal> params;
   private final List<QueryInstance> queryInstances;
   private final String baseCursorId;
 
diff --git a/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java b/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java
index 99dcd6d9..1a5759a4 100644
--- a/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java
+++ b/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java
@@ -12,7 +12,6 @@
 package io.vertx.mssqlclient.impl.codec;
 
 import io.netty.buffer.ByteBuf;
-import io.vertx.sqlclient.Tuple;
 import io.vertx.sqlclient.impl.TupleInternal;
 import io.vertx.sqlclient.impl.command.CommandResponse;
 import io.vertx.sqlclient.impl.command.ExtendedQueryCommand;
@@ -21,7 +20,7 @@ import java.util.List;
 
 class ExtendedBatchQueryCommandCodec<T> extends ExtendedQueryCommandBaseCodec<T> {
 
-  private final List<Tuple> paramsList;
+  private final List<TupleInternal> paramsList;
 
   private int paramsIdx;
   private int messageDecoded;
@@ -52,7 +51,7 @@ class ExtendedBatchQueryCommandCodec<T> extends ExtendedQueryCommandBaseCodec<T>
   @Override
   protected TupleInternal prepexecRequestParams() {
     paramsIdx = 1;
-    return (TupleInternal) paramsList.get(0);
+    return paramsList.get(0);
   }
 
   @Override
@@ -67,6 +66,6 @@ class ExtendedBatchQueryCommandCodec<T> extends ExtendedQueryCommandBaseCodec<T>
 
   @Override
   protected TupleInternal execRequestParams() {
-    return (TupleInternal) paramsList.get(paramsIdx);
+    return paramsList.get(paramsIdx);
   }
 }
diff --git a/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedCursorQueryCommandCodec.java b/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedCursorQueryCommandCodec.java
index a58ed0cb..4efb5961 100644
--- a/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedCursorQueryCommandCodec.java
+++ b/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedCursorQueryCommandCodec.java
@@ -175,7 +175,7 @@ class ExtendedCursorQueryCommandCodec<T> extends ExtendedQueryCommandBaseCodec<T
 
   @Override
   protected TupleInternal prepexecRequestParams() {
-    return (TupleInternal) cmd.params();
+    return cmd.params();
   }
 
   @Override
diff --git a/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedQueryCommandCodec.java b/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedQueryCommandCodec.java
index 3106756b..8d4b8a59 100644
--- a/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedQueryCommandCodec.java
+++ b/vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedQueryCommandCodec.java
@@ -22,11 +22,11 @@ class ExtendedQueryCommandCodec<T> extends ExtendedQueryCommandBaseCodec<T> {
 
   @Override
   protected TupleInternal prepexecRequestParams() {
-    return (TupleInternal) cmd.params();
+    return cmd.params();
   }
 
   @Override
   protected TupleInternal execRequestParams() {
-    return (TupleInternal) cmd.params();
+    return cmd.params();
   }
 }
diff --git a/vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java b/vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java
index 02b247ed..bf021292 100644
--- a/vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java
+++ b/vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/ExtendedBatchQueryCommandCodec.java
@@ -16,6 +16,7 @@ import io.vertx.core.impl.NoStackTraceThrowable;
 import io.vertx.mysqlclient.MySQLBatchException;
 import io.vertx.mysqlclient.MySQLException;
 import io.vertx.sqlclient.Tuple;
+import io.vertx.sqlclient.impl.TupleInternal;
 import io.vertx.sqlclient.impl.command.CommandResponse;
 import io.vertx.sqlclient.impl.command.ExtendedQueryCommand;
 
@@ -25,7 +26,7 @@ import static io.vertx.mysqlclient.impl.protocol.Packets.EnumCursorType.CURSOR_T
 
 class ExtendedBatchQueryCommandCodec<R> extends ExtendedQueryCommandBaseCodec<R, ExtendedQueryCommand<R>> {
 
-  private final List<Tuple> params;
+  private final List<TupleInternal> params;
   private int batchIdx = 0;
 
   ExtendedBatchQueryCommandCodec(ExtendedQueryCommand<R> cmd) {
diff --git a/vertx-oracle-client/src/main/java/io/vertx/oracleclient/impl/commands/OraclePreparedBatch.java b/vertx-oracle-client/src/main/java/io/vertx/oracleclient/impl/commands/OraclePreparedBatch.java
index c7958b54..32384edb 100644
--- a/vertx-oracle-client/src/main/java/io/vertx/oracleclient/impl/commands/OraclePreparedBatch.java
+++ b/vertx-oracle-client/src/main/java/io/vertx/oracleclient/impl/commands/OraclePreparedBatch.java
@@ -16,6 +16,7 @@ import io.vertx.oracleclient.OraclePrepareOptions;
 import io.vertx.oracleclient.impl.Helper;
 import io.vertx.sqlclient.Row;
 import io.vertx.sqlclient.Tuple;
+import io.vertx.sqlclient.impl.TupleInternal;
 import io.vertx.sqlclient.impl.command.ExtendedQueryCommand;
 import oracle.jdbc.OraclePreparedStatement;
 
@@ -30,9 +31,9 @@ import java.util.stream.Collector;
 public class OraclePreparedBatch<C, R> extends QueryCommand<C, R> {
 
   private final ExtendedQueryCommand<R> query;
-  private final List<Tuple> listParams;
+  private final List<TupleInternal> listParams;
 
-  public OraclePreparedBatch(ExtendedQueryCommand<R> query, Collector<Row, C, R> collector, List<Tuple> listParams) {
+  public OraclePreparedBatch(ExtendedQueryCommand<R> query, Collector<Row, C, R> collector, List<TupleInternal> listParams) {
     super(collector);
     this.query = query;
     this.listParams = listParams;
diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/ExtendedQueryCommandCodec.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/ExtendedQueryCommandCodec.java
index c1db0d35..d3821b1d 100644
--- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/ExtendedQueryCommandCodec.java
+++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/ExtendedQueryCommandCodec.java
@@ -16,7 +16,7 @@
  */
 package io.vertx.pgclient.impl.codec;
 
-import io.vertx.sqlclient.Tuple;
+import io.vertx.sqlclient.impl.TupleInternal;
 import io.vertx.sqlclient.impl.codec.InvalidCachedStatementEvent;
 import io.vertx.sqlclient.impl.RowDesc;
 import io.vertx.sqlclient.impl.command.CommandResponse;
@@ -48,7 +48,7 @@ class ExtendedQueryCommandCodec<R, C extends ExtendedQueryCommand<R>> extends Qu
           completionHandler.handle(CommandResponse.failure("Can not execute batch query with 0 sets of batch parameters."));
           return;
         } else {
-          for (Tuple param : cmd.paramsList()) {
+          for (TupleInternal param : cmd.paramsList()) {
             encoder.writeBind(ps.bind, cmd.cursorId(), param);
             encoder.writeExecute(cmd.cursorId(), cmd.fetch());
           }
diff --git a/vertx-sql-client-templates/src/main/java/io/vertx/sqlclient/templates/impl/JsonTuple.java b/vertx-sql-client-templates/src/main/java/io/vertx/sqlclient/templates/impl/JsonTuple.java
index a90f83ef..9a204318 100644
--- a/vertx-sql-client-templates/src/main/java/io/vertx/sqlclient/templates/impl/JsonTuple.java
+++ b/vertx-sql-client-templates/src/main/java/io/vertx/sqlclient/templates/impl/JsonTuple.java
@@ -16,7 +16,7 @@ import io.vertx.sqlclient.impl.TupleInternal;
 
 import java.util.function.Function;
 
-public class JsonTuple implements TupleInternal {
+public class JsonTuple extends TupleInternal {
 
   private final int size;
   private final Function<Integer, String> columnMapping;
diff --git a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ArrayTuple.java b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ArrayTuple.java
index 9cd1e2a9..46e2fcf9 100644
--- a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ArrayTuple.java
+++ b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ArrayTuple.java
@@ -22,7 +22,7 @@ import io.vertx.sqlclient.Tuple;
 import java.util.Arrays;
 import java.util.Collection;
 
-public class ArrayTuple implements TupleInternal {
+public class ArrayTuple extends TupleInternal {
 
   private static final Object[] EMPTY_ARRAY = new Object[0];
   public static Tuple EMPTY = new ArrayTuple(0);
diff --git a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ListTuple.java b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ListTuple.java
index f813465b..1591fe6f 100644
--- a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ListTuple.java
+++ b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/ListTuple.java
@@ -21,7 +21,7 @@ import io.vertx.sqlclient.Tuple;
 
 import java.util.List;
 
-public class ListTuple implements TupleInternal {
+public class ListTuple extends TupleInternal {
 
   private final List<Object> list;
 
diff --git a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/TupleInternal.java b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/TupleInternal.java
index e4eb9805..c45a6111 100644
--- a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/TupleInternal.java
+++ b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/TupleInternal.java
@@ -17,20 +17,20 @@ import io.vertx.sqlclient.data.NullValue;
 import java.util.ArrayList;
 import java.util.List;
 
-public interface TupleInternal extends Tuple {
+public abstract class TupleInternal implements Tuple {
 
-  void setValue(int pos, Object value);
+  public abstract void setValue(int pos, Object value);
 
   @Override
-  default Object getValue(int pos) {
+  public Object getValue(int pos) {
     Object val = getValueInternal(pos);
     return val instanceof NullValue ? null : val;
   }
 
-  Object getValueInternal(int pos);
+  public abstract Object getValueInternal(int pos);
 
   @Override
-  default List<Class<?>> types() {
+  public List<Class<?>> types() {
     int len = size();
     List<Class<?>> types = new ArrayList<>(len);
     for (int i = 0; i < len; i++) {
diff --git a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/command/ExtendedQueryCommand.java b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/command/ExtendedQueryCommand.java
index 18d5f40a..3d70a8fe 100644
--- a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/command/ExtendedQueryCommand.java
+++ b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/command/ExtendedQueryCommand.java
@@ -111,8 +111,8 @@ public class ExtendedQueryCommand<R> extends QueryCommandBase<R> {
   public String prepare() {
     if (ps != null) {
       if (batch) {
-        for (Tuple tuple : (List<Tuple>) tuples) {
-          String msg = ps.prepare((TupleInternal) tuple);
+        for (TupleInternal tuple : (List<TupleInternal>) tuples) {
+          String msg = ps.prepare(tuple);
           if (msg != null) {
             return msg;
           }
@@ -132,8 +132,8 @@ public class ExtendedQueryCommand<R> extends QueryCommandBase<R> {
   /**
    * @return the list of parameters for batch execution
    */
-  public List<Tuple> paramsList() {
-    return batch ? (List<Tuple>) tuples : null;
+  public List<TupleInternal> paramsList() {
+    return batch ? (List<TupleInternal>) tuples : null;
   }
 
   /**
@@ -148,7 +148,7 @@ public class ExtendedQueryCommand<R> extends QueryCommandBase<R> {
       }
       tuple = list.get(0);
     } else {
-      tuple = (Tuple) tuples;
+      tuple = (TupleInternal) tuples;
     }
     return tuple.types();
   }
@@ -156,8 +156,8 @@ public class ExtendedQueryCommand<R> extends QueryCommandBase<R> {
   /**
    * @return the parameters for query execution
    */
-  public Tuple params() {
-    return batch ? null : (Tuple) tuples;
+  public TupleInternal params() {
+    return batch ? null : (TupleInternal) tuples;
   }
 
   public PreparedStatement preparedStatement() {