diff --git a/build.sbt b/build.sbt index 4c12b7f..7d2e4cb 100644 --- a/build.sbt +++ b/build.sbt @@ -25,9 +25,11 @@ lazy val troyDriver = project .in(file("troy-driver")) .settings(name := "troy-driver") .settings(libraryDependencies ++= Vector( + Library.scalaReflect, Library.scalaTest % Test, Library.mockito % Test, - Library.cassandraDriverCore + Library.cassandraDriverCore, + Library.shapeless )) lazy val troy = project @@ -62,17 +64,3 @@ lazy val troyMeta = project lazy val root = project.in(file(".")) .settings(name := "troy-root", publishArtifact := false, publish := {}, publishLocal := {}) .aggregate(troy, troyDriver, troySchema, cqlParser, cqlAst) - -initialCommands := """import java.util.UUID - |import troy.Troy - |import com.datastax.driver.core._ - |import scala.concurrent.duration.Duration - |import scala.concurrent.Await - |import scala.concurrent.ExecutionContext.Implicits.global - | - |val cluster = Cluster.builder().addContactPoint("127.0.0.1").build() - |implicit val session: Session = cluster.connect() - | - |import Troy._ - | - |""".stripMargin \ No newline at end of file diff --git a/cql-ast/src/main/scala/troy/cql/ast/CQL3.scala b/cql-ast/src/main/scala/troy/cql/ast/CQL3.scala index ce4a285..81435f0 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/CQL3.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/CQL3.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.cql.ast +package troy +package cql.ast trait ConsistencyLevel object ConsistencyLevel { diff --git a/cql-ast/src/main/scala/troy/cql/ast/DataType.scala b/cql-ast/src/main/scala/troy/cql/ast/DataType.scala index 2cf1865..1eaa5cb 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/DataType.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/DataType.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.cql.ast +package troy +package cql.ast sealed trait DataType diff --git a/cql-ast/src/main/scala/troy/cql/ast/OptionInstruction.scala b/cql-ast/src/main/scala/troy/cql/ast/OptionInstruction.scala index 1f8c690..6aa38e0 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/OptionInstruction.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/OptionInstruction.scala @@ -1,4 +1,5 @@ -package troy.cql.ast +package troy +package cql.ast sealed trait OptionInstruction diff --git a/cql-ast/src/main/scala/troy/cql/ast/Statements.scala b/cql-ast/src/main/scala/troy/cql/ast/Statements.scala index 84acff8..c5b9921 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/Statements.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/Statements.scala @@ -1,4 +1,5 @@ -package troy.cql.ast +package troy +package cql.ast import troy.cql.ast.dml._ import troy.cql.ast.ddl._ diff --git a/cql-ast/src/main/scala/troy/cql/ast/Term.scala b/cql-ast/src/main/scala/troy/cql/ast/Term.scala index 1e746b7..4fbcdf9 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/Term.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/Term.scala @@ -1,4 +1,5 @@ -package troy.cql.ast +package troy +package cql.ast import java.util.UUID diff --git a/cql-ast/src/main/scala/troy/cql/ast/ddl/Alter.scala b/cql-ast/src/main/scala/troy/cql/ast/ddl/Alter.scala index eb8fcd0..9bfec4e 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/ddl/Alter.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/ddl/Alter.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.ddl +package troy +package cql.ast.ddl import troy.cql.ast.{ DataType, OptionInstruction } import troy.cql.ast.Identifier diff --git a/cql-ast/src/main/scala/troy/cql/ast/ddl/Field.scala b/cql-ast/src/main/scala/troy/cql/ast/ddl/Field.scala index b8f790f..0ac8ea1 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/ddl/Field.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/ddl/Field.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.ddl +package troy +package cql.ast.ddl import troy.cql.ast.{ DataType, Identifier } diff --git a/cql-ast/src/main/scala/troy/cql/ast/ddl/Index.scala b/cql-ast/src/main/scala/troy/cql/ast/ddl/Index.scala index b1a9f6c..1cef313 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/ddl/Index.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/ddl/Index.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.ddl +package troy +package cql.ast.ddl import troy.cql.ast.MapLiteral diff --git a/cql-ast/src/main/scala/troy/cql/ast/ddl/Keyspace.scala b/cql-ast/src/main/scala/troy/cql/ast/ddl/Keyspace.scala index e5ef7ef..57479ad 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/ddl/Keyspace.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/ddl/Keyspace.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.ddl +package troy +package cql.ast.ddl object Keyspace { sealed trait KeyspaceOption diff --git a/cql-ast/src/main/scala/troy/cql/ast/ddl/Table.scala b/cql-ast/src/main/scala/troy/cql/ast/ddl/Table.scala index 70e9135..02345d4 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/ddl/Table.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/ddl/Table.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.ddl +package troy +package cql.ast.ddl import troy.cql.ast.DataType diff --git a/cql-ast/src/main/scala/troy/cql/ast/dml/Condition.scala b/cql-ast/src/main/scala/troy/cql/ast/dml/Condition.scala index f081c56..8c22089 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/dml/Condition.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/dml/Condition.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.dml +package troy +package cql.ast.dml import troy.cql.ast.Term diff --git a/cql-ast/src/main/scala/troy/cql/ast/dml/Insert.scala b/cql-ast/src/main/scala/troy/cql/ast/dml/Insert.scala index 63b5d6f..1bfa4a5 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/dml/Insert.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/dml/Insert.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.dml +package troy +package cql.ast.dml import troy.cql.ast._ diff --git a/cql-ast/src/main/scala/troy/cql/ast/dml/SimpleSelection.scala b/cql-ast/src/main/scala/troy/cql/ast/dml/SimpleSelection.scala index 87362c5..d5155ac 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/dml/SimpleSelection.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/dml/SimpleSelection.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.dml +package troy +package cql.ast.dml import troy.cql.ast.{ Term, _ } diff --git a/cql-ast/src/main/scala/troy/cql/ast/dml/Update.scala b/cql-ast/src/main/scala/troy/cql/ast/dml/Update.scala index c38e34a..35c9d95 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/dml/Update.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/dml/Update.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.dml +package troy +package cql.ast.dml import troy.cql.ast.{ BindMarker, Identifier, ListLiteral, Term } diff --git a/cql-ast/src/main/scala/troy/cql/ast/dml/UpdateParam.scala b/cql-ast/src/main/scala/troy/cql/ast/dml/UpdateParam.scala index 9fed91e..aad401e 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/dml/UpdateParam.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/dml/UpdateParam.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.dml +package troy +package cql.ast.dml import troy.cql.ast.BindMarker diff --git a/cql-ast/src/main/scala/troy/cql/ast/dml/WhereClause.scala b/cql-ast/src/main/scala/troy/cql/ast/dml/WhereClause.scala index c4ff625..b2c9d31 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/dml/WhereClause.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/dml/WhereClause.scala @@ -1,4 +1,5 @@ -package troy.cql.ast.dml +package troy +package cql.ast.dml import troy.cql.ast.{ Identifier, Term, TupleLiteral } diff --git a/cql-ast/src/main/scala/troy/cql/ast/package.scala b/cql-ast/src/main/scala/troy/cql/ast/package.scala index c94445a..24f93a9 100644 --- a/cql-ast/src/main/scala/troy/cql/ast/package.scala +++ b/cql-ast/src/main/scala/troy/cql/ast/package.scala @@ -1,4 +1,5 @@ -package troy.cql +package troy +package cql package object ast { type Identifier = String diff --git a/cql-parser/src/main/scala/troy/cql/parser/CqlParser.scala b/cql-parser/src/main/scala/troy/cql/parser/CqlParser.scala index 8954ccd..5896bfd 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/CqlParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/CqlParser.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.cql.ast +package troy +package cql.ast import java.util.UUID diff --git a/cql-parser/src/main/scala/troy/cql/parser/Helpers.scala b/cql-parser/src/main/scala/troy/cql/parser/Helpers.scala index d4c3fad..748b5cb 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/Helpers.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/Helpers.scala @@ -1,4 +1,5 @@ -package troy.cql.parser +package troy +package cql.parser import scala.util.parsing.combinator.JavaTokenParsers diff --git a/cql-parser/src/main/scala/troy/cql/parser/TermParser.scala b/cql-parser/src/main/scala/troy/cql/parser/TermParser.scala index b1f9e46..1e0422f 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/TermParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/TermParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser +package troy +package cql.parser import troy.cql.ast.CqlParser._ import troy.cql.ast._ diff --git a/cql-parser/src/main/scala/troy/cql/parser/ddl/AlterTableParser.scala b/cql-parser/src/main/scala/troy/cql/parser/ddl/AlterTableParser.scala index f116ab0..a9127a9 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/ddl/AlterTableParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/ddl/AlterTableParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import troy.cql.ast.AlterTable import troy.cql.ast.CqlParser._ diff --git a/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateIndexParser.scala b/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateIndexParser.scala index 821214c..1a9619c 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateIndexParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateIndexParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import troy.cql.ast.CqlParser._ import troy.cql.ast.CreateIndex diff --git a/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateKeyspaceParser.scala b/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateKeyspaceParser.scala index e5dd582..f4aca3c 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateKeyspaceParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateKeyspaceParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import troy.cql.ast.CqlParser._ import troy.cql.ast.CreateKeyspace diff --git a/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateTableParser.scala b/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateTableParser.scala index 81a6f77..d996afa 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateTableParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateTableParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import troy.cql.ast.CqlParser._ import troy.cql.ast.ddl.Table._ diff --git a/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateTypeParser.scala b/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateTypeParser.scala index 521e209..8842e41 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateTypeParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/ddl/CreateTypeParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import troy.cql.ast.CqlParser._ import troy.cql.ast.CreateType diff --git a/cql-parser/src/main/scala/troy/cql/parser/ddl/UseStatementParser.scala b/cql-parser/src/main/scala/troy/cql/parser/ddl/UseStatementParser.scala index ef64880..78190c1 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/ddl/UseStatementParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/ddl/UseStatementParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import troy.cql.ast.CqlParser._ import troy.cql.ast.UseStatement diff --git a/cql-parser/src/main/scala/troy/cql/parser/dml/DeleteStatementParser.scala b/cql-parser/src/main/scala/troy/cql/parser/dml/DeleteStatementParser.scala index af4eb31..e0af41a 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/dml/DeleteStatementParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/dml/DeleteStatementParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.dml +package troy +package cql.parser.dml import troy.cql.ast.CqlParser._ import troy.cql.ast.DeleteStatement diff --git a/cql-parser/src/main/scala/troy/cql/parser/dml/InsertStatementParser.scala b/cql-parser/src/main/scala/troy/cql/parser/dml/InsertStatementParser.scala index d1ac8e4..64f0214 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/dml/InsertStatementParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/dml/InsertStatementParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.dml +package troy +package cql.parser.dml import troy.cql.ast.CqlParser._ import troy.cql.ast.InsertStatement diff --git a/cql-parser/src/main/scala/troy/cql/parser/dml/SelectStatementParser.scala b/cql-parser/src/main/scala/troy/cql/parser/dml/SelectStatementParser.scala index e0c2bdc..c13f364 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/dml/SelectStatementParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/dml/SelectStatementParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.dml +package troy +package cql.parser.dml import troy.cql.ast.CqlParser._ import troy.cql.ast.SelectStatement import troy.cql.ast.dml.Select diff --git a/cql-parser/src/main/scala/troy/cql/parser/dml/UpdateStatementParser.scala b/cql-parser/src/main/scala/troy/cql/parser/dml/UpdateStatementParser.scala index c694cf2..200a5c3 100644 --- a/cql-parser/src/main/scala/troy/cql/parser/dml/UpdateStatementParser.scala +++ b/cql-parser/src/main/scala/troy/cql/parser/dml/UpdateStatementParser.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.dml +package troy +package cql.parser.dml import troy.cql.ast.CqlParser._ import troy.cql.ast.UpdateStatement diff --git a/cql-parser/src/test/scala/troy/cql/parser/CqlParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/CqlParserTest.scala index 09571f9..76319e4 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/CqlParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/CqlParserTest.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.cql.parser +package troy +package cql.parser import org.scalatest._ import troy.cql.ast._ diff --git a/cql-parser/src/test/scala/troy/cql/parser/CreateTypeParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/CreateTypeParserTest.scala index 7a152f2..e55bbc4 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/CreateTypeParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/CreateTypeParserTest.scala @@ -1,4 +1,5 @@ -package troy.cql.parser +package troy +package cql.parser import org.scalatest._ import troy.cql.ast.ddl.Field diff --git a/cql-parser/src/test/scala/troy/cql/parser/ParserTestUtils.scala b/cql-parser/src/test/scala/troy/cql/parser/ParserTestUtils.scala index de9f0ba..3465fbc 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/ParserTestUtils.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/ParserTestUtils.scala @@ -1,4 +1,5 @@ -package troy.cql.parser +package troy +package cql.parser import org.scalatest.{ FlatSpec, Matchers } import troy.cql.ast._ diff --git a/cql-parser/src/test/scala/troy/cql/parser/ddl/AlterTableParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/ddl/AlterTableParserTest.scala index f921ad7..853f410 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/ddl/AlterTableParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/ddl/AlterTableParserTest.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import org.scalatest.{ FlatSpec, Matchers } import troy.cql.ast._ diff --git a/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateIndexParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateIndexParserTest.scala index 5df67c0..6e42602 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateIndexParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateIndexParserTest.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import org.scalatest.{ FlatSpec, Matchers } import troy.cql.ast.ddl.Index diff --git a/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateKeyspaceParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateKeyspaceParserTest.scala index c4748ca..b0e8062 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateKeyspaceParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateKeyspaceParserTest.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import org.scalatest._ import troy.cql.ast.CreateKeyspace diff --git a/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateTableParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateTableParserTest.scala index b1ef951..24757c7 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateTableParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/ddl/CreateTableParserTest.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.ddl +package troy +package cql.parser.ddl import org.scalatest._ import troy.cql.ast.ddl.Table diff --git a/cql-parser/src/test/scala/troy/cql/parser/dml/DeleteStatementParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/dml/DeleteStatementParserTest.scala index 0446dba..5e25b7a 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/dml/DeleteStatementParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/dml/DeleteStatementParserTest.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.dml +package troy +package cql.parser.dml import java.util.UUID diff --git a/cql-parser/src/test/scala/troy/cql/parser/dml/InsertStatementParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/dml/InsertStatementParserTest.scala index 67ee186..8cedec5 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/dml/InsertStatementParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/dml/InsertStatementParserTest.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.dml +package troy +package cql.parser.dml import org.scalatest.{ FlatSpec, Matchers } import troy.cql.ast.dml.{ Insert, Timestamp, Ttl, UpdateValue } diff --git a/cql-parser/src/test/scala/troy/cql/parser/dml/SelectStatementParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/dml/SelectStatementParserTest.scala index ed21832..0f25404 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/dml/SelectStatementParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/dml/SelectStatementParserTest.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.cql.parser.dml +package troy +package cql.parser.dml import java.util.UUID diff --git a/cql-parser/src/test/scala/troy/cql/parser/dml/UpdateStatementParserTest.scala b/cql-parser/src/test/scala/troy/cql/parser/dml/UpdateStatementParserTest.scala index 2e2c1c1..c528f2a 100644 --- a/cql-parser/src/test/scala/troy/cql/parser/dml/UpdateStatementParserTest.scala +++ b/cql-parser/src/test/scala/troy/cql/parser/dml/UpdateStatementParserTest.scala @@ -1,4 +1,5 @@ -package troy.cql.parser.dml +package troy +package cql.parser.dml import java.util.UUID diff --git a/cql-parser/src/test/scala/troy/cql3_3/lexical/CaseSensitivityParsingSpec.scala b/cql-parser/src/test/scala/troy/cql3_3/lexical/CaseSensitivityParsingSpec.scala index c2a1802..bb09ecf 100644 --- a/cql-parser/src/test/scala/troy/cql3_3/lexical/CaseSensitivityParsingSpec.scala +++ b/cql-parser/src/test/scala/troy/cql3_3/lexical/CaseSensitivityParsingSpec.scala @@ -1,4 +1,5 @@ -package troy.cql3_3.lexical +package troy +package cql3_3.lexical import org.scalatest.{ FlatSpec, Matchers } import troy.cql.ast.dml.Select.{ ColumnName, SelectionClauseItem, SelectClause } diff --git a/cql-parser/src/test/scala/troy/cql3_3/lexical/EscapingCharactersParsingSpec.scala b/cql-parser/src/test/scala/troy/cql3_3/lexical/EscapingCharactersParsingSpec.scala index 991cb11..a9f7dce 100644 --- a/cql-parser/src/test/scala/troy/cql3_3/lexical/EscapingCharactersParsingSpec.scala +++ b/cql-parser/src/test/scala/troy/cql3_3/lexical/EscapingCharactersParsingSpec.scala @@ -1,4 +1,5 @@ -package troy.cql3_3.lexical +package troy +package cql3_3.lexical import org.scalatest.{ FlatSpec, Matchers } import troy.cql.ast.StringConstant diff --git a/cql-parser/src/test/scala/troy/cql3_3/lexical/KeywordsParsingSpec.scala b/cql-parser/src/test/scala/troy/cql3_3/lexical/KeywordsParsingSpec.scala index abb39a4..ee6240a 100644 --- a/cql-parser/src/test/scala/troy/cql3_3/lexical/KeywordsParsingSpec.scala +++ b/cql-parser/src/test/scala/troy/cql3_3/lexical/KeywordsParsingSpec.scala @@ -1,4 +1,5 @@ -package troy.cql3_3.lexical +package troy +package cql3_3.lexical import org.scalatest.{ FlatSpec, Matchers } import troy.cql.parser.ParserTestUtils._ diff --git a/cql-parser/src/test/scala/troy/cql3_4/lexical/KeywordsParsingSpec.scala b/cql-parser/src/test/scala/troy/cql3_4/lexical/KeywordsParsingSpec.scala index 55abe1e..33a5aea 100644 --- a/cql-parser/src/test/scala/troy/cql3_4/lexical/KeywordsParsingSpec.scala +++ b/cql-parser/src/test/scala/troy/cql3_4/lexical/KeywordsParsingSpec.scala @@ -1,4 +1,5 @@ -package troy.cql3_4.lexical +package troy +package cql3_4.lexical import org.scalatest.{ FlatSpec, Matchers } import troy.cql.parser.ParserTestUtils._ diff --git a/project/Build.scala b/project/Build.scala index ba0c16f..9dc054e 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -18,7 +18,8 @@ object Build extends AutoPlugin { organization := "io.github.cassandra-scala", licenses += ("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0")), scalaVersion := Version.Scala, - crossScalaVersions := Vector("2.11.8", "2.12.0"), + scalaOrganization := "org.typelevel", // FIXME: Remove once literal-types is merged into lightbend Scala + crossScalaVersions := Vector("2.11.8", "2.12.1"), scalacOptions ++= Vector( "-encoding", "UTF-8", "-target:jvm-1.8", @@ -31,8 +32,10 @@ object Build extends AutoPlugin { "-Ywarn-dead-code", "-Ywarn-unused-import", "-Ywarn-unused", - "-Ywarn-nullary-unit" + "-Ywarn-nullary-unit", + "-Yliteral-types" ), + scalacOptions in (Compile, console) := Seq("-Yliteral-types"), unmanagedSourceDirectories.in(Compile) := Vector(scalaSource.in(Compile).value), unmanagedSourceDirectories.in(Test) := Vector(scalaSource.in(Test).value) ) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index d8fa1a7..aacf0c4 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -15,6 +15,7 @@ object Library { val scalaParserCombinators = "org.scala-lang.modules" %% "scala-parser-combinators" % Version.ScalaParserCombinators val cassandraDriverCore = "com.datastax.cassandra" % "cassandra-driver-core" % "3.0.0" val cassandraUnit = "org.cassandraunit" % "cassandra-unit" % "3.0.0.1" - val scalaMeta = "org.scalameta" %% "scalameta" % "1.0.0" + val scalaMeta = "org.scalameta" %% "scalameta" % "1.4.0" val macroParadise = "org.scalamacros" % s"paradise_${Version.Scala}" % "3.0.0-M3" + val shapeless = "com.chuusai" %% "shapeless" % "2.3.2" } diff --git a/troy-driver/src/main/scala/troy/driver/CassandraDataType.scala b/troy-driver/src/main/scala/troy/driver/CassandraDataType.scala index aa182ce..9cb2a4e 100644 --- a/troy-driver/src/main/scala/troy/driver/CassandraDataType.scala +++ b/troy-driver/src/main/scala/troy/driver/CassandraDataType.scala @@ -1,4 +1,5 @@ -package troy.driver +package troy +package driver /** * Represents Cassandra Types diff --git a/troy-driver/src/main/scala/troy/driver/Dsl.scala b/troy-driver/src/main/scala/troy/driver/Dsl.scala index 622bbe4..299b423 100644 --- a/troy-driver/src/main/scala/troy/driver/Dsl.scala +++ b/troy-driver/src/main/scala/troy/driver/Dsl.scala @@ -1,4 +1,5 @@ -package troy.driver +package troy +package driver import com.datastax.driver.core.{ Session, Row, ResultSet, Statement } diff --git a/troy-driver/src/main/scala/troy/driver/InternalDsl.scala b/troy-driver/src/main/scala/troy/driver/InternalDsl.scala index 92390ed..4fe25cf 100644 --- a/troy-driver/src/main/scala/troy/driver/InternalDsl.scala +++ b/troy-driver/src/main/scala/troy/driver/InternalDsl.scala @@ -1,4 +1,5 @@ -package troy.driver +package troy +package driver import com.datastax.driver.core._ import troy.driver.codecs.TroyCodec @@ -13,16 +14,16 @@ object InternalDsl { val CDT = CassandraDataType def column[S](i: Int)(implicit row: GettableByIndexData) = new { - def as[C <: CassandraDataType](implicit getter: TroyCodec[S, C]): S = + def as[C <: CassandraDataType](implicit getter: TroyCodec[C, S]): S = getter.get(row, i) } def param[S](value: S) = new { - def as[C <: CassandraDataType](implicit setter: TroyCodec[S, C]) = + def as[C <: CassandraDataType](implicit setter: TroyCodec[C, S]) = Param[S, C](value, setter) } - case class Param[S, C <: CassandraDataType](value: S, setter: TroyCodec[S, C]) { + case class Param[S, C <: CassandraDataType](value: S, setter: TroyCodec[C, S]) { def set(bs: BoundStatement, i: Int) = setter.set(bs, i, value) } diff --git a/troy-driver/src/main/scala/troy/driver/JavaConverters.scala b/troy-driver/src/main/scala/troy/driver/JavaConverters.scala index 2f55b1c..b33d597 100644 --- a/troy-driver/src/main/scala/troy/driver/JavaConverters.scala +++ b/troy-driver/src/main/scala/troy/driver/JavaConverters.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.driver +package troy +package driver import com.google.common.util.concurrent.{ FutureCallback, Futures, ListenableFuture } diff --git a/troy-driver/src/main/scala/troy/driver/codecs/AdapterTypeCodec.scala b/troy-driver/src/main/scala/troy/driver/codecs/AdapterTypeCodec.scala index d2bfccc..031a63c 100644 --- a/troy-driver/src/main/scala/troy/driver/codecs/AdapterTypeCodec.scala +++ b/troy-driver/src/main/scala/troy/driver/codecs/AdapterTypeCodec.scala @@ -1,4 +1,5 @@ -package troy.driver.codecs +package troy +package driver.codecs import java.nio.ByteBuffer diff --git a/troy-driver/src/main/scala/troy/driver/codecs/HasTypeCodec.scala b/troy-driver/src/main/scala/troy/driver/codecs/HasTypeCodec.scala index 7b55eb0..9935db8 100644 --- a/troy-driver/src/main/scala/troy/driver/codecs/HasTypeCodec.scala +++ b/troy-driver/src/main/scala/troy/driver/codecs/HasTypeCodec.scala @@ -1,4 +1,5 @@ -package troy.driver.codecs +package troy +package driver.codecs import java.net.InetAddress import java.nio.ByteBuffer diff --git a/troy-driver/src/main/scala/troy/driver/codecs/PrimitivesCodecs.scala b/troy-driver/src/main/scala/troy/driver/codecs/PrimitivesCodecs.scala index e1daa63..20e9d84 100644 --- a/troy-driver/src/main/scala/troy/driver/codecs/PrimitivesCodecs.scala +++ b/troy-driver/src/main/scala/troy/driver/codecs/PrimitivesCodecs.scala @@ -1,4 +1,5 @@ -package troy.driver.codecs +package troy +package driver.codecs import com.datastax.driver.core.{ SettableByIndexData, GettableByIndexData } import troy.driver.{ CassandraDataType => CT } @@ -33,55 +34,54 @@ object PrimitivesConverter { ) } -object PrimitivesCodecs { +trait PrimitivesCodecs { - implicit val intAsInt = new TroyCodec[Int, CT.Int] { + implicit val intAsInt = new TroyCodec[CT.Int, Int] { override def get(gettable: GettableByIndexData, i: Int): Int = gettable.getInt(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Int): T = settable.setInt(i, v) } - implicit val bigIntAsLong = new TroyCodec[Long, CT.BigInt] { + implicit val bigIntAsLong = new TroyCodec[CT.BigInt, Long] { override def get(gettable: GettableByIndexData, i: Int): Long = gettable.getLong(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Long): T = settable.setLong(i, v) } - implicit val counterAsLong = new TroyCodec[Long, CT.Counter] { + implicit val counterAsLong = new TroyCodec[CT.Counter, Long] { override def get(gettable: GettableByIndexData, i: Int): Long = gettable.getLong(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Long): T = settable.setLong(i, v) } - implicit val timeAsLong = new TroyCodec[Long, CT.Time] { + implicit val timeAsLong = new TroyCodec[CT.Time, Long] { override def get(gettable: GettableByIndexData, i: Int): Long = gettable.getLong(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Long): T = settable.setLong(i, v) } - implicit val doubleAsDouble = new TroyCodec[Double, CT.Double] { + implicit val doubleAsDouble = new TroyCodec[CT.Double, Double] { override def get(gettable: GettableByIndexData, i: Int): Double = gettable.getDouble(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Double): T = settable.setDouble(i, v) } - implicit val booleanAsBoolean = new TroyCodec[Boolean, CT.Boolean] { + implicit val booleanAsBoolean = new TroyCodec[CT.Boolean, Boolean] { override def get(gettable: GettableByIndexData, i: Int): Boolean = gettable.getBool(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Boolean): T = settable.setBool(i, v) } - implicit val smallIntAsShort = new TroyCodec[Short, CT.SmallInt] { + implicit val smallIntAsShort = new TroyCodec[CT.SmallInt, Short] { override def get(gettable: GettableByIndexData, i: Int): Short = gettable.getShort(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Short): T = settable.setShort(i, v) } - - implicit val tinyIntAsByte = new TroyCodec[Byte, CT.TinyInt] { + implicit val tinyIntAsByte = new TroyCodec[CT.TinyInt, Byte] { override def get(gettable: GettableByIndexData, i: Int): Byte = gettable.getByte(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Byte): T = settable.setByte(i, v) } - implicit val floatAsFloat = new TroyCodec[Float, CT.Float] { + implicit val floatAsFloat = new TroyCodec[CT.Float, Float] { override def get(gettable: GettableByIndexData, i: Int): Float = gettable.getFloat(i) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, v: Float): T = settable.setFloat(i, v) } - implicit def troyOptionalPrimitiveTypeCodec[S <: AnyVal, C <: CT](implicit inner: TroyCodec[S, C]) = - new TroyCodec[Option[S], C] { + implicit def troyOptionalPrimitiveTypeCodec[S <: AnyVal, C <: CT](implicit inner: TroyCodec[C, S]) = + new TroyCodec[C, Option[S]] { override def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: Option[S]) = value.map(inner.set(settable, i, _)).getOrElse(settable.setToNull(i)) @@ -92,3 +92,5 @@ object PrimitivesCodecs { Some(inner.get(gettable, i)) } } + +object PrimitivesCodecs extends PrimitivesCodecs \ No newline at end of file diff --git a/troy-driver/src/main/scala/troy/driver/codecs/TroyCodec.scala b/troy-driver/src/main/scala/troy/driver/codecs/TroyCodec.scala index b5b1f01..c0001e6 100644 --- a/troy-driver/src/main/scala/troy/driver/codecs/TroyCodec.scala +++ b/troy-driver/src/main/scala/troy/driver/codecs/TroyCodec.scala @@ -1,4 +1,5 @@ -package troy.driver.codecs +package troy +package driver.codecs import com.datastax.driver.core._ import scala.annotation.implicitNotFound @@ -6,60 +7,60 @@ import scala.collection.JavaConverters._ import troy.driver.{ CassandraDataType => CT } @implicitNotFound("Incompatible column type ${S} <--> ${C}") -trait TroyCodec[S, C <: CT] { +trait TroyCodec[C <: CT, S] { def get(gettable: GettableByIndexData, i: Int): S def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: S): T } -object TroyCodec { +object TroyCodec extends PrimitivesCodecs { - def apply[S <: AnyRef, C <: CT](typeCodec: TypeCodec[S]) = - new TroyCodec[S, C] { + def instance[C <: CT, S](typeCodec: TypeCodec[S]) = + new TroyCodec[C, S] { override def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: S) = settable.set(i, value, typeCodec) override def get(gettable: GettableByIndexData, i: Int) = gettable.get(i, typeCodec) } implicit def wrapJavaTypeCodecs[S <: AnyRef, C <: CT](implicit hasTypeCodec: HasTypeCodec[S, C]) = - TroyCodec[S, C](hasTypeCodec.typeCodec) + instance[C, S](hasTypeCodec.typeCodec) - implicit def wrapOptional[S <: AnyRef, C <: CT](implicit hasTypeCodec: HasTypeCodec[S, C]) = - TroyCodec[Option[S], C](new OptionTypeCodec(hasTypeCodec.typeCodec)) + implicit def wrapOptional[C <: CT, S <: AnyRef](implicit hasTypeCodec: HasTypeCodec[S, C]) = + instance[C, Option[S]](new OptionTypeCodec(hasTypeCodec.typeCodec)) - implicit def listOfNonPrimitives[S <: AnyRef, C <: CT.Native](implicit inner: HasTypeCodec[S, C]) = - new TroyCodec[Seq[S], CT.List[C]] { + implicit def listOfNonPrimitives[C <: CT.Native, S <: AnyRef](implicit inner: HasTypeCodec[S, C]) = + new TroyCodec[CT.List[C], Seq[S]] { val codec = TypeCodec.list(inner.typeCodec) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: Seq[S]) = settable.set(i, value.asJava, codec) override def get(gettable: GettableByIndexData, i: Int) = gettable.get(i, codec).asScala } - implicit def listOfPrimitives[J <: AnyRef, S <: AnyVal, C <: CT.Native](implicit inner: TroyCodec[Seq[J], CT.List[C]], converter: PrimitivesConverter[J, S]) = - new TroyCodec[Seq[S], CT.List[C]] { + implicit def listOfPrimitives[J <: AnyRef, S <: AnyVal, C <: CT.Native](implicit inner: TroyCodec[CT.List[C], Seq[J]], converter: PrimitivesConverter[J, S]) = + new TroyCodec[CT.List[C], Seq[S]] { override def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: Seq[S]) = inner.set(settable, i, value.map(converter.toJava)) override def get(gettable: GettableByIndexData, i: Int) = inner.get(gettable, i).map(converter.toScala) } implicit def setOfNonPrimitives[S <: AnyRef, C <: CT.Native](implicit inner: HasTypeCodec[S, C]) = - new TroyCodec[Set[S], CT.Set[C]] { + new TroyCodec[CT.Set[C], Set[S]] { val codec = TypeCodec.set(inner.typeCodec) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: Set[S]) = settable.set(i, value.asJava, codec) override def get(gettable: GettableByIndexData, i: Int) = gettable.get(i, codec).asScala.toSet } - implicit def setOfPrimitives[J <: AnyRef, S <: AnyVal, C <: CT.Native](implicit inner: TroyCodec[Set[J], CT.Set[C]], converter: PrimitivesConverter[J, S]) = - new TroyCodec[Set[S], CT.Set[C]] { + implicit def setOfPrimitives[J <: AnyRef, S <: AnyVal, C <: CT.Native](implicit inner: TroyCodec[CT.Set[C], Set[J]], converter: PrimitivesConverter[J, S]) = + new TroyCodec[CT.Set[C], Set[S]] { override def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: Set[S]) = inner.set(settable, i, value.map(converter.toJava)) override def get(gettable: GettableByIndexData, i: Int) = inner.get(gettable, i).map(converter.toScala) } implicit def mapOfNonPrimitives[KS <: AnyRef, KC <: CT.Native, VS <: AnyRef, VC <: CT.Native](implicit keyInner: HasTypeCodec[KS, KC], valueInner: HasTypeCodec[VS, VC]) = - new TroyCodec[Map[KS, VS], CT.Map[KC, VC]] { + new TroyCodec[CT.Map[KC, VC], Map[KS, VS]] { val codec = TypeCodec.map(keyInner.typeCodec, valueInner.typeCodec) override def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: Map[KS, VS]) = settable.set(i, value.asJava, codec) override def get(gettable: GettableByIndexData, i: Int) = gettable.get(i, codec).asScala.toMap } - implicit def mapOfPrimitives[KJ, KS, KC <: CT.Native, VJ, VS, VC <: CT.Native](implicit inner: TroyCodec[Map[KJ, VJ], CT.Map[KC, VC]], converter: PrimitivesConverter[(KJ, VJ), (KS, VS)]) = - new TroyCodec[Map[KS, VS], CT.Map[KC, VC]] { + implicit def mapOfPrimitives[KJ, KS, KC <: CT.Native, VJ, VS, VC <: CT.Native](implicit inner: TroyCodec[CT.Map[KC, VC], Map[KJ, VJ]], converter: PrimitivesConverter[(KJ, VJ), (KS, VS)]) = + new TroyCodec[CT.Map[KC, VC], Map[KS, VS]] { override def set[T <: SettableByIndexData[T]](settable: T, i: Int, value: Map[KS, VS]) = inner.set(settable, i, value.map(converter.toJava)) override def get(gettable: GettableByIndexData, i: Int) = inner.get(gettable, i).map(converter.toScala) } diff --git a/troy-driver/src/main/scala/troy/driver/query/select/Select.scala b/troy-driver/src/main/scala/troy/driver/query/select/Select.scala new file mode 100644 index 0000000..cf5bb3c --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/query/select/Select.scala @@ -0,0 +1,142 @@ +package troy +package driver.query.select + +import com.datastax.driver.core._ +import shapeless._ +import shapeless.ops.hlist.ZipWithIndex +import shapeless.ops.nat.ToInt +import troy.driver.CassandraDataType +import troy.driver.schema.{ KeyspaceExists, TableExists, VersionExists } +import troy.driver.JavaConverters.RichListenableFuture +import troy.driver.codecs.TroyCodec +import troy.driver.JavaConverters.RichListenableFuture + +import scala.annotation.implicitNotFound +import scala.collection.JavaConverters._ +import scala.concurrent.{ ExecutionContext, Future } +import scala.reflect.macros.blackbox.Context + +/* + * PInfo: Compile time info about schema + * Params: type of params to be sent in runtime + */ +trait StatementBinder[PInfo, Params] { + def bind(bs: BoundStatement, params: Params): BoundStatement +} + + +object StatementBinder { + def instance[PInfo, Params <: HList](binder: (BoundStatement, Params) => BoundStatement) = new StatementBinder[PInfo, Params] { + override def bind(bs: BoundStatement, params: Params): BoundStatement = binder(bs, params) + } + + implicit val hNilInstance = instance[HNil, HNil]((s, _) => s) + + implicit def hListInstance[Index <: Nat, CassandraType <: CassandraDataType, ScalaType, PInfoTail <: HList, ParamsTail <: HList]( + implicit + index: ToInt[Index], + headCodec: TroyCodec[CassandraType, ScalaType], + tailBinder: StatementBinder[PInfoTail, ParamsTail] + ) = instance[(CassandraType, Index) :: PInfoTail, ScalaType :: ParamsTail] { (statement, params) => + val newS = headCodec.set(statement, Nat.toInt[Index], params.head) + tailBinder.bind(newS, params.tail) + } + + // + // implicit def implicitNotFoundMacro[X, Ps <: HList]: StatementBinder[X, Ps] = macro implicitNotFoundMacroImpl[Ps] + // + // def implicitNotFoundMacroImpl[Ps](c: Context) = c.abort(c.enclosingPosition, s"a7a need 3ala fekra") +} + +/* + * QInfo: Compile time info about schema + * Out: generic representation of required Scala Row representation + */ +trait RowParser[QInfo, Out <: HList] { + def parse(row: Row): Out +} +object RowParser { + def instance[QInfo, Out <: HList](p: Row => Out) = new RowParser[QInfo, Out] { + override def parse(row: Row): Out = p(row) + } + + implicit val hNilInstance = instance[HNil, HNil](_ => HNil) + + implicit def hListInstance[CassandraType <: CassandraDataType, Index <: Nat, QInfoTail <: HList, ScalaType, OutTail <: HList]( + implicit + index: ToInt[Index], + headCodec: TroyCodec[CassandraType, ScalaType], + tailParser: RowParser[QInfoTail, OutTail] + ) = instance[(CassandraType, Index) :: QInfoTail, ScalaType :: OutTail] { row => + headCodec.get(row, Nat.toInt[Index]) :: tailParser.parse(row) + } +} + +trait Select[I, O] { + def apply(input: I): Future[Iterable[O]] +} + +object Select { + // TODO + // trait Aliased[S, Alias] + trait Column[Name] + trait Function[Keyspace, Name, Params <: HList] + // trait Asterisk + trait NoKeyspace // Used to mark absence of a specific keyspace + + trait Relation[ColumnName, O, T] + + // trait Operator + /** + * Denotes ==, <, >, >, <=, >=, != + */ + trait Equality + trait In + trait Contains + trait ContainsKey + trait Like + + trait Term + trait AnonymousBindMarker extends Term + trait NamedBindMarker[Name] extends Term + + object Query { + /** + * Type Params to be sent by macro + */ + def apply[Version, Keyspace, Table, SelectonClause, Relations, QueryInputScalaType, QueryOutputRowScalaType] = + new Query[Version, Keyspace, Table, SelectonClause, Relations, QueryInputScalaType, QueryOutputRowScalaType] + } + class Query[Version, Keyspace, Table, SelectonClause, Relations, QueryInputScalaType, QueryOutputRowScalaType] { + /** + * Type Params to be inferred by compiler + */ + def apply[PInfo <: HList, IndexedPInfo <: HList, QInfo <: HList, IndexedQInfo <: HList, IRepr <: HList, ORepr <: HList](raw: String)( + implicit + versionExists: VersionExists[Version], + keyspaceExists: KeyspaceExists[Version, Keyspace], + tableExists: TableExists[Version, Keyspace, Table], + selection: Selection.Aux[Version, Keyspace, Table, SelectonClause, QInfo], + relations: Where.Aux[Version, Keyspace, Table, Relations, PInfo], + zipSelectionWithIndex: ZipWithIndex.Aux[QInfo, IndexedQInfo], + zipParamsWithIndex: ZipWithIndex.Aux[PInfo, IndexedPInfo], + iGen: Generic.Aux[QueryInputScalaType, IRepr], + oGen: Generic.Aux[QueryOutputRowScalaType, ORepr], + binder: StatementBinder[IndexedPInfo, IRepr], + parser: RowParser[IndexedQInfo, ORepr], + // preparationStrategy: PreparationStrategy.Aux[String, PreparedStatement], + // bindStrategy: BindStrategy.Aux[PreparedStatement, IRepr, BoundStatement], + // executionStrategy: ExecutionStrategy.Aux[BoundStatement, ResultSet], + // parsingStrategy: ParsingStrategy.Aux[ResultSet, ORepr] + session: Session, + ec: ExecutionContext + ) = new Select[QueryInputScalaType, QueryOutputRowScalaType] { + val prepared = session.prepare(raw) + override def apply(input: QueryInputScalaType): Future[Iterable[QueryOutputRowScalaType]] = { + val bound = binder.bind(prepared.bind, iGen.to(input)) + val resultSet = session.executeAsync(bound).asScala + resultSet.map(_.asScala.map(parser.parse).map(oGen.from)) + } + } + } +} diff --git a/troy-driver/src/main/scala/troy/driver/query/select/Selection.scala b/troy-driver/src/main/scala/troy/driver/query/select/Selection.scala new file mode 100644 index 0000000..a053ffc --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/query/select/Selection.scala @@ -0,0 +1,41 @@ +package troy +package driver.query.select + +import shapeless._ +import troy.driver.schema.FunctionType +import troy.driver.schema.column.ColumnType + +import scala.reflect.macros.blackbox.Context + +trait Selection[Version, Keyspace, Table, S] { + type Out <: HList +} + +object Selection { + import Select._ + + type Aux[V, K, T, S, O] = Selection[V, K, T, S] { type Out = O } + + def apply[V, K, T, S](implicit s: Selection[V, K, T, S]): Aux[V, K, T, S, s.Out] = s + + def instance[V, K, T, S, O <: HList]: Aux[V, K, T, S, O] = new Selection[V, K, T, S] { type Out = O } + + implicit def hNilInstance[V, K, T]: Aux[V, K, T, HNil, HNil] = instance[V, K, T, HNil, HNil] + + implicit def hColumnInstance[V, K, T, ColumnName, ST <: HList]( + implicit + headType: ColumnType[V, K, T, ColumnName], + tailType: Selection[V, K, T, ST] + ) = instance[V, K, T, Column[ColumnName] :: ST, headType.Out :: tailType.Out] + + implicit def hFunctionInstance[V, K, T, FKeyspace, FName, FParams <: HList, FParamTypes <: HList, ST <: HList]( + implicit + fParamTypes: Selection.Aux[V, K, T, FParams, FParamTypes], + headType: FunctionType[V, FKeyspace, FName, FParamTypes], + tailType: Selection[V, K, T, ST] + ) = instance[V, K, T, Function[FKeyspace, FName, FParams] :: ST, headType.Out :: tailType.Out] + + implicit def implicitNotFoundMacro[V, K, T, C, CT]: Aux[V, K, T, C, CT] = macro implicitNotFoundMacroImpl[V, K, T, C] + + def implicitNotFoundMacroImpl[V, K, T, C](c: Context) = c.abort(c.enclosingPosition, "a7a") +} diff --git a/troy-driver/src/main/scala/troy/driver/query/select/Where.scala b/troy-driver/src/main/scala/troy/driver/query/select/Where.scala new file mode 100644 index 0000000..ca8c2e7 --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/query/select/Where.scala @@ -0,0 +1,37 @@ +package troy.driver.query.select + +import shapeless._ +import troy.driver.schema.FunctionType +import troy.driver.schema.column.ColumnType + +import scala.reflect.macros.blackbox.Context + +trait Where[Version, Keyspace, Table, Relations] { + type BindMarkerTypes <: HList +} + +object Where { + import Select._ + + type Aux[V, K, T, Rs, BMs] = Where[V, K, T, Rs] { type BindMarkerTypes = BMs } + + def apply[V, K, T, Rs](implicit w: Where[V, K, T, Rs]): Aux[V, K, T, Rs, w.BindMarkerTypes] = w + + def instance[V, K, T, Rs, BMs <: HList]: Aux[V, K, T, Rs, BMs] = new Where[V, K, T, Rs] { type BindMarkerTypes = BMs } + + implicit def hNilInstance[V, K, T]: Aux[V, K, T, HNil, HNil] = instance[V, K, T, HNil, HNil] + + /** + * For equality operators like ==, <, >, etc .. + * the type of the term on the right, is the same as the left + */ + implicit def simpleEqualityInstance[V, K, T, C, RT <: HList]( + implicit + headType: ColumnType[V, K, T, C], + tailType: Where[V, K, T, RT] + ) = instance[V, K, T, Relation[Column[C] :: HNil, Equality, AnonymousBindMarker :: HNil] :: RT, headType.Out :: tailType.BindMarkerTypes] + + // implicit def implicitNotFoundMacro[V, K, T, C, CT]: Aux[V, K, T, C, CT] = macro implicitNotFoundMacroImpl[V, K, T, C] + // + // def implicitNotFoundMacroImpl[V, K, T, Rs](c: Context) = c.abort(c.enclosingPosition, s"a7a000hh") +} diff --git a/troy-driver/src/main/scala/troy/driver/query/select/strategies.scala b/troy-driver/src/main/scala/troy/driver/query/select/strategies.scala new file mode 100644 index 0000000..29080d2 --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/query/select/strategies.scala @@ -0,0 +1,56 @@ +package troy.driver.query.select + +import scala.concurrent.Future +import troy.driver.JavaConverters.RichListenableFuture +import com.datastax.driver.core.{ + PreparedStatement => DSPreparedStatement, + Session => DSSession, + ResultSet => DSResultSet +} + +trait PreparationStrategy[Statement] { + def prepare(raw: String): Statement +} +object PreparationStrategy { + def instance[S](p: String => S) = new PreparationStrategy[S] { + override def prepare(raw: String) = p(raw) + } + + // val noPreparationStrategy = + // instance[com.datastax.driver.core.Statement](s => new DSSimpleStatement(s)) + + def eagerSyncPreparationStrategy(implicit session: DSSession) = + instance[DSPreparedStatement](session.prepare) + + // def eagerAsyncPreparationStrategy(implicit session: DSSession) = + // instance[Future[DSPreparedStatement]](session.prepareAsync(_).asScala) +} + +trait ExecutionStrategy[ExecutionMonad[_]] { + def execute(statement: DSPreparedStatement, binder: StatementBinder[_, _]): ExecutionMonad[DSResultSet] +} +object ExecutionStrategy { + def instance[ExecutionMonad[_]](e: (DSPreparedStatement, StatementBinder[_, _]) => ExecutionMonad[DSResultSet]) = + new ExecutionStrategy[ExecutionMonad] { + def execute(statement: DSPreparedStatement, binder: StatementBinder[_, _]): ExecutionMonad[DSResultSet] = + e(statement, binder) + } + + // def asyncStatementExecutionStrategy[PInfo, Params](implicit session: DSSession) = + // instance[Future] { (s, binder) => + // session.executeAsync(binder.bind(s.bind)).asScala + // } + + // def syncStatementExecutionStrategy(implicit session: Session) = + // instance[Statement, ResultSet](session.execute) + // + // def asyncStatementExecutionStrategy(implicit session: Session) = + // instance[Statement, Future[ResultSet]](session.executeAsync(_).asScala) + // + // def asyncAsyncStatementExecutionStrategy(implicit session: Session) = + // instance[Future[Statement], ExecutionContext => Future[ResultSet]](f => implicit ec => f.map(session.executeAsync).flatMap(_.asScala)) +} + +trait ParsingStrategy[ExecutionMonad[_], OutputMonad[_], Output] { + def parse(resultSet: ExecutionMonad[DSResultSet]): OutputMonad[Output] +} diff --git a/troy-driver/src/main/scala/troy/driver/schema/FunctionType.scala b/troy-driver/src/main/scala/troy/driver/schema/FunctionType.scala new file mode 100644 index 0000000..95126f5 --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/schema/FunctionType.scala @@ -0,0 +1,30 @@ +package troy +package driver.schema + +import troy.driver.{ CassandraDataType => CDT } +import shapeless._ +import troy.driver.query.select.Select.NoKeyspace // FIXME: We should not refer to query types here + +/* + * Represents Cql function type + * This type-class is meant to be instantiated at the call site (might be auto-generated by a macro/plugin) + * to give the compiler a hint about the schema + */ +trait FunctionType[Version, Keyspace, Name, Params <: HList] { + type Out <: CDT +} + +object FunctionType { + type Aux[Version, Keyspace, Name, Ps <: HList, O] = FunctionType[Version, Keyspace, Name, Ps] { type Out = O } + + def instance[Version, Keyspace, Name, Ps <: HList, O <: CDT]: Aux[Version, Keyspace, Name, Ps, O] = + new FunctionType[Version, Keyspace, Name, Ps] { type Out = O } + + def builtin0[V, N, O <: CDT] = instance[V, NoKeyspace, N, HNil, O] + def builtin1[V, N, P1 <: CDT, O <: CDT] = instance[V, NoKeyspace, N, P1 :: HNil, O] + + implicit def nowFunctionType[V] = builtin0[V, "now", CDT.TimeUuid] + implicit def writetimeFunctionType[V, P1 <: CDT] = builtin1[V, "writetime", P1, CDT.BigInt] + implicit def dateOfFunctionType[V] = builtin1[V, "dateof", CDT.TimeUuid, CDT.Timestamp] + // TODO +} diff --git a/troy-driver/src/main/scala/troy/driver/schema/TypeBinding.scala b/troy-driver/src/main/scala/troy/driver/schema/TypeBinding.scala new file mode 100644 index 0000000..861bc05 --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/schema/TypeBinding.scala @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Tamer AbdulRadi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package troy +package driver.schema + +import troy.driver.{ CassandraDataType => CDT } + +/* + * Maps a Cassandra type to equivalent Scala type + * This type-class is meant to be instantiated by Troy, yet still can be overridden in Call site + */ +trait TypeBinding[CassandraType <: CDT] { + type ScalaType +} + +object TypeBinding { + type Aux[CT <: CDT, ST] = TypeBinding[CT] { type ScalaType = ST } + + def instance[CT <: CDT, ST]: Aux[CT, ST] = + new TypeBinding[CT] { type ScalaType = ST } + + implicit val defaultIntMapping = instance[CDT.Int, Int] + implicit val defaultAsciiMapping = instance[CDT.Ascii, String] + implicit val defaultTextMapping = instance[CDT.Text, String] + // TODO + implicit def defaultListMapping[T <: CDT.Native](implicit tb: TypeBinding[T]) = instance[CDT.List[T], Seq[tb.ScalaType]] + implicit def defaultSetMapping[T <: CDT.Native](implicit tb: TypeBinding[T]) = instance[CDT.Set[T], Set[tb.ScalaType]] + implicit def defaultMapMapping[K <: CDT.Native, V <: CDT.Native](implicit kTb: TypeBinding[K], vTb: TypeBinding[V]) = + instance[CDT.Map[K, V], Map[kTb.ScalaType, vTb.ScalaType]] + // TODO +} + diff --git a/troy-driver/src/main/scala/troy/driver/schema/column/CodecForColumn.scala b/troy-driver/src/main/scala/troy/driver/schema/column/CodecForColumn.scala new file mode 100644 index 0000000..65f3138 --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/schema/column/CodecForColumn.scala @@ -0,0 +1,49 @@ +/* + * Copyright 2016 Tamer AbdulRadi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package troy +package driver.schema.column + +import troy.driver.codecs.TroyCodec +import troy.driver.schema.TypeBinding +import troy.driver.{ CassandraDataType => CDT } + +sealed trait CodecForColumn[Version, Keyspace, Table, Column] { + type CassandraType <: CDT + type ScalaType + + val codec: TroyCodec[CassandraType, ScalaType] +} + +object CodecForColumn { + type Aux[V, K, T, C, CT, ST] = CodecForColumn[V, K, T, C] { + type CassandraType = CT + type ScalaType = ST + } + + def apply[V, K, T, C](implicit cf: CodecForColumn[V, K, T, C]): Aux[V, K, T, C, cf.CassandraType, cf.ScalaType] = cf + + implicit def instance[V, K, T, C, CT <: CDT, ST]( + implicit + columnType: ColumnType.Aux[V, K, T, C, CT], + typeBinding: TypeBinding.Aux[CT, ST], + codec: TroyCodec[CT, ST] + ): Aux[V, K, T, C, CT, ST] = new CodecForColumn[V, K, T, C] { + type CassandraType = CT + type ScalaType = ST + val codec: TroyCodec[CassandraType, ScalaType] = codec + } +} diff --git a/troy-driver/src/main/scala/troy/driver/schema/column/ColumnExists.scala b/troy-driver/src/main/scala/troy/driver/schema/column/ColumnExists.scala new file mode 100644 index 0000000..09db690 --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/schema/column/ColumnExists.scala @@ -0,0 +1,29 @@ +/* + * Copyright 2016 Tamer AbdulRadi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package troy +package driver.schema.column + +/* + * Name is expected to be a textual literal type + * This type-class is meant to be instantiated at the call site (might be auto-generated by a macro/plugin) + * to give the compiler a hint about the schema + */ +trait ColumnExists[Version, Keyspace, Table, Name] + +object ColumnExists { + implicit def instance[V, K, T, Name](implicit ct: ColumnType[V, K, T, Name]) = new ColumnExists[V, K, T, Name] {} +} diff --git a/troy-driver/src/main/scala/troy/driver/schema/column/ColumnType.scala b/troy-driver/src/main/scala/troy/driver/schema/column/ColumnType.scala new file mode 100644 index 0000000..136c0d4 --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/schema/column/ColumnType.scala @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Tamer AbdulRadi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package troy +package driver.schema.column + +import troy.driver.CassandraDataType +import troy.driver.schema.TableExists + +import scala.reflect.macros.blackbox.Context + +/* + * Represents the column's Cassandra data type + * This type-class is meant to be instantiated at the call site (might be auto-generated by a macro/plugin) + * to give the compiler a hint about the schema + */ +trait ColumnType[Version, Keyspace, Table, Column] { + type Out <: CassandraDataType +} + +object ColumnType { + type Aux[V, K, T, C, CT] = ColumnType[V, K, T, C] { type Out = CT } + + def instance[V, K, T, C, CT <: CassandraDataType](implicit tableExists: TableExists[V, K, T]): Aux[V, K, T, C, CT] = + new ColumnType[V, K, T, C] { type Out = CT } + + implicit def implicitNotFoundMacro[V, K, T, C, CT]: Aux[V, K, T, C, CT] = macro implicitNotFoundMacroImpl[V, K, T, C] + + def implicitNotFoundMacroImpl[V, K, T, C](c: Context) = c.abort(c.enclosingPosition, "Column Not Found") +} diff --git a/troy-driver/src/main/scala/troy/driver/schema/keyspace.scala b/troy-driver/src/main/scala/troy/driver/schema/keyspace.scala new file mode 100644 index 0000000..112d802 --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/schema/keyspace.scala @@ -0,0 +1,29 @@ +/* + * Copyright 2016 Tamer AbdulRadi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package troy +package driver.schema + +/* + * Name is expected to be a textual literal type + * This type-class is meant to be instantiated at the call site (might be auto-generated by a macro/plugin) + * to give the compiler a hint about the schema + */ +trait KeyspaceExists[Version, Name] + +object KeyspaceExists { + def instance[Version: VersionExists, Name] = new KeyspaceExists[Version, Name] {} +} \ No newline at end of file diff --git a/troy-driver/src/main/scala/troy/driver/schema/table.scala b/troy-driver/src/main/scala/troy/driver/schema/table.scala new file mode 100644 index 0000000..66b609f --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/schema/table.scala @@ -0,0 +1,32 @@ +/* + * Copyright 2016 Tamer AbdulRadi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package troy +package driver.schema + +import scala.annotation.implicitNotFound + +/* + * Name is expected to be a textual literal type + * This type-class is meant to be instantiated at the call site (might be auto-generated by a macro/plugin) + * to give the compiler a hint about the schema + */ +@implicitNotFound("Table ${Name} doesn't exists in keyspace ${Keyspace} (version ${Version})") +trait TableExists[Version, Keyspace, Name] + +object TableExists { + def instance[V, K, Name](implicit keyspaceExists: KeyspaceExists[V, K]) = new TableExists[V, K, Name] {} +} \ No newline at end of file diff --git a/troy-driver/src/main/scala/troy/driver/schema/version.scala b/troy-driver/src/main/scala/troy/driver/schema/version.scala new file mode 100644 index 0000000..c62fc0d --- /dev/null +++ b/troy-driver/src/main/scala/troy/driver/schema/version.scala @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Tamer AbdulRadi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package troy +package driver.schema + +/** + * V is expected to be a numric literal type + * This type-class is meant to be instantiated at the call site (might be auto-generated by a macro/plugin) + * to give the compiler a hint about the schema + * + * @author Tamer AbdulRadi + */ +trait VersionExists[V] + +object VersionExists { + def instance[V] = new VersionExists[V] {} +} + +/** + * V is expected to be a numric literal type + * This type-class is meant to be instantiated at the call site (might be auto-generated by a macro/plugin) + * to give the compiler a hint about the schema + * + * @author Tamer AbdulRadi + */ +trait LatestVersion[V] + +object LatestVersion { + def instance[V] = new LatestVersion[V] {} +} diff --git a/troy-driver/src/test/scala/troy/driver/CodecsSpec.scala b/troy-driver/src/test/scala/troy/driver/CodecsSpec.scala index f7bb31e..b0286de 100644 --- a/troy-driver/src/test/scala/troy/driver/CodecsSpec.scala +++ b/troy-driver/src/test/scala/troy/driver/CodecsSpec.scala @@ -1,4 +1,5 @@ -package troy.driver +package troy +package driver import java.math.{ BigInteger, BigDecimal } import java.net.InetAddress diff --git a/troy-driver/src/test/scala/troy/driver/Usage.scala b/troy-driver/src/test/scala/troy/driver/Usage.scala index c99b2ec..86e421f 100644 --- a/troy-driver/src/test/scala/troy/driver/Usage.scala +++ b/troy-driver/src/test/scala/troy/driver/Usage.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.driver +package troy +package driver import java.util.UUID diff --git a/troy-driver/src/test/scala/troy/driver/driver/SelectSpec.scala b/troy-driver/src/test/scala/troy/driver/driver/SelectSpec.scala new file mode 100644 index 0000000..ce177db --- /dev/null +++ b/troy-driver/src/test/scala/troy/driver/driver/SelectSpec.scala @@ -0,0 +1,54 @@ +package troy +package driver.driver + +import shapeless._ +import troy.driver.query.select.Selection +import troy.driver.{CassandraDataType => CDT} +import troy.driver.query.select._ + +object SelectionSpec { + import Select._ + import TestSchema._ + + def assertSelectionOut[V, K, T, S, O](implicit s: Selection.Aux[V, K, T, S, O]) = () + + assertSelectionOut[1, "test", "posts", + HNil, + HNil + ] + + // Columns + assertSelectionOut[1, "test", "posts", + Column["author_id"] :: HNil, + CDT.Uuid :: HNil + ] + + assertSelectionOut[1, "test", "posts", + Column["author_id"] :: Column["post_id"] :: HNil, + CDT.Uuid :: CDT.TimeUuid :: HNil + ] + + // Functions only + assertSelectionOut[1, "test", "posts", + Function[NoKeyspace, "now", HNil] :: HNil, + CDT.TimeUuid :: HNil + ] + + // Functions on columns + assertSelectionOut[1, "test", "posts", + Function[NoKeyspace, "writetime", Column["author_id"] :: HNil] :: HNil, + CDT.BigInt :: HNil + ] + + // Functions AND columns + assertSelectionOut[1, "test", "posts", + Column["author_id"] :: Function[NoKeyspace, "writetime", Column["author_id"] :: HNil] :: HNil, + CDT.Uuid :: CDT.BigInt :: HNil + ] + + // Functions on Functions + assertSelectionOut[1, "test", "posts", + Function[NoKeyspace, "dateof", Function[NoKeyspace, "now", HNil] :: HNil] :: HNil, + CDT.Timestamp :: HNil + ] +} diff --git a/troy-driver/src/test/scala/troy/driver/driver/TestSchema.scala b/troy-driver/src/test/scala/troy/driver/driver/TestSchema.scala new file mode 100644 index 0000000..1b11352 --- /dev/null +++ b/troy-driver/src/test/scala/troy/driver/driver/TestSchema.scala @@ -0,0 +1,45 @@ +package troy +package driver.driver + +import troy.driver.{CassandraDataType => CDT} +import troy.driver.schema.column.ColumnType +import troy.driver.schema.KeyspaceExists +import troy.driver.schema.TableExists +import troy.driver.schema.VersionExists + +object TestSchema { + implicit val v1Exists = VersionExists.instance[1] + implicit val v2Exists = VersionExists.instance[2] + implicit val keyspacetestExistsInV1 = KeyspaceExists.instance[1, "test"] + implicit val keyspacetestExistsInV2 = KeyspaceExists.instance[2, "test"] + implicit val tablepostsExistsInKeyspacetestInV1 = TableExists.instance[1, "test", "posts"] + implicit val tablepost_detailsExistsInKeyspacetestInV1 = TableExists.instance[1, "test", "post_details"] + implicit val tablepostsExistsInKeyspacetestInV2 = TableExists.instance[2, "test", "posts"] + implicit val tablepost_detailsExistsInKeyspacetestInV2 = TableExists.instance[2, "test", "post_details"] + implicit val columnauthor_idExistsInTablepostsInKeyspacetestInV1 = ColumnType.instance[1, "test", "posts", "author_id", CDT.Uuid] + implicit val columnpost_idExistsInTablepostsInKeyspacetestInV1 = ColumnType.instance[1, "test", "posts", "post_id", CDT.TimeUuid] + implicit val columnauthor_nameExistsInTablepostsInKeyspacetestInV1 = ColumnType.instance[1, "test", "posts", "author_name", CDT.Text] + implicit val columnpost_titleExistsInTablepostsInKeyspacetestInV1 = ColumnType.instance[1, "test", "posts", "post_title", CDT.Text] + implicit val columntagsExistsInTablepost_detailsInKeyspacetestInV1 = ColumnType.instance[1, "test", "post_details", "tags", CDT.Set[CDT.Text]] + implicit val columnsomeFieldExistsInTablepost_detailsInKeyspacetestInV1 = ColumnType.instance[1, "test", "post_details", "someField", CDT.Text] + implicit val columncomment_idsExistsInTablepost_detailsInKeyspacetestInV1 = ColumnType.instance[1, "test", "post_details", "comment_ids", CDT.Set[CDT.Int]] + implicit val columnidExistsInTablepost_detailsInKeyspacetestInV1 = ColumnType.instance[1, "test", "post_details", "id", CDT.Uuid] + implicit val columnratingExistsInTablepost_detailsInKeyspacetestInV1 = ColumnType.instance[1, "test", "post_details", "rating", CDT.Int] + implicit val columnauthor_idExistsInTablepost_detailsInKeyspacetestInV1 = ColumnType.instance[1, "test", "post_details", "author_id", CDT.Uuid] + implicit val columntitleExistsInTablepost_detailsInKeyspacetestInV1 = ColumnType.instance[1, "test", "post_details", "title", CDT.Text] + implicit val columncommentsExistsInTablepost_detailsInKeyspacetestInV1 = ColumnType.instance[1, "test", "post_details", "comments", CDT.Map[CDT.Int, CDT.Text]] + implicit val columnpost_titleExistsInTablepostsInKeyspacetestInV2 = ColumnType.instance[2, "test", "posts", "post_title", CDT.Text] + implicit val columnpost_idExistsInTablepostsInKeyspacetestInV2 = ColumnType.instance[2, "test", "posts", "post_id", CDT.Uuid] + implicit val columnauthor_nameExistsInTablepostsInKeyspacetestInV2 = ColumnType.instance[2, "test", "posts", "author_name", CDT.Text] + implicit val columnauthor_idExistsInTablepostsInKeyspacetestInV2 = ColumnType.instance[2, "test", "posts", "author_id", CDT.Uuid] + implicit val columnpost_ratingExistsInTablepostsInKeyspacetestInV2 = ColumnType.instance[2, "test", "posts", "post_rating", CDT.Int] + implicit val columntagsExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "tags", CDT.Set[CDT.Text]] + implicit val columncomment_bodiesExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "comment_bodies", CDT.List[CDT.Text]] + implicit val columncomment_idsExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "comment_ids", CDT.Set[CDT.Int]] + implicit val columnidExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "id", CDT.Uuid] + implicit val columnratingExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "rating", CDT.Int] + implicit val columnauthor_idExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "author_id", CDT.Uuid] + implicit val columntitleExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "title", CDT.Text] + implicit val columncomment_userIdsExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "comment_userIds", CDT.List[CDT.Int]] + implicit val columncommentsExistsInTablepost_detailsInKeyspacetestInV2 = ColumnType.instance[2, "test", "post_details", "comments", CDT.Map[CDT.Int, CDT.Text]] +} diff --git a/troy-driver/src/test/scala/troy/driver/schema/CodecForColumnSpec.scala b/troy-driver/src/test/scala/troy/driver/schema/CodecForColumnSpec.scala new file mode 100644 index 0000000..ad2c3b0 --- /dev/null +++ b/troy-driver/src/test/scala/troy/driver/schema/CodecForColumnSpec.scala @@ -0,0 +1,35 @@ +package troy +package driver.schema + +import org.scalatest.{FlatSpec, Matchers} +import shapeless.test.illTyped +import troy.driver.{CassandraDataType => CDT} +import troy.driver.codecs.TroyCodec +import troy.driver.codecs.PrimitivesCodecs.intAsInt +import troy.driver.schema.column.{CodecForColumn, ColumnType} +import troy.driver.schema.KeyspaceExists +import troy.driver.schema.TableExists +import troy.driver.schema.VersionExists + +class CodecForColumnSpec extends FlatSpec with Matchers { + implicit val v1Exists = VersionExists.instance[1] + implicit val keyspaceTestExists = KeyspaceExists.instance[1, "my_keyspace"] + implicit val tablePostsInKeyspaceTestExists = TableExists.instance[1, "my_keyspace", "my_table"] + + implicit val column1 = ColumnType.instance[1, "my_keyspace", "my_table", "my_column_1", CDT.Ascii] + implicit val column2 = ColumnType.instance[1, "my_keyspace", "my_table", "my_column_2", CDT.Int] + implicit val column3 = ColumnType.instance[1, "my_keyspace", "my_table", "my_column_3", CDT.List[CDT.Int]] + implicit val column4 = ColumnType.instance[1, "my_keyspace", "my_table", "my_column_4", CDT.Map[CDT.Int, CDT.Ascii]] + + CodecForColumn[1, "my_keyspace", "my_table", "my_column_1"].codec: TroyCodec[CDT.Ascii, String] + CodecForColumn[1, "my_keyspace", "my_table", "my_column_2"].codec: TroyCodec[CDT.Int, Int] + CodecForColumn[1, "my_keyspace", "my_table", "my_column_3"].codec: TroyCodec[CDT.List[CDT.Int], Seq[Int]] + CodecForColumn[1, "my_keyspace", "my_table", "my_column_4"].codec: TroyCodec[CDT.Map[CDT.Int, CDT.Ascii], Map[Int, String]] + + "Fetching codec for non existing column" should "fail" in { + illTyped( + """ + CodecForColumn[1, "my_keyspace", "my_table", "my_column_99"].codec + """) + } +} diff --git a/troy-macro/src/main/scala/troy/dsl/MacroDSL.scala b/troy-macro/src/main/scala/troy/dsl/MacroDSL.scala index a5ec4fb..80b4f36 100644 --- a/troy-macro/src/main/scala/troy/dsl/MacroDSL.scala +++ b/troy-macro/src/main/scala/troy/dsl/MacroDSL.scala @@ -1,4 +1,5 @@ -package troy.dsl +package troy +package dsl import com.datastax.driver.core._ diff --git a/troy-macro/src/main/scala/troy/dsl/ParsingOps.scala b/troy-macro/src/main/scala/troy/dsl/ParsingOps.scala index aeccaf4..34c7e9b 100644 --- a/troy-macro/src/main/scala/troy/dsl/ParsingOps.scala +++ b/troy-macro/src/main/scala/troy/dsl/ParsingOps.scala @@ -1,4 +1,5 @@ -package troy.dsl +package troy +package dsl import scala.annotation.compileTimeOnly diff --git a/troy-macro/src/main/scala/troy/macros/CqlOps.scala b/troy-macro/src/main/scala/troy/macros/CqlOps.scala index 047102e..7ab7784 100644 --- a/troy-macro/src/main/scala/troy/macros/CqlOps.scala +++ b/troy-macro/src/main/scala/troy/macros/CqlOps.scala @@ -1,4 +1,5 @@ -package troy.macros +package troy +package macros import troy.cql.ast.CqlParser import troy.schema._ diff --git a/troy-macro/src/main/scala/troy/macros/package.scala b/troy-macro/src/main/scala/troy/macros/package.scala index aec81e0..a02895e 100644 --- a/troy-macro/src/main/scala/troy/macros/package.scala +++ b/troy-macro/src/main/scala/troy/macros/package.scala @@ -17,8 +17,8 @@ package troy import scala.reflect.macros.blackbox.Context -import troy.cql.ast.DataType -import troy.schema.{SchemaEngine, V, Result, ResourceFileOrFolderSchemaLoader} +import troy.cql.ast.{DataType, KeyspaceName, TableName} +import troy.schema._ package object macros { import CqlOps._ @@ -39,6 +39,35 @@ package object macros { val (qParts, qParams) = findCqlQuery(c)(expr) val rawQuery = qParts.map{case q"${p: String}" => p}.mkString("?") val schema = getOrAbort(loadedSchema) +// +// val versionsSchemaImplicits = for { +// (v, schema: SchemaEngineImpl) <- schema.asInstanceOf[VersionedSchemaEngineImpl].schemas +// identifier = TermName(s"v${v}Exists") +// } yield q"implicit val $identifier = VersionExists.instance[$v]" +// +// val keyspacesSchemaImplicits = for { +// (v, schema: SchemaEngineImpl) <- schema.asInstanceOf[VersionedSchemaEngineImpl].schemas +// (KeyspaceName(k), keyspace) <- schema.schema.keyspaces +// identifier = TermName(s"keyspace${k}ExistsInV$v") +// } yield q"implicit val $identifier = KeyspaceExists.instance[$v, $k]" +// +// val tablesSchemaImplicits = for { +// (v, schema: SchemaEngineImpl) <- schema.asInstanceOf[VersionedSchemaEngineImpl].schemas +// (KeyspaceName(k), keyspace) <- schema.schema.keyspaces +// (TableName(_, t), table) <- keyspace.tables +// identifier = TermName(s"table${t}ExistsInKeyspace${k}InV$v") +// } yield q"implicit val $identifier = TableExists.instance[$v, $k, $t]" +// +// val columnsSchemaImplicits = for { +// (v, schema: SchemaEngineImpl) <- schema.asInstanceOf[VersionedSchemaEngineImpl].schemas +// (KeyspaceName(k), keyspace) <- schema.schema.keyspaces +// (TableName(_, t), table) <- keyspace.tables +// (cName, column) <- table.columns +// ct = translateColumnType(c)(column.dataType) +// identifier = TermName(s"column${cName}ExistsInTable${t}InKeyspace${k}InV$v") +// } yield q"implicit val $identifier = ColumnType.instance[$v, $k, $t, $cName, $ct]" +// + val query = getOrAbort(parseQuery(rawQuery)) val (rowType, variableDataTypes) = getOrAbort { queryConfig match { @@ -50,8 +79,12 @@ package object macros { val imports = Seq( q"import _root_.troy.driver.InternalDsl._", - q"import _root_.troy.driver.codecs.PrimitivesCodecs._" - ) + q"import _root_.troy.driver.codecs.PrimitivesCodecs._" //, +// q"import troy.driver.schema.version.VersionExists", +// q"import troy.driver.schema.keyspace.KeyspaceExists", +// q"import troy.driver.schema.table.TableExists", +// q"import troy.driver.schema.column.ColumnType" + ) //++ versionsSchemaImplicits ++ keyspacesSchemaImplicits ++ tablesSchemaImplicits ++ columnsSchemaImplicits val session = q"implicitly[com.datastax.driver.core.Session]" @@ -189,7 +222,11 @@ package object macros { } private def translateColumnTypes(c: Context)(types: Seq[DataType]) = { - types map { + types.map(t => translateColumnType(c)(t)) + } + + private def translateColumnType(c: Context)(t: DataType) = { + t match { case t: DataType.Native => translateNativeColumnType(c)(t) case t: DataType.Collection => translateCollectionColumnType(c)(t) } diff --git a/troy-macro/src/test/resources/schema/01.cql b/troy-macro/src/test/resources/schema/01.cql index d19fcca..dca9682 100644 --- a/troy-macro/src/test/resources/schema/01.cql +++ b/troy-macro/src/test/resources/schema/01.cql @@ -2,7 +2,7 @@ CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy' , 'replicatio CREATE TABLE test.posts ( author_id uuid, - post_id uuid, + post_id timeuuid, author_name text static, post_title text, PRIMARY KEY ((author_id), post_id) diff --git a/troy-macro/src/test/scala/troy/macros/CassandraSpec.scala b/troy-macro/src/test/scala/troy/macros/CassandraSpec.scala index dd9bf6c..e7af4c2 100644 --- a/troy-macro/src/test/scala/troy/macros/CassandraSpec.scala +++ b/troy-macro/src/test/scala/troy/macros/CassandraSpec.scala @@ -1,4 +1,5 @@ -package troy.macros +package troy +package macros import java.util diff --git a/troy-macro/src/test/scala/troy/macros/CqlOpsSpec.scala b/troy-macro/src/test/scala/troy/macros/CqlOpsSpec.scala index c32d7d7..6d3afeb 100644 --- a/troy-macro/src/test/scala/troy/macros/CqlOpsSpec.scala +++ b/troy-macro/src/test/scala/troy/macros/CqlOpsSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.macros +package troy +package macros import troy.schema.Messages.{ SchemaNotFound, QueryParseFailure } import troy.schema.VTestUtils._ diff --git a/troy-macro/src/test/scala/troy/macros/DslSpec.scala b/troy-macro/src/test/scala/troy/macros/DslSpec.scala index e7f4dde..63cdf0a 100644 --- a/troy-macro/src/test/scala/troy/macros/DslSpec.scala +++ b/troy-macro/src/test/scala/troy/macros/DslSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.macros +package troy +package macros import java.util.UUID @@ -31,10 +32,10 @@ class DslSpec extends CassandraSpec { override val testDataFixtures = """ INSERT INTO test.posts (author_id, post_id , author_name , post_rating, post_title) - VALUES ( uuid(), uuid(), 'test author', 5, 'test post') ; + VALUES ( uuid(), now(), 'test author', 5, 'test post') ; INSERT INTO test.post_details (author_id, id , tags , comment_ids, comment_userIds, comment_bodies , comments) - VALUES ( uuid(), uuid(), {'test1', 'test2'}, {1, 2}, [1, 2], ['test1', 'test2'], {1: 'test1', 2 : 'test2'}) ; + VALUES ( uuid(), now(), {'test1', 'test2'}, {1, 2}, [1, 2], ['test1', 'test2'], {1: 'test1', 2 : 'test2'}) ; """ case class Post(id: UUID, author_name: String, title: String) diff --git a/troy-macro/src/test/scala/troy/macros/SelectValidationSpec.scala b/troy-macro/src/test/scala/troy/macros/SelectValidationSpec.scala index ae66a54..35a560b 100644 --- a/troy-macro/src/test/scala/troy/macros/SelectValidationSpec.scala +++ b/troy-macro/src/test/scala/troy/macros/SelectValidationSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.macros +package troy +package macros import java.util.UUID import com.datastax.driver.core.Session diff --git a/troy-macro/src/test/scala/troy/macros/TypesSpec.scala b/troy-macro/src/test/scala/troy/macros/TypesSpec.scala index 428d0a5..516dee7 100644 --- a/troy-macro/src/test/scala/troy/macros/TypesSpec.scala +++ b/troy-macro/src/test/scala/troy/macros/TypesSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.macros +package troy +package macros import java.util.UUID diff --git a/troy-macro/src/test/scala/troy/macros/Usage.scala b/troy-macro/src/test/scala/troy/macros/Usage.scala index a61fb6d..b725a26 100644 --- a/troy-macro/src/test/scala/troy/macros/Usage.scala +++ b/troy-macro/src/test/scala/troy/macros/Usage.scala @@ -14,11 +14,13 @@ * limitations under the License. */ -package troy.macros +package troy +package macros import java.util.UUID -import com.datastax.driver.core.{ Statement, Row, ResultSet, BoundStatement } +import com.datastax.driver.core.utils.UUIDs +import com.datastax.driver.core.{ BoundStatement, ResultSet, Row, Statement } import scala.concurrent.Future @@ -35,7 +37,7 @@ class Usage extends CassandraSpec { override val testDataFixtures = """ INSERT INTO test.posts (author_id, post_id , author_name , post_rating, post_title) - VALUES ( uuid(), uuid(), 'test author', 5, 'Title') ; + VALUES ( uuid(), now(), 'test author', 5, 'Title') ; """ case class Post(id: UUID, authorName: String, title: String) @@ -87,7 +89,7 @@ class Usage extends CassandraSpec { VALUES ( ${p.authorId}, ${p.postId}, ${p.authorName}, ${p.postRating}, ${p.postTitle}) ; """.prepared.executeAsync } - createPost(AuthorAndPost(UUID.randomUUID(), UUID.randomUUID(), "Author", Some(5), Some("Title"))).futureValue + createPost(AuthorAndPost(UUID.randomUUID(), UUIDs.timeBased(), "Author", Some(5), Some("Title"))).futureValue } it should "support INSERT with if not exists flag" in { @@ -98,7 +100,7 @@ class Usage extends CassandraSpec { IF NOT EXISTS; """.prepared.execute.oneOption.as(identity[Boolean] _) } - createPost(AuthorAndPost(UUID.randomUUID(), UUID.randomUUID(), "Author", Some(5), Some("Title"))).get shouldBe true + createPost(AuthorAndPost(UUID.randomUUID(), UUIDs.timeBased(), "Author", Some(5), Some("Title"))).get shouldBe true } // TODO: https://github.com/tabdulradi/troy/issues/34 @@ -110,7 +112,7 @@ class Usage extends CassandraSpec { WHERE author_id=$authId and post_id=$postId; """.prepared.executeAsync } - setTitle(UUID.randomUUID(), UUID.randomUUID(), "not test anymore") + setTitle(UUID.randomUUID(), UUIDs.timeBased(), "not test anymore") val setRating = withSchema { (authId: UUID, id: UUID, title: String, rating: Int) => cql""" @@ -119,7 +121,7 @@ class Usage extends CassandraSpec { WHERE author_id=$authId and id=$id IF title=$title; """.prepared.executeAsync } - setRating(UUID.randomUUID(), UUID.randomUUID(), "test", 5) + setRating(UUID.randomUUID(), UUIDs.timeBased(), "test", 5) val addTag = withSchema { (authId: UUID, id: UUID, newTag: Set[String]) => cql""" @@ -128,7 +130,7 @@ class Usage extends CassandraSpec { WHERE author_id=$authId and id=$id; """.prepared.executeAsync } - addTag(UUID.randomUUID(), UUID.randomUUID(), Set("test")) + addTag(UUID.randomUUID(), UUIDs.timeBased(), Set("test")) } // TODO: https://github.com/tabdulradi/troy/issues/33 diff --git a/troy-meta/src/main/scala/troy/meta/Debug.scala b/troy-meta/src/main/scala/troy/meta/Debug.scala new file mode 100644 index 0000000..36d20a5 --- /dev/null +++ b/troy-meta/src/main/scala/troy/meta/Debug.scala @@ -0,0 +1,12 @@ +package troy +package meta + +import scala.meta._ + +class Debug extends scala.annotation.StaticAnnotation { + + inline def apply(defn: Defn): Any = meta { + println(defn) + defn + } +} \ No newline at end of file diff --git a/troy-meta/src/main/scala/troy/meta/MacroDSL.scala b/troy-meta/src/main/scala/troy/meta/MacroDSL.scala index 804dff0..786fc2c 100644 --- a/troy-meta/src/main/scala/troy/meta/MacroDSL.scala +++ b/troy-meta/src/main/scala/troy/meta/MacroDSL.scala @@ -1,4 +1,5 @@ -package troy.meta +package troy +package meta import com.datastax.driver.core._ diff --git a/troy-meta/src/main/scala/troy/meta/ParsingOps.scala b/troy-meta/src/main/scala/troy/meta/ParsingOps.scala index 1d2f6d8..c302dcf 100644 --- a/troy-meta/src/main/scala/troy/meta/ParsingOps.scala +++ b/troy-meta/src/main/scala/troy/meta/ParsingOps.scala @@ -1,4 +1,5 @@ -package troy.meta +package troy +package meta import scala.annotation.compileTimeOnly diff --git a/troy-meta/src/main/scala/troy/meta/Utils.scala b/troy-meta/src/main/scala/troy/meta/Utils.scala new file mode 100644 index 0000000..b2ba831 --- /dev/null +++ b/troy-meta/src/main/scala/troy/meta/Utils.scala @@ -0,0 +1,41 @@ +package troy +package meta + +import troy.cql.ast.CqlParser +import troy.schema.{ Message, Result, V } + +import scala.meta.Type + +trait Utils { + def abort(str: String): Nothing = { + println(str) + scala.meta.abort(str) + } + + def notImplemented(msg: String) = { + abort("Not implemented " + msg) + } + + def getOrAbort[T](result: CqlParser.ParseResult[T]): T = + result.getOrElse(abort("Parse error")) + + def warn(msg: String): Unit = println(msg) + + def warn(msg: Message): Unit = warn(msg.message) + + def getOrAbort[T](result: Result[T]): T = + result match { + case V.Success(value, ws) => + ws.foreach(warn) + value + case V.Error(es, ws) => + ws.foreach(warn) + abort(es.head.message) + } + + def quoted(str: String): String = s"""\"$str\"""" + + def literal(str: String): Type.Name = Type.Name(quoted(str)) + + def literal(num: Int): Type.Name = Type.Name(num.toString) +} diff --git a/troy-meta/src/main/scala/troy/meta/query.scala b/troy-meta/src/main/scala/troy/meta/query.scala new file mode 100644 index 0000000..c5cbec9 --- /dev/null +++ b/troy-meta/src/main/scala/troy/meta/query.scala @@ -0,0 +1,124 @@ +package troy +package meta + +import troy.cql.ast._ +import troy.cql.ast.dml.Operator +import troy.cql.ast.dml.Select._ + +import scala.collection.immutable.{Seq => ImmutableSeq} +import troy.cql.ast.dml.WhereClause.Relation + +import scala.meta._ + +object QueryUtils extends Utils { + + val imports = ImmutableSeq( + q"import troy.driver.query.select._", + q"import Select._", + q"import shapeless._" + ) + def constructQuery(inputType: Type, outputType: Type, rawQuery: String): Tree = { + val xs = imports ++ translateQuery(inputType, outputType, getOrAbort(CqlParser.parseDML(rawQuery)), rawQuery) + q"{..$xs}" + } + + def translateQuery(inputType: Type, outputType: Type, query: DataManipulation, raw: String): Seq[Stat] = query match { + case SelectStatement(mod, selection, from, where, orderBy, perPartitionLimit, limit, allowFiltering) => + ImmutableSeq( + q"type Version = ${Type.Name("1")}", + q"type Keyspace = ${translateKeyspace(from.keyspace)}", + q"type Table = ${literal(from.table)}", + q"type SelectonClause = ${translateSelection(selection)}", + q"type Relations = ${translateRelations(where.map(_.relations).getOrElse(Seq.empty))}", + q"type QueryInputScalaType = ${inputType}", + q"type QueryOutputRowScalaType = ${outputType}", + q"Query[Version, Keyspace, Table, SelectonClause, Relations, QueryInputScalaType, QueryOutputRowScalaType]($raw)" + ) + } + + def translateKeyspace(keyspace: Option[KeyspaceName]): Type = + keyspace.map(k => t"${literal(k.name)}").getOrElse(t"NoKeyspace") + + def translateSelection(selection: Selection): Type = selection match { + case Asterisk => + notImplemented("translateSelection Asterisk") + case SelectClause(items) => + foldAsHList(items.map { + case SelectionClauseItem(selector, None) => + translateSelector(selector) + case SelectionClauseItem(selector, Some(alias)) => + notImplemented("translateSelection SelectClause SelectionClauseItem") + }) + } + + def translateSelectors(selectors: Seq[Selector]): Type = + foldAsHList(selectors.map(translateSelector)) + + + def foldAsHList(ts: Seq[Type]): Type = ts.reverse.foldLeft[Type](t"HNil") { + case (acc: Type, s: Type) => + t"$s :: $acc" + } + + def translateSelector(selector: Selector): Type = selector match { + case ColumnName(name) => t"Column[${literal(name)}]" + case SelectTerm(term) => notImplemented("translateSelector SelectTerm") + case Cast(selector, typ) => notImplemented("translateSelector Cast") + case troy.cql.ast.dml.Select.Function(FunctionName(keyspace, name), params) => + t"Function[${translateKeyspace(keyspace)}, ${literal(name)}, ${translateSelectors(params)}]" + case Count => notImplemented("translateSelector Count") + } + + def translateRelations(relations: Seq[Relation]): Type = + foldAsHList(relations.map(translateRelation)) + + def translateRelation(relation: Relation): Type = relation match { + case Relation.Simple(columnName, operator, term) => + t"Relation[Column[${literal(columnName)}] :: HNil, ${translateOperator(operator)}, ${translateTerm(term)} :: HNil]" + case Relation.Tupled(columnNames, operator, terms) => notImplemented("Relation.Tupled") + case Relation.Token(columnNames, operator, term) => notImplemented("Relation.Token") + } + + def translateOperator(op: Operator): Type = + op match { + case Operator.Equals => t"Equality" + case Operator.LessThan => t"Equality" + case Operator.GreaterThan => t"Equality" + case Operator.LessThanOrEqual => t"Equality" + case Operator.GreaterThanOrEqual => t"Equality" + case Operator.NotEquals => t"Equality" + case Operator.In => t"In" + case Operator.Contains => t"Contains" + case Operator.ContainsKey => t"ContainsKey" + case Operator.Like => t"Like" + } + + def translateTerm(t: troy.cql.ast.Term): Type = t match { + case BindMarker.Anonymous => t"AnonymousBindMarker" + case BindMarker.Named(name) => t"NamedBindMarker[${literal(name)}]" + case _ => notImplemented("translateTerm not BindMarker") + } +} + +class schemasafe extends scala.annotation.StaticAnnotation { + inline def apply(defn: Any): Any = meta { + def transform(t: Tree) = + defn.transform { + case q"query[$t1, $t2](${p: Lit})" => + QueryUtils.constructQuery(t1, t2, p.value.toString) + } + + defn match { + case tree: Tree => + println(">>>>>>>>>>>>>>>>>>") + println(tree) + val q = transform(tree) + println(">>>>>>>>>>>>>>>>>>") + println(q) + println(">>>>>>>>>>>>>>>>>>") + q + case _ => + abort(???, "") + } + } +} \ No newline at end of file diff --git a/troy-meta/src/main/scala/troy/meta/schema.scala b/troy-meta/src/main/scala/troy/meta/schema.scala new file mode 100644 index 0000000..c7070c4 --- /dev/null +++ b/troy-meta/src/main/scala/troy/meta/schema.scala @@ -0,0 +1,147 @@ +package troy +package meta + +import java.io.InputStream + +import troy.cql.ast.{CqlParser, DataType, KeyspaceName, TableName} +import troy.schema.{SchemaEngineImpl, VersionedSchemaEngine, VersionedSchemaEngineImpl} + +import scala.io.Source +import scala.meta._ + + + +object SchemaUtils extends Utils { + + val imports = Seq( + q"import troy.driver.schema._", + q"import troy.driver.schema.column._", + q"import troy.driver.InternalDsl.CDT" + ) + + def writeSchema: Seq[Stat] = { + val schemas = loadSchemaFromFileName("/schema.cql").asInstanceOf[VersionedSchemaEngineImpl].schemas + + val globalSchemaImplicits = Seq( + // q"implicit val minVersion = VersionExists.instance[${schema.schemas.keys.min}]", + q"implicit val maxVersion = LatestVersion.instance[${literal(schemas.keys.max)}]" + ) + + def constructImplicitVal(identifier: String, value: Term) = + q"implicit val ${Pat.Var.Term(Term.Name(identifier))} = $value" + + val versionsSchemaImplicits = for { + (v, schema: SchemaEngineImpl) <- schemas + vLit = literal(v) + } yield constructImplicitVal(s"v${v}Exists", q"VersionExists.instance[$vLit]") + + val keyspacesSchemaImplicits = for { + (v, schema: SchemaEngineImpl) <- schemas + vLit = literal(v) + (KeyspaceName(k), _) <- schema.schema.keyspaces + kLit = literal(k) + } yield constructImplicitVal(s"keyspace${k}ExistsInV$v", q"KeyspaceExists.instance[$vLit, $kLit]") + + val tablesSchemaImplicits = for { + (v, schema: SchemaEngineImpl) <- schemas + vLit = literal(v) + (KeyspaceName(k), keyspace) <- schema.schema.keyspaces + kLit = literal(k) + (TableName(_, t), table) <- keyspace.tables + tLit = literal(t) + } yield constructImplicitVal(s"table${t}ExistsInKeyspace${k}InV$v", q"TableExists.instance[$vLit, $kLit, $tLit]") + + val columnsSchemaImplicits = for { + (v, schema: SchemaEngineImpl) <- schemas + vLit = literal(v) + (KeyspaceName(k), keyspace) <- schema.schema.keyspaces + kLit = literal(k) + (TableName(_, t), table) <- keyspace.tables + tLit = literal(t) + (cName, column) <- table.columns + cLit = literal(cName) + ct = translateColumnType(column.dataType) + } yield constructImplicitVal(s"column${cName}ExistsInTable${t}InKeyspace${k}InV$v", q"ColumnType.instance[$vLit, $kLit, $tLit, $cLit, $ct]") + + imports ++ globalSchemaImplicits ++ versionsSchemaImplicits ++ keyspacesSchemaImplicits ++ tablesSchemaImplicits ++ columnsSchemaImplicits + } + + def loadSchemaFromFileName(path: String) = + loadSchemaFromInputStream( + Option(this.getClass.getResourceAsStream(path)) + .getOrElse(abort(s"Can't find schema file $path")) + ) + + def loadSchemaFromInputStream(schemaFile: InputStream) = + loadSchemaFromSource(scala.io.Source.fromInputStream(schemaFile)) + + def loadSchemaFromSource(schema: Source) = { + val lines = schema.getLines() + val str = lines.mkString("\n") + loadSchemaFromString(str) + } + + def loadSchemaFromString(schema: String) = + CqlParser.parseSchema(schema) match { + case CqlParser.Success(result, _) => + getOrAbort(VersionedSchemaEngine(Seq(result))) + case CqlParser.Failure(msg, next) => + abort(s"Failure during parsing the schema. Error ($msg) near line ${next.pos.line}, column ${next.pos.column}") + } + + private def translateColumnTypes(types: Seq[DataType]) = { + types.map(t => translateColumnType(t)) + } + + private def translateColumnType(t: DataType) = { + t match { + case t: DataType.Native => translateNativeColumnType(t) + case t: DataType.Collection => translateCollectionColumnType(t) + } + } + + private def translateCollectionColumnType(typ: DataType): Type = { + def translate(t: DataType) = translateNativeColumnType(t) + typ match { + case DataType.List(t) => t"CDT.List[${translate(t)}]" + case DataType.Set(t) => t"CDT.Set[${translate(t)}]" + case DataType.Map(k, v) => t"CDT.Map[${translate(k)}, ${translate(v)}]" + // case DataType.Tuple(ts: Seq[DataType]) => t"CDT." + // case DataType.Custom(javaClass: String) => t"CDT." + } + } + + private def translateNativeColumnType(typ: DataType): Type = + typ match { + case DataType.Ascii => t"CDT.Ascii" + case DataType.BigInt => t"CDT.BigInt" + case DataType.Blob => t"CDT.Blob" + case DataType.Boolean => t"CDT.Boolean" + case DataType.Counter => t"CDT.Counter" + case DataType.Date => t"CDT.Date" + case DataType.Decimal => t"CDT.Decimal" + case DataType.Double => t"CDT.Double" + case DataType.Float => t"CDT.Float" + case DataType.Inet => t"CDT.Inet" + case DataType.Int => t"CDT.Int" + case DataType.Smallint => t"CDT.SmallInt" + case DataType.Text => t"CDT.Text" + case DataType.Time => t"CDT.Time" + case DataType.Timestamp => t"CDT.Timestamp" + case DataType.Timeuuid => t"CDT.TimeUuid" + case DataType.Tinyint => t"CDT.TinyInt" + case DataType.Uuid => t"CDT.Uuid" + case DataType.Varchar => t"CDT.VarChar" + case DataType.Varint => t"CDT.VarInt" + } + +} + +class schema extends scala.annotation.StaticAnnotation { + inline def apply(defn: Any): Any = meta { + val q"object $name { ..$stats }" = defn + val newStats = stats ++ SchemaUtils.writeSchema + newStats.foreach(println) + q"object $name { ..$newStats }" + } +} \ No newline at end of file diff --git a/troy-meta/src/main/scala/troy/meta/withSchema.scala b/troy-meta/src/main/scala/troy/meta/withSchema.scala index bcdce64..78cf8a7 100644 --- a/troy-meta/src/main/scala/troy/meta/withSchema.scala +++ b/troy-meta/src/main/scala/troy/meta/withSchema.scala @@ -1,4 +1,5 @@ -package troy.meta +package troy +package meta import java.io.InputStream diff --git a/troy-meta/src/test/scala/troy/meta/BaseSpec.scala b/troy-meta/src/test/scala/troy/meta/BaseSpec.scala index 96b78ef..465ff9e 100644 --- a/troy-meta/src/test/scala/troy/meta/BaseSpec.scala +++ b/troy-meta/src/test/scala/troy/meta/BaseSpec.scala @@ -1,4 +1,5 @@ -package troy.meta +package troy +package meta import java.util diff --git a/troy-meta/src/test/scala/troy/meta/Playground.scala b/troy-meta/src/test/scala/troy/meta/Playground.scala new file mode 100644 index 0000000..4bf6499 --- /dev/null +++ b/troy-meta/src/test/scala/troy/meta/Playground.scala @@ -0,0 +1,38 @@ +package troy +package meta + +import java.util.UUID + +import com.datastax.driver.core.{ Cluster, Session } +import com.datastax.driver.core.utils.UUIDs + +import scala.concurrent.duration.Duration +import scala.concurrent.{ Await, Future } +import scala.util.Try + +@schema object Schema + +object Playground extends App { + def query[I, O](x: String): I => Future[Seq[O]] = ??? + case class Post(id: UUID, title: String) + + import Schema._ + + withSession { implicit session => + import scala.concurrent.ExecutionContext.Implicits.global + + @schemasafe val getAuthorPosts = + query[(UUID, Int), Post]("select post_id, post_title from test.posts where author_id = ? AND post_rating >= ? ALLOW FILTERING;") + + val authorId = UUID.fromString("6287c470-e298-11e6-9b3d-ffeaf4ddcb54") + println(Await.result(getAuthorPosts((authorId, 4)): Future[Iterable[Post]], Duration.Inf)) + } + + def withSession[T](f: Session => T) = { + val cluster = new Cluster.Builder().addContactPoints("127.0.0.1").withPort(9042).build() + val session: Session = cluster.connect() + Try(f(session)) + session.close() + cluster.close() + } +} diff --git a/troy-meta/src/test/scala/troy/meta/Usage.scala b/troy-meta/src/test/scala/troy/meta/Usage.scala index bb36959..db4bf7e 100644 --- a/troy-meta/src/test/scala/troy/meta/Usage.scala +++ b/troy-meta/src/test/scala/troy/meta/Usage.scala @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package troy.meta +package troy +package meta import java.util.UUID import scala.concurrent.Future diff --git a/troy-schema/src/main/scala/troy/schema/ColumnOps.scala b/troy-schema/src/main/scala/troy/schema/ColumnOps.scala index 9be3e35..2eb9afb 100644 --- a/troy-schema/src/main/scala/troy/schema/ColumnOps.scala +++ b/troy-schema/src/main/scala/troy/schema/ColumnOps.scala @@ -1,4 +1,5 @@ -package troy.schema +package troy +package schema import troy.cql.ast.DataType import troy.cql.ast.dml.Operator diff --git a/troy-schema/src/main/scala/troy/schema/Message.scala b/troy-schema/src/main/scala/troy/schema/Message.scala index 499aa8a..e4cb853 100644 --- a/troy-schema/src/main/scala/troy/schema/Message.scala +++ b/troy-schema/src/main/scala/troy/schema/Message.scala @@ -1,4 +1,5 @@ -package troy.schema +package troy +package schema import troy.cql.ast._ import troy.cql.ast.dml.Operator diff --git a/troy-schema/src/main/scala/troy/schema/Schema.scala b/troy-schema/src/main/scala/troy/schema/Schema.scala index 03954a0..3207ca1 100644 --- a/troy-schema/src/main/scala/troy/schema/Schema.scala +++ b/troy-schema/src/main/scala/troy/schema/Schema.scala @@ -1,4 +1,5 @@ -package troy.schema +package troy +package schema import troy.cql.ast._ import V.Implicits._ diff --git a/troy-schema/src/main/scala/troy/schema/SchemaEngine.scala b/troy-schema/src/main/scala/troy/schema/SchemaEngine.scala index 5b94c3c..a6a827d 100644 --- a/troy-schema/src/main/scala/troy/schema/SchemaEngine.scala +++ b/troy-schema/src/main/scala/troy/schema/SchemaEngine.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import troy.cql.ast._ import troy.cql.ast.dml.Update.UpdateOperator diff --git a/troy-schema/src/main/scala/troy/schema/SchemaLoader.scala b/troy-schema/src/main/scala/troy/schema/SchemaLoader.scala index cb0b5a0..5df61f1 100644 --- a/troy-schema/src/main/scala/troy/schema/SchemaLoader.scala +++ b/troy-schema/src/main/scala/troy/schema/SchemaLoader.scala @@ -1,4 +1,5 @@ -package troy.schema +package troy +package schema import java.io.InputStream import troy.cql.ast.CqlParser diff --git a/troy-schema/src/main/scala/troy/schema/V.scala b/troy-schema/src/main/scala/troy/schema/V.scala index a96443b..bb93673 100644 --- a/troy-schema/src/main/scala/troy/schema/V.scala +++ b/troy-schema/src/main/scala/troy/schema/V.scala @@ -1,4 +1,5 @@ -package troy.schema +package troy +package schema /** * V for ValidationŲŒ similar to Either but also includes Warnings diff --git a/troy-schema/src/main/scala/troy/schema/VersionedSchemaEngine.scala b/troy-schema/src/main/scala/troy/schema/VersionedSchemaEngine.scala index b927599..c5df277 100644 --- a/troy-schema/src/main/scala/troy/schema/VersionedSchemaEngine.scala +++ b/troy-schema/src/main/scala/troy/schema/VersionedSchemaEngine.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import troy.cql.ast._ import troy.schema.Messages.QueryNotCrossCompatible diff --git a/troy-schema/src/main/scala/troy/schema/validation/AlterColumnIncompatibleTypes.scala b/troy-schema/src/main/scala/troy/schema/validation/AlterColumnIncompatibleTypes.scala index 45d4cd6..03f519c 100644 --- a/troy-schema/src/main/scala/troy/schema/validation/AlterColumnIncompatibleTypes.scala +++ b/troy-schema/src/main/scala/troy/schema/validation/AlterColumnIncompatibleTypes.scala @@ -1,4 +1,5 @@ -package troy.schema.validation +package troy +package schema.validation import troy.cql.ast._ import troy.cql.ast.ddl.Alter.AlterType diff --git a/troy-schema/src/main/scala/troy/schema/validation/SelectDistinctNonStaticColumns.scala b/troy-schema/src/main/scala/troy/schema/validation/SelectDistinctNonStaticColumns.scala index e1535f5..b816649 100644 --- a/troy-schema/src/main/scala/troy/schema/validation/SelectDistinctNonStaticColumns.scala +++ b/troy-schema/src/main/scala/troy/schema/validation/SelectDistinctNonStaticColumns.scala @@ -1,4 +1,5 @@ -package troy.schema.validation +package troy +package schema.validation import troy.schema.Messages.SelectedDistinctNonStaticColumn import troy.schema.Schema diff --git a/troy-schema/src/main/scala/troy/schema/validation/Validation.scala b/troy-schema/src/main/scala/troy/schema/validation/Validation.scala index 821b265..cf00607 100644 --- a/troy-schema/src/main/scala/troy/schema/validation/Validation.scala +++ b/troy-schema/src/main/scala/troy/schema/validation/Validation.scala @@ -1,4 +1,5 @@ -package troy.schema.validation +package troy +package schema.validation import troy.cql.ast.{ DataDefinition, DataManipulation } import troy.schema._ diff --git a/troy-schema/src/main/scala/troy/schema/validation/WhereNonPrimaryNoIndex.scala b/troy-schema/src/main/scala/troy/schema/validation/WhereNonPrimaryNoIndex.scala index 9556dc7..cfafac3 100644 --- a/troy-schema/src/main/scala/troy/schema/validation/WhereNonPrimaryNoIndex.scala +++ b/troy-schema/src/main/scala/troy/schema/validation/WhereNonPrimaryNoIndex.scala @@ -1,4 +1,5 @@ -package troy.schema.validation +package troy +package schema.validation import troy.cql.ast._ import troy.cql.ast.dml.WhereClause diff --git a/troy-schema/src/test/scala/troy/cql3_3/lexical/CaseSensitivitySchemaSpec.scala b/troy-schema/src/test/scala/troy/cql3_3/lexical/CaseSensitivitySchemaSpec.scala index dc0b9ec..f9ccbae 100644 --- a/troy-schema/src/test/scala/troy/cql3_3/lexical/CaseSensitivitySchemaSpec.scala +++ b/troy-schema/src/test/scala/troy/cql3_3/lexical/CaseSensitivitySchemaSpec.scala @@ -1,4 +1,5 @@ -package troy.cql3_3.lexical +package troy +package cql3_3.lexical import org.scalatest._ import troy.cql.ast.DataType diff --git a/troy-schema/src/test/scala/troy/schema/AlterTableSpec.scala b/troy-schema/src/test/scala/troy/schema/AlterTableSpec.scala index e5cda50..1576351 100644 --- a/troy-schema/src/test/scala/troy/schema/AlterTableSpec.scala +++ b/troy-schema/src/test/scala/troy/schema/AlterTableSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import org.scalatest._ import troy.cql.ast._ diff --git a/troy-schema/src/test/scala/troy/schema/DeleteSpec.scala b/troy-schema/src/test/scala/troy/schema/DeleteSpec.scala index 23eafb2..058331c 100644 --- a/troy-schema/src/test/scala/troy/schema/DeleteSpec.scala +++ b/troy-schema/src/test/scala/troy/schema/DeleteSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import org.scalatest._ import troy.cql.ast.{ SelectStatement, _ } diff --git a/troy-schema/src/test/scala/troy/schema/SchemaEngineValidationTest.scala b/troy-schema/src/test/scala/troy/schema/SchemaEngineValidationTest.scala index 7ac96ba..1ee85cb 100644 --- a/troy-schema/src/test/scala/troy/schema/SchemaEngineValidationTest.scala +++ b/troy-schema/src/test/scala/troy/schema/SchemaEngineValidationTest.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import troy.cql.ast._ import org.scalatest._ diff --git a/troy-schema/src/test/scala/troy/schema/SchemaLoaderSpec.scala b/troy-schema/src/test/scala/troy/schema/SchemaLoaderSpec.scala index 12f44af..f0c14bb 100644 --- a/troy-schema/src/test/scala/troy/schema/SchemaLoaderSpec.scala +++ b/troy-schema/src/test/scala/troy/schema/SchemaLoaderSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import org.scalatest.FreeSpec import troy.schema.Messages._ diff --git a/troy-schema/src/test/scala/troy/schema/SchemaTestUtils.scala b/troy-schema/src/test/scala/troy/schema/SchemaTestUtils.scala index a4f5c08..cbbee80 100644 --- a/troy-schema/src/test/scala/troy/schema/SchemaTestUtils.scala +++ b/troy-schema/src/test/scala/troy/schema/SchemaTestUtils.scala @@ -1,4 +1,5 @@ -package troy.schema +package troy +package schema import org.scalatest.{ FlatSpec, Matchers } import troy.cql.parser.ParserTestUtils.{ parseSchema, parseQuery } diff --git a/troy-schema/src/test/scala/troy/schema/UpdateSpec.scala b/troy-schema/src/test/scala/troy/schema/UpdateSpec.scala index 6af301a..1c1a86b 100644 --- a/troy-schema/src/test/scala/troy/schema/UpdateSpec.scala +++ b/troy-schema/src/test/scala/troy/schema/UpdateSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import org.scalatest._ import troy.cql.ast._ diff --git a/troy-schema/src/test/scala/troy/schema/VMatchers.scala b/troy-schema/src/test/scala/troy/schema/VMatchers.scala index b08f498..8ca8048 100644 --- a/troy-schema/src/test/scala/troy/schema/VMatchers.scala +++ b/troy-schema/src/test/scala/troy/schema/VMatchers.scala @@ -1,4 +1,5 @@ -package troy.schema +package troy +package schema import org.scalatest._ import matchers._ diff --git a/troy-schema/src/test/scala/troy/schema/VSpec.scala b/troy-schema/src/test/scala/troy/schema/VSpec.scala index 6c98785..cf853a9 100644 --- a/troy-schema/src/test/scala/troy/schema/VSpec.scala +++ b/troy-schema/src/test/scala/troy/schema/VSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import org.scalatest._ diff --git a/troy-schema/src/test/scala/troy/schema/VTestUtils.scala b/troy-schema/src/test/scala/troy/schema/VTestUtils.scala index d31f90c..30d2ed6 100644 --- a/troy-schema/src/test/scala/troy/schema/VTestUtils.scala +++ b/troy-schema/src/test/scala/troy/schema/VTestUtils.scala @@ -1,4 +1,5 @@ -package troy.schema +package troy +package schema import org.scalatest.{ FlatSpec, Matchers } diff --git a/troy-schema/src/test/scala/troy/schema/VersionedSchemaEngineSpec.scala b/troy-schema/src/test/scala/troy/schema/VersionedSchemaEngineSpec.scala index 9ba00be..65ee8e3 100644 --- a/troy-schema/src/test/scala/troy/schema/VersionedSchemaEngineSpec.scala +++ b/troy-schema/src/test/scala/troy/schema/VersionedSchemaEngineSpec.scala @@ -14,7 +14,8 @@ * limitations under the License. */ -package troy.schema +package troy +package schema import org.scalatest._ import troy.cql.ast.{ DataType, CqlParser }