Skip to content

Upgrade to Cassandra Java Driver 4.x - #302

Merged
unidevel merged 3 commits into
prestodb:masterfrom
msmygit:cassandra-java-driver-4x
Jun 8, 2026
Merged

Upgrade to Cassandra Java Driver 4.x#302
unidevel merged 3 commits into
prestodb:masterfrom
msmygit:cassandra-java-driver-4x

Conversation

@msmygit

@msmygit msmygit commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Related presto PR with temp jitpack tests are at prestodb/presto#27029

msmygit added 2 commits June 2, 2026 23:27
signing { sign publishing.publications } registered the .asc signature artifacts
unconditionally, so publishToMavenLocal failed for SNAPSHOT builds (and JitPack,
which builds the project without a GPG key) looking for a non-existent
tempto-core-<version>.jar.asc. Only attach signatures when actually signing
(release builds with signing enabled); release publishing is unchanged.
@msmygit
msmygit requested review from imjalpreet and tdcmeehan June 3, 2026 03:41
@msmygit msmygit self-assigned this Jun 3, 2026
@sourcery-ai

sourcery-ai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Upgrades Tempto to use the Apache Cassandra Java Driver 4.x by replacing the legacy Cluster/Session APIs with CqlSession, updating type mappings and metadata access patterns, adapting batch write logic to the new driver, and adjusting build/signing and example configs (driver coordinates, Cassandra Docker image, and configuration) for compatibility with newer Cassandra and JitPack builds.

Flow diagram for Cassandra configuration into CqlSession builder and usage

flowchart LR
    Configuration -->|getStringMandatory host, getIntMandatory port, getString datacenter| CassandraQueryExecutor
    CassandraQueryExecutor -->|CqlSession.builder addContactPoint withLocalDatacenter build| CqlSession
    CqlSession -->|execute sql| QueryResult
    CqlSession -->|prepare insertQuery| CassandraBatchLoader
    CassandraBatchLoader -->|BatchStatementBuilder addStatement execute| CqlSession
Loading

File-Level Changes

Change Details Files
Migrate CassandraQueryExecutor from Datastax driver 3.x Cluster/Session APIs to Apache Cassandra driver 4.x CqlSession and new metadata/type APIs.
  • Replace Cluster/Session fields with a single CqlSession field created via CqlSession.builder using host, port, and a configurable local datacenter.
  • Update Cassandra type-to-JDBCType mapping to use DataTypes constants and expand to include TEXT mapping; fix null-check to validate missing JDBCType instead of DataType.
  • Change query execution to enforce non-closed sessions, iterate ColumnDefinition from new ResultSet metadata API, derive column names via ColumnDefinition.getName().toString, and fetch row values using row.getObject(i) instead of token values.
  • Refactor metadata helpers (getColumnNames, tableExists, getTableNames) to use session.getMetadata(), Optional<KeyspaceMetadata/TableMetadata>, and new collections API, returning names as strings via toString().
  • Simplify lifecycle management by removing lazy session connect and ensuring close() closes the CqlSession if open.
tempto-core/src/main/java/io/prestodb/tempto/internal/query/CassandraQueryExecutor.java
Adapt CassandraBatchLoader to driver 4.x batching APIs.
  • Change loader to accept and store a CqlSession instead of Session.
  • Replace BatchStatement construction with BatchStatementBuilder using DefaultBatchType.UNLOGGED.
  • Track current batch size manually and execute batches via session.execute(batchBuilder.build()) when the configured row limit is reached or at the end.
  • Bind rows using statement.bind(row.toArray()) added to the BatchStatementBuilder rather than BatchStatement.add().
tempto-core/src/main/java/io/prestodb/tempto/internal/fulfillment/table/cassandra/CassandraBatchLoader.java
Update build and publishing configuration to use the new Apache Cassandra Java driver and avoid signing issues for non-release builds.
  • Bump cassandra driver version from 3.4.0 to 4.19.2 and switch dependency coordinates from com.datastax.cassandra:cassandra-driver-core to org.apache.cassandra:java-driver-core.
  • Guard Gradle signing configuration so that publishing signatures are only attached when signing is actually performed (release builds), preventing publishToMavenLocal failures for SNAPSHOT/JitPack builds.
build.gradle
Align example environment and configuration with Cassandra 3.x and driver 4.x requirements.
  • Update example Docker Cassandra image from cassandra:2.1.15 to cassandra:3.11.19 to match driver expectations.
  • Add a datacenter configuration property (defaulting to datacenter1) for the Cassandra database in tempto-configuration.yaml, used by CqlSession.withLocalDatacenter().
tempto-examples/docker/docker-compose.yml
tempto-examples/src/main/resources/tempto-configuration.yaml

Possibly linked issues

  • #: PR updates dependency to Apache Cassandra driver 4.x and adapts CassandraQueryExecutor, batch loader, and configs per issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@msmygit
msmygit requested a review from pdabre12 June 3, 2026 03:42
@msmygit
msmygit marked this pull request as ready for review June 5, 2026 18:38

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tempto-core/src/main/java/io/prestodb/tempto/internal/query/CassandraQueryExecutor.java" line_range="103-110" />
<code_context>
                 .map(definition -> getJDBCType(definition.getType()))
                 .collect(toList());

         List<String> columnNames = definitions.stream()
-                .map(ColumnDefinitions.Definition::getName)
+                .map(ColumnDefinition::getName)
+                .map(Object::toString)
                 .collect(toList());

</code_context>
<issue_to_address>
**suggestion (bug_risk):** Prefer using CqlIdentifier#asInternal (or similar) over toString() for column names.

In the 4.x driver, `ColumnDefinition#getName` returns a `CqlIdentifier`, and `toString()` preserves quoting and case. This can diverge from the internal CQL name and break consumers expecting unquoted, lowercase column names. Please use `def.getName().asInternal()` (or the equivalent accessor in this driver version) instead of `Object::toString` when building `columnNames`.

```suggestion
        List<JDBCType> types = definitions.stream()
                .map(definition -> getJDBCType(definition.getType()))
                .collect(toList());

        List<String> columnNames = definitions.stream()
                .map(definition -> definition.getName().asInternal())
                .collect(toList());
```
</issue_to_address>

### Comment 2
<location path="tempto-core/src/main/java/io/prestodb/tempto/internal/query/CassandraQueryExecutor.java" line_range="171-172" />
<code_context>
-
-        if (session == null || session.isClosed()) {
-            session = cluster.connect();
+        if (session != null && !session.isClosed()) {
+            session.close();
         }
     }
</code_context>
<issue_to_address>
**nitpick:** The null check on a final session field is redundant.

Because `session` is a `final` field set in the constructor and never reassigned, it cannot be null here. This condition can be reduced to checking `!session.isClosed()` before closing, which also makes the lifecycle assumptions clearer.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 103 to 110
List<JDBCType> types = definitions.stream()
.map(definition -> getJDBCType(definition.getType()))
.collect(toList());

List<String> columnNames = definitions.stream()
.map(ColumnDefinitions.Definition::getName)
.map(ColumnDefinition::getName)
.map(Object::toString)
.collect(toList());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Prefer using CqlIdentifier#asInternal (or similar) over toString() for column names.

In the 4.x driver, ColumnDefinition#getName returns a CqlIdentifier, and toString() preserves quoting and case. This can diverge from the internal CQL name and break consumers expecting unquoted, lowercase column names. Please use def.getName().asInternal() (or the equivalent accessor in this driver version) instead of Object::toString when building columnNames.

Suggested change
List<JDBCType> types = definitions.stream()
.map(definition -> getJDBCType(definition.getType()))
.collect(toList());
List<String> columnNames = definitions.stream()
.map(ColumnDefinitions.Definition::getName)
.map(ColumnDefinition::getName)
.map(Object::toString)
.collect(toList());
List<JDBCType> types = definitions.stream()
.map(definition -> getJDBCType(definition.getType()))
.collect(toList());
List<String> columnNames = definitions.stream()
.map(definition -> definition.getName().asInternal())
.collect(toList());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Analysis: the asInternal() suggestion and its repercussions

First, the mechanics behind the suggestion. In the 4.x driver, ColumnDefinition#getName() (and ColumnMetadata#getName(), TableMetadata#getName()) return a CqlIdentifier. Per CqlIdentifier.java:

  • asInternal() → the raw name exactly as stored in the schema, never quoted, case preserved ("Foo" → Foo).
  • toString() → defined as asCql(true), the pretty CQL form. asCql adds double-quotes whenever the identifier needs them — i.e. mixed case, a reserved keyword, or special characters. The CqlIdentifierTest shows this
    directly: fromInternal("foo") → foo, but fromInternal("Foo") → "Foo" (quoted), and a reserved word like table → "table".

So the two only diverge for mixed-case / reserved / special-char identifiers. For a plain lowercase name (the overwhelmingly common case) asInternal() and toString() are byte-for-byte identical, so most existing
tests see no change at all.

The reviewer is also right that this is a regression from 3.x: there getName() returned a plain String (the unquoted internal name), which is exactly what asInternal() reproduces. toString() does not.

Repercussion 1 — the same pattern exists in three places, and the fix is not the same for all three

The suggestion patches only lines 107–110, but .map(Object::toString) on a CqlIdentifier also appears at:

  • getColumnNames() (140–143)
  • getTableNames() (162–165)

And here's the catch — asInternal() is not universally the right replacement, because these three feed very different consumers:

Site Consumer What the name is used for Correct form
executeQuery (107–110) QueryResult.columnNamesIndexes → QueryAssert.column(String name) via tryFindColumnIndex a lookup key compared against the bare name a test passes asInternal() ✅
getColumnNames (140–143) CassandraBatchLoader.createInsertQuery → embedded into INSERT INTO t (col,...) VALUES(?) a CQL fragment needs quoting ⚠️
getTableNames (162–165) dropStaleMutableTables → DROP TABLE ks. a CQL DDL fragment needs quoting ⚠️

For the two CQL-embedding paths, switching to asInternal() would actively break mixed-case/reserved identifiers:

  • A column whose internal name is Foo: asInternal() yields Foo, producing INSERT INTO t (Foo) VALUES(?). Cassandra lowercases unquoted identifiers, so this resolves to a nonexistent foo and the insert fails.
    toString()/asCql() yields "Foo", which is the correct thing to splice into CQL.

So the reviewer's reasoning ("toString() can diverge and break consumers expecting unquoted names") is correct for the result-column path but inverted for the loader/DDL paths — there the quoting is exactly what you
want.

Repercussion 2 — the executeQuery change is actually a net positive beyond what the reviewer claims

For that one site, asInternal() doesn't just match style — it fixes a real latent bug. columnNames becomes the key set in QueryResult's bimap, and QueryAssert.column("myColumn", ...) looks them up by bare name. With
toString(), a query selecting a quoted/mixed-case column would be stored under "MyColumn" and the test's lookup by MyColumn would silently fail with "No column with name". asInternal() restores 3.x behavior and makes
the lookup work.

Recommendation

  • Lines 107–110 (executeQuery): adopt the suggestion — definition.getName().asInternal(). Correct and an improvement.
  • Lines 140–143 (getColumnNames) and 162–165 (getTableNames): do not blindly switch to asInternal(). These splice into CQL, so the safe choice is to keep a quoting-aware form. Cleanest is to make the intent explicit
    with getName().asCql(true) (what toString() already does) rather than relying on Object::toString, or — more robustly — change CassandraBatchLoader/dropTable to take CqlIdentifiers and let the driver handle quoting.
    If you only "fix" 107–110, the three sites become inconsistent for non-trivial identifiers, which is a subtle trap for future readers.

Practical blast radius

