Skip to content

Commit

Permalink
JAVA-2210: Add ability to set TTL for modification queries
Browse files Browse the repository at this point in the history
  • Loading branch information
emerkle826 authored and olim7t committed Apr 5, 2019
1 parent 75a12e0 commit 424a257
Show file tree
Hide file tree
Showing 11 changed files with 518 additions and 72 deletions.
1 change: 1 addition & 0 deletions changelog/README.md
Expand Up @@ -4,6 +4,7 @@

### 4.0.1 (in progress)

- [bug] JAVA-2210: Add ability to set TTL for modification queries
- [improvement] JAVA-2212: Add truncate to QueryBuilder
- [improvement] JAVA-2211: Upgrade Jersey examples to fix security issue sid-3606
- [bug] JAVA-2193: Fix flaky tests in ExecutionInfoWarningsIT
Expand Down
29 changes: 28 additions & 1 deletion manual/query_builder/insert/README.md
Expand Up @@ -87,4 +87,31 @@ insertInto("user").json(bindMarker()).usingTimestamp(bindMarker())

If you call the method multiple times, the last value will be used.

[QueryBuilder]: http://docs.datastax.com/en/drivers/java/4.0/com/datastax/oss/driver/api/query-builder/QueryBuilder.html
### Time To Live (TTL)

You can generate a USING TTL clause that will cause column values to be deleted (marked with a
tombstone) after the specified time (in seconds) has expired. This can be done with a literal:

```java
insertInto("user").value("a", bindMarker()).usingTtl(60)
// INSERT INTO user (a) VALUES (?) USING TTL 60
```

Or a bind marker:

```java
insertInto("user").value("a", bindMarker()).usingTtl(bindMarker())
// INSERT INTO user (a) VALUES (?) USING TTL ?
```

If you call the method multiple times, the last value will be used.

The TTL value applies only to the inserted data, not the entire column. Any subsequent updates to
the column resets the TTL.

Setting the value to 0 will result in removing the TTL for the inserted data in Cassandra when the query
is executed. This is distinctly different than setting the value to null. Passing a null value to
this method will only remove the USING TTL clause from the query, which will not alter the TTL (if
one is set) in Cassandra.

[QueryBuilder]: http://docs.datastax.com/en/drivers/java/4.0/com/datastax/oss/driver/api/querybuilder/QueryBuilder.html
33 changes: 31 additions & 2 deletions manual/query_builder/update/README.md
Expand Up @@ -32,6 +32,35 @@ update("user").usingTimestamp(bindMarker());

If you call the method multiple times, the last value will be used.

### Time To Live (TTL)

You can generate a USING TTL clause that will cause column values to be deleted (marked with a
tombstone) after the specified time (in seconds) has expired. This can be done with a literal:

```java
update("user").usingTtl(60).setColumn("v", bindMarker()).whereColumn("k").isEqualTo(bindMarker());
// UPDATE user USING TTL 60 SET v=? WHERE k=?
```

Or a bind marker:

```java
update("user").usingTtl(bindMarker()).setColumn("v", bindMarker()).whereColumn("k").isEqualTo(bindMarker());
// UPDATE user USING TTL ? SET v=? WHERE k=?
```

You can clear a previously set TTL by setting the value to 0:

```java
update("user").usingTtl(0).setColumn("v", bindMarker()).whereColumn("k").isEqualTo(bindMarker());
// UPDATE user USING TTL 0 SET v=? WHERE k=?
```

Setting the value to 0 will result in removing the TTL from the column in Cassandra when the query
is executed. This is distinctly different than setting the value to null. Passing a null value to
this method will only remove the USING TTL clause from the query, which will not alter the TTL (if
one is set) in Cassandra.

### Assignments

An assignment is an operation that appears after the SET keyword. You need at least one for a valid
Expand Down Expand Up @@ -222,5 +251,5 @@ update("foo")
Conditions are a common feature used by UPDATE and DELETE, so they have a
[dedicated page](../condition) in this manual.

[QueryBuilder]: http://docs.datastax.com/en/drivers/java/4.0/com/datastax/oss/driver/api/query-builder/QueryBuilder.html
[Assignment]: http://docs.datastax.com/en/drivers/java/4.0/com/datastax/oss/driver/api/query-builder/update/Assignment.html
[QueryBuilder]: http://docs.datastax.com/en/drivers/java/4.0/com/datastax/oss/driver/api/querybuilder/QueryBuilder.html
[Assignment]: http://docs.datastax.com/en/drivers/java/4.0/com/datastax/oss/driver/api/querybuilder/update/Assignment.html
20 changes: 20 additions & 0 deletions query-builder/revapi.json
Expand Up @@ -2752,6 +2752,26 @@
"new": "method SelfT com.datastax.oss.driver.api.querybuilder.relation.OngoingWhereClause<SelfT extends com.datastax.oss.driver.api.querybuilder.relation.OngoingWhereClause<SelfT extends com.datastax.oss.driver.api.querybuilder.relation.OngoingWhereClause<SelfT>>>::whereRaw(java.lang.String) @ com.datastax.oss.driver.api.querybuilder.update.UpdateWithAssignments",
"annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue",
"justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue"
},
{
"code": "java.method.addedToInterface",
"new": "method com.datastax.oss.driver.api.querybuilder.insert.Insert com.datastax.oss.driver.api.querybuilder.insert.Insert::usingTtl(com.datastax.oss.driver.api.querybuilder.BindMarker)",
"justification": "JAVA-2210: Add ability to set TTL for modification queries"
},
{
"code": "java.method.addedToInterface",
"new": "method com.datastax.oss.driver.api.querybuilder.insert.Insert com.datastax.oss.driver.api.querybuilder.insert.Insert::usingTtl(int)",
"justification": "JAVA-2210: Add ability to set TTL for modification queries"
},
{
"code": "java.method.addedToInterface",
"new": "method com.datastax.oss.driver.api.querybuilder.update.UpdateStart com.datastax.oss.driver.api.querybuilder.update.UpdateStart::usingTtl(com.datastax.oss.driver.api.querybuilder.BindMarker)",
"justification": "JAVA-2210: Add ability to set TTL for modification queries"
},
{
"code": "java.method.addedToInterface",
"new": "method com.datastax.oss.driver.api.querybuilder.update.UpdateStart com.datastax.oss.driver.api.querybuilder.update.UpdateStart::usingTtl(int)",
"justification": "JAVA-2210: Add ability to set TTL for modification queries"
}
]
}
Expand Down
Expand Up @@ -44,4 +44,32 @@ public interface Insert extends BuildableQuery {
*/
@NonNull
Insert usingTimestamp(@Nullable BindMarker bindMarker);

/**
* Adds a {@code USING TTL} clause to this statement with a literal value. Setting a value of
* {@code null} will remove the {@code USING TTL} clause on this statement. Setting a value of
* {@code 0} will insert the data with no TTL when the statement is executed, overriding any Table
* TTL that might exist.
*
* <p>If this method or {@link #usingTtl(BindMarker) } is called multiple times, the value from
* the last invocation is used.
*
* @param ttlInSeconds Time, in seconds, the inserted data should live before expiring.
*/
@NonNull
Insert usingTtl(int ttlInSeconds);

/**
* Adds a {@code USING TTL} clause to this statement with a bind marker. Setting a value of {@code
* null} will remove the {@code USING TTL} clause on this statement. Binding a value of {@code 0}
* will insert the data with no TTL when the statement is executed, overriding any Table TTL that
* might exist.
*
* <p>If this method or {@link #usingTtl(int) } is called multiple times, the value from the last
* invocation is used.
*
* @param bindMarker A bind marker that is understood to be a value in seconds.
*/
@NonNull
Insert usingTtl(@Nullable BindMarker bindMarker);
}
Expand Up @@ -41,4 +41,32 @@ public interface UpdateStart extends OngoingAssignment {
*/
@NonNull
UpdateStart usingTimestamp(@NonNull BindMarker bindMarker);

/**
* Adds a {@code USING TTL} clause to this statement with a literal value. Setting a value of
* {@code null} will remove the {@code USING TTL} clause on this statement. Setting a value of
* {@code 0} will update the data and remove any TTL on the column when the statement is executed,
* overriding any TTL (table or column) that may exist in Cassandra.
*
* <p>If this method or {@link #usingTtl(BindMarker) } is called multiple times, the value from
* the last invocation is used.
*
* @param ttlInSeconds Time, in seconds, the inserted data should live before expiring.
*/
@NonNull
UpdateStart usingTtl(int ttlInSeconds);

/**
* Adds a {@code USING TTL} clause to this statement with a bind marker. Setting a value of {@code
* null} will remove the {@code USING TTL} clause on this statement. Binding a value of {@code 0}
* will update the data and remove any TTL on the column when the statement is executed,
* overriding any TTL (table or column) that may exist in Cassandra.
*
* <p>If this method or {@link #usingTtl(int)} is called multiple times, the value from the last
* invocation is used.
*
* @param bindMarker A bind marker that is understood to be a value in seconds.
*/
@NonNull
UpdateStart usingTtl(@NonNull BindMarker bindMarker);
}
Expand Up @@ -49,10 +49,11 @@ public enum MissingJsonBehavior {
private final MissingJsonBehavior missingJsonBehavior;
private final ImmutableMap<CqlIdentifier, Term> assignments;
private final Object timestamp;
private final Object ttlInSeconds;
private final boolean ifNotExists;

public DefaultInsert(@Nullable CqlIdentifier keyspace, @NonNull CqlIdentifier table) {
this(keyspace, table, null, null, ImmutableMap.of(), null, false);
this(keyspace, table, null, null, ImmutableMap.of(), null, null, false);
}

public DefaultInsert(
Expand All @@ -62,17 +63,27 @@ public DefaultInsert(
@Nullable MissingJsonBehavior missingJsonBehavior,
@NonNull ImmutableMap<CqlIdentifier, Term> assignments,
@Nullable Object timestamp,
@Nullable Object ttlInSeconds,
boolean ifNotExists) {
// Note: the public API guarantees this, but check in case someone is calling the internal API
// directly.
Preconditions.checkArgument(
json == null || assignments.isEmpty(), "JSON insert can't have regular assignments");
Preconditions.checkArgument(
timestamp == null || timestamp instanceof Long || timestamp instanceof BindMarker,
"TIMESTAMP value must be a BindMarker or a Long");
Preconditions.checkArgument(
ttlInSeconds == null
|| ttlInSeconds instanceof Integer
|| ttlInSeconds instanceof BindMarker,
"TTL value must be a BindMarker or an Integer");
this.keyspace = keyspace;
this.table = table;
this.json = json;
this.missingJsonBehavior = missingJsonBehavior;
this.assignments = assignments;
this.timestamp = timestamp;
this.ttlInSeconds = ttlInSeconds;
this.ifNotExists = ifNotExists;
}

Expand All @@ -86,14 +97,22 @@ public JsonInsert json(@NonNull String json) {
missingJsonBehavior,
ImmutableMap.of(),
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
@Override
public JsonInsert json(@NonNull BindMarker json) {
return new DefaultInsert(
keyspace, table, json, missingJsonBehavior, ImmutableMap.of(), timestamp, ifNotExists);
keyspace,
table,
json,
missingJsonBehavior,
ImmutableMap.of(),
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
Expand All @@ -106,14 +125,22 @@ public <T> JsonInsert json(@NonNull T value, @NonNull TypeCodec<T> codec) {
missingJsonBehavior,
ImmutableMap.of(),
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
@Override
public JsonInsert defaultNull() {
return new DefaultInsert(
keyspace, table, json, MissingJsonBehavior.NULL, ImmutableMap.of(), timestamp, ifNotExists);
keyspace,
table,
json,
MissingJsonBehavior.NULL,
ImmutableMap.of(),
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
Expand All @@ -126,6 +153,7 @@ public JsonInsert defaultUnset() {
MissingJsonBehavior.UNSET,
ImmutableMap.of(),
timestamp,
ttlInSeconds,
ifNotExists);
}

Expand All @@ -139,28 +167,71 @@ public RegularInsert value(@NonNull CqlIdentifier columnId, @NonNull Term value)
null,
ImmutableCollections.append(assignments, columnId, value),
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
@Override
public Insert ifNotExists() {
return new DefaultInsert(
keyspace, table, json, missingJsonBehavior, assignments, timestamp, true);
keyspace, table, json, missingJsonBehavior, assignments, timestamp, ttlInSeconds, true);
}

@NonNull
@Override
public Insert usingTimestamp(long timestamp) {
return new DefaultInsert(
keyspace, table, json, missingJsonBehavior, assignments, timestamp, ifNotExists);
keyspace,
table,
json,
missingJsonBehavior,
assignments,
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
@Override
public Insert usingTimestamp(@Nullable BindMarker timestamp) {
return new DefaultInsert(
keyspace, table, json, missingJsonBehavior, assignments, timestamp, ifNotExists);
keyspace,
table,
json,
missingJsonBehavior,
assignments,
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
@Override
public Insert usingTtl(int ttlInSeconds) {
return new DefaultInsert(
keyspace,
table,
json,
missingJsonBehavior,
assignments,
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
@Override
public Insert usingTtl(@Nullable BindMarker ttlInSeconds) {
return new DefaultInsert(
keyspace,
table,
json,
missingJsonBehavior,
assignments,
timestamp,
ttlInSeconds,
ifNotExists);
}

@NonNull
Expand Down Expand Up @@ -192,6 +263,14 @@ public String asCql() {
builder.append(timestamp);
}
}
if (ttlInSeconds != null) {
builder.append((timestamp != null) ? " AND " : " USING ").append("TTL ");
if (ttlInSeconds instanceof BindMarker) {
((BindMarker) ttlInSeconds).appendTo(builder);
} else {
builder.append(ttlInSeconds);
}
}
return builder.toString();
}

Expand Down Expand Up @@ -267,6 +346,11 @@ public Object getTimestamp() {
return timestamp;
}

@Nullable
public Object getTtlInSeconds() {
return ttlInSeconds;
}

public boolean isIfNotExists() {
return ifNotExists;
}
Expand Down

0 comments on commit 424a257

Please sign in to comment.