Skip to content

Commit

Permalink
Feature/delete timestamp (#530)
Browse files Browse the repository at this point in the history
* Enabling now operator support

* ADding overload for keyspace

* Adding ability to specify custom delete timestamps.

* Adding now operator tests.
  • Loading branch information
alexflav23 authored Aug 4, 2016
1 parent e0e38a7 commit ca30604
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,19 @@ class KeySpaceBuilder(clusterBuilder: ClusterBuilder) {
new KeySpaceDef(name, clusterBuilder, autoinit, query)
}

/**
* Creates and can initialise a keyspace with the given name.
* This will automatically initialise the keyspace by default, as we consider
* passing a specific keyspace query indicates clear intent you want this to happen.
* @param name The name of the keyspace, case sensititve by default.
* @param query The builder to use when producing the keyspace query.
* @return
*/
def keySpace(
name: String,
query: (Session, KeySpace) => String
): KeySpaceDef = {
new KeySpaceDef(name, clusterBuilder, true, Some(query))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,17 @@ sealed class DateOfCqlFunction extends CqlFunction {
}

sealed class NowCqlFunction extends CqlFunction {
def apply(): OperatorClause.Condition = {
def apply()(implicit ev: Primitive[Long], session: Session): OperatorClause.Condition = {
new OperatorClause.Condition(QueryBuilder.Select.now())

/*
new TypedClause.Condition(QueryBuilder.Select.now(), row => {
if (session.v3orNewer) {
ev.fromRow(s"system.timestamp", row).toOption
} else {
ev.fromRow(s"timestamp", row).toOption
}
})*/
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import com.websudos.phantom.builder.ops.MapKeyUpdateClause
import com.websudos.phantom.builder.query.prepared.PreparedBlock
import com.websudos.phantom.column.AbstractColumn
import com.websudos.phantom.connectors.KeySpace
import com.websudos.phantom.dsl.DateTime
import shapeless.ops.hlist.Reverse
import shapeless.{::, =:!=, HList, HNil}

Expand Down Expand Up @@ -89,6 +90,21 @@ class DeleteQuery[
new PreparedBlock(qb, options)
}

def timestamp(time: Long): DeleteQuery[Table, Record, Limit, Order, Status, Chainned, PS] = {
new DeleteQuery(
table = table,
init = init,
wherePart = wherePart,
casPart = casPart,
usingPart = usingPart append QueryBuilder.timestamp(time.toString),
options = options
)
}

def timestamp(time: DateTime): DeleteQuery[Table, Record, Limit, Order, Status, Chainned, PS] = {
timestamp(time.getMillis)
}

/**
* The where method of a select query.
* @param condition A where clause condition restricted by path dependant types.
Expand All @@ -102,6 +118,7 @@ class DeleteQuery[
init = init,
wherePart = wherePart append QueryBuilder.Update.where(condition(table).qb),
casPart = casPart,
usingPart = usingPart,
options = options
)
}
Expand All @@ -120,6 +137,7 @@ class DeleteQuery[
init = init,
wherePart = wherePart append QueryBuilder.Update.where(condition(table).qb),
casPart = casPart,
usingPart = usingPart,
options = options
)
}
Expand All @@ -138,6 +156,7 @@ class DeleteQuery[
init = init,
wherePart = wherePart append query,
casPart = casPart,
usingPart = usingPart,
options = options
)
}
Expand Down Expand Up @@ -183,25 +202,29 @@ class DeleteQuery[
}
}


/**
* Generates a conditional query clause based on CQL lightweight transactions.
* Compare and set transactions only get executed if a particular condition is true.
*
* @param clause The Compare-And-Set clause to append to the builder.
* @return A conditional query, now bound by a compare-and-set part.
*/
def onlyIf(clause: Table => CompareAndSetClause.Condition): ConditionalDeleteQuery[Table, Record, Limit, Order, Status, Chain, PS] = {
def onlyIf(
clause: Table => CompareAndSetClause.Condition
): ConditionalDeleteQuery[Table, Record, Limit, Order, Status, Chain, PS] = {
new ConditionalDeleteQuery(
table = table,
init = init,
wherePart = wherePart,
casPart = casPart append QueryBuilder.Update.onlyIf(clause(table).qb),
usingPart = usingPart,
options = options
)
}

override val qb: CQLQuery = {
(wherePart merge casPart) build init
(wherePart merge usingPart merge casPart) build init
}

}
Expand Down Expand Up @@ -242,11 +265,12 @@ sealed class ConditionalDeleteQuery[
val init: CQLQuery,
wherePart : WherePart = WherePart.empty,
casPart : CompareAndSetPart = CompareAndSetPart.empty,
usingPart: UsingPart = UsingPart.empty,
override val options: QueryOptions
) extends ExecutableStatement with Batchable {

override val qb: CQLQuery = {
(wherePart merge casPart) build init
(wherePart merge usingPart merge casPart) build init
}

final def and(clause: Table => CompareAndSetClause.Condition): ConditionalDeleteQuery[Table, Record, Limit, Order, Status, Chain, PS] = {
Expand All @@ -255,6 +279,7 @@ sealed class ConditionalDeleteQuery[
init,
wherePart,
casPart append QueryBuilder.Update.and(clause(table).qb),
usingPart,
options
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ package com.websudos.phantom.builder.query
import com.datastax.driver.core.{ConsistencyLevel, Session}
import com.websudos.phantom.CassandraTable
import com.websudos.phantom.builder._
import com.websudos.phantom.builder.clauses.{OperatorClause, UsingClause}
import com.websudos.phantom.builder.clauses.{OperatorClause, TypedClause, UsingClause}
import com.websudos.phantom.builder.query.prepared.{PrepareMark, PreparedBlock}
import com.websudos.phantom.builder.syntax.CQLSyntax
import com.websudos.phantom.column.AbstractColumn
Expand Down Expand Up @@ -91,7 +91,34 @@ class InsertQuery[
)
}

final def value[RR](col: Table => AbstractColumn[RR], value: RR): InsertQuery[Table, Record, Status, PS] = {
/**
* Insert function adding the ability to specify operator values as the value of an insert.
* This is useful when we want to use functions to generate the CQL, such as using
* the "now()" operator when inserting the value of a date.
* @param col The function that selects a specific column from the table.
* @param value The value to insert in the column, based on the output of the operator.
* @tparam RR The type of the value held in the column.
* @return A new instance of insert query, with the clause added.
*/
def opValue[RR](
col: Table => AbstractColumn[RR],
value: OperatorClause.Condition
): InsertQuery[Table, Record, Status, PS] = {
new InsertQuery(
table,
init,
columnsPart append CQLQuery(col(table).name),
valuePart append value.qb,
usingPart,
lightweightPart,
options
)
}

def value[RR](
col: Table => AbstractColumn[RR],
value: RR
): InsertQuery[Table, Record, Status, PS] = {
new InsertQuery(
table,
init,
Expand All @@ -103,7 +130,10 @@ class InsertQuery[
)
}

final def p_value[RR](col: Table => AbstractColumn[RR], value: PrepareMark) : InsertQuery[Table, Record, Status, RR :: PS] = {
final def p_value[RR](
col: Table => AbstractColumn[RR],
value: PrepareMark
): InsertQuery[Table, Record, Status, RR :: PS] = {
new InsertQuery(
table,
init,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ class SelectFunctionsTesting extends PhantomSuite {
store <- database.timeuuidTable.store(record).ttl(timeToLive).future()
timestamp <- database.timeuuidTable.select.function(t => ttl(t.name))
.where(_.user eqs record.user)
.and(_.id eqs record.id).one()
.and(_.id eqs record.id)
.one()
} yield timestamp

whenReady(chain) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ class DeleteQueryBuilderTest extends QueryBuilderTest {
val qb = QueryBuilder.Delete.delete("table").queryString
qb shouldEqual "DELETE FROM table"
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,45 @@ class DeleteQuerySerialisationTest extends QueryBuilderTest {

qb shouldEqual s"DELETE props['test'], props['test2'] FROM phantom.recipes WHERE url = '$url';"
}
}

"should allow specifying a custom timestamp for deletes" - {
"should allow using a milliseconds Long value as a timestamp" in {
val value = gen[Long]
val url = gen[String]

val qb = TestDatabase.recipes
.delete.where(_.url eqs url)
.timestamp(value)
.queryString

qb shouldEqual s"DELETE FROM phantom.recipes WHERE url = '$url' USING TIMESTAMP $value;"
}

"should allow using a DateTime instance value as a timestamp" in {
val value = gen[DateTime]
val url = gen[String]

val qb = TestDatabase.recipes
.delete.where(_.url eqs url)
.timestamp(value)
.queryString

qb shouldEqual s"DELETE FROM phantom.recipes WHERE url = '$url' USING TIMESTAMP ${value.getMillis};"
}

"should allow mixing a timestamp clause with a conditional clause" in {
val value = gen[DateTime]
val url = gen[String]

val qb = TestDatabase.recipes
.delete.where(_.url eqs url)
.timestamp(value)
.onlyIf(_.lastcheckedat is value)
.queryString

qb shouldEqual s"DELETE FROM phantom.recipes WHERE url = '$url' USING TIMESTAMP ${value.getMillis} IF lastcheckedat = ${value.getMillis};"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ class InsertQuerySerializationTest extends QueryBuilderTest {
query shouldEqual "INSERT INTO phantom.recipes (url) VALUES('test') USING IGNORE_NULLS;"
}

"should allow using operator values as parts of the insert statements" in {
val query = TestDatabase.timeSeriesTable.insert
.opValue(_.id, now())
.queryString

query shouldEqual "INSERT INTO phantom.timeSeriesTable (id) VALUES(now());"
}

}
}

Expand Down

0 comments on commit ca30604

Please sign in to comment.