Because tempto's own mutable table names and typical test schemas use lowercase identifiers, all three forms coincide in practice today, so this is unlikely to change any current test outcome. The risk is entirely
around case-sensitive / quoted / reserved-word identifiers — which is precisely the scenario the reviewer is flagging, and precisely where applying their fix uniformly would do harm.

@wanglinsong wanglinsong left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the discussion with @msmygit, the updates were verified.

@unidevel
unidevel merged commit 5fe0d85 into prestodb:master Jun 8, 2026
3 checks passed
czentgr pushed a commit to prestodb/presto that referenced this pull request Jun 22, 2026
## Description
Upgrade to latest [Cassandra Java Driver
4.x](https://apache.github.io/cassandra-java-driver). Depends on
`tempto` PR: prestodb/tempto#302 (which is
already reviewed, merged, & released).

## Motivation and Context
<!---Why is this change required? What problem does it solve?-->
<!---If it fixes an open issue, please link to the issue here.-->
Cassandra Java Driver `3.x` versions has reached EOL and is mostly not
receiving any fixes or features. [**DataStax** *has* already donated it
to the ASF and is spearheading the `4.x`
series](https://groups.google.com/a/lists.datastax.com/g/java-driver-user/c/9JsrEJqaYOc/m/QJQQCj5MBQAJ).

Resolves #26852 #26762

## Impact
<!---Describe any public API or user-facing feature change or any
performance impact-->
This will get presto to a more stable and future-forward and supported
non-EOL Cassandra Java driver.

## Test Plan
<!---Please fill in how you tested your change-->
All relevant tests are now updated to leverage the latest Cassandra Java
driver flavor

## Contributor checklist

- [x ] Please make sure your submission complies with our [contributing
guide](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md),
in particular [code
style](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md#code-style)
and [commit
standards](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md#commit-standards).
- [x] PR description addresses the issue accurately and concisely. If
the change is non-trivial, a GitHub Issue is referenced.
- [x] Documented new properties (with its default value), SQL syntax,
functions, or other functionality.
- [x] If release notes are required, they follow the [release notes
guidelines](https://github.com/prestodb/presto/wiki/Release-Notes-Guidelines).
- [x] Adequate tests were added if applicable.
- [x] CI passed.
- [ ] If adding new dependencies, verified they have an [OpenSSF
Scorecard](https://securityscorecards.dev/#the-checks) score of 5.0 or
higher (or obtained explicit TSC approval for lower scores).

## Release Notes
Please follow [release notes
guidelines](https://github.com/prestodb/presto/wiki/Release-Notes-Guidelines)
and fill in the release notes below.

```
== RELEASE NOTES ==

Cassandra Connector Changes
* Upgrade to Cassandra Java Driver `4.x`
```

## Summary by Sourcery

Upgrade the Cassandra connector to use the Cassandra Java Driver 4.x and
adapt the connector’s session, metadata, type handling, and
configuration to the new driver APIs, including support for DataStax
Astra secure connect bundles.

New Features:
- Add support for configuring Cassandra connections via DataStax Astra
secure connect bundles.
- Introduce a ReopeningSession wrapper to transparently reopen
CqlSession instances on failure.

Enhancements:
- Refactor Cassandra client wiring to build CqlSession with programmatic
driver configuration and updated retry, load-balancing, and speculative
execution settings.
- Update connector metadata, schema, and token range handling to the new
driver 4.x metadata and token map model.
- Adjust Cassandra type mappings and value conversions to the driver 4.x
type system and Java time APIs.
- Simplify configuration by removing deprecated protocol and whitelist
settings and relying on driver 4.x defaults.
- Update write path (page sink) and test utilities to use the new query
builder and statement APIs.
- Replace host-based replica/address handling with the new Node-based
APIs and adapt split/token code accordingly.

Build:
- Replace the legacy DataStax driver dependency with the Apache
Cassandra Java Driver 4.x core and query-builder modules, and add the
LZ4 runtime and jsr305 annotations dependencies.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants