All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.websudos.phantom.builder.query.prepared.PreparedBuilder.scala Maven / Gradle / Ivy

There is a newer version: 1.29.6
Show newest version
/*
 * Copyright 2013-2015 Websudos, Limited.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Explicit consent must be obtained from the copyright owner, Outworkers Limited before any redistribution is made.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.websudos.phantom.builder.query.prepared

import java.util.concurrent.Executor

import com.datastax.driver.core.{QueryOptions => _, _}
import com.websudos.phantom.CassandraTable
import com.websudos.phantom.builder.query._
import com.websudos.phantom.builder.{LimitBound, Unlimited}
import com.websudos.phantom.connectors.KeySpace
import org.joda.time.DateTime
import shapeless.HList
import shapeless.ops.hlist.Tupler

import scala.collection.JavaConverters._
import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, blocking, Future => ScalaFuture}

private[phantom] trait PrepareMark {

  def symbol: String = "?"

  def qb: CQLQuery = CQLQuery("?")
}

object ? extends PrepareMark

class ExecutablePreparedQuery(val statement: Statement, val options: QueryOptions) extends ExecutableStatement with Batchable {
  override val qb = CQLQuery.empty

  override def statement()(implicit session: Session): Statement = {
    statement
      .setConsistencyLevel(options.consistencyLevel.orNull)
  }
}

class ExecutablePreparedSelectQuery[
  Table <: CassandraTable[Table, _],
  R,
  Limit <: LimitBound
](val st: Statement, fn: Row => R, val options: QueryOptions) extends ExecutableQuery[Table, R, Limit] {

  override def fromRow(r: Row): R = fn(r)

  override def future()(
    implicit session: Session,
    keySpace: KeySpace,
    ec: ExecutionContextExecutor
  ): ScalaFuture[ResultSet] = {
    scalaQueryStringExecuteToFuture(st)
  }


  /**
   * Returns the first row from the select ignoring everything else
   * @param session The implicit session provided by a [[com.websudos.phantom.connectors.Connector]].
   * @param keySpace The implicit keySpace definition provided by a [[com.websudos.phantom.connectors.Connector]].
   * @param ev The implicit limit for the query.
   * @param ec The implicit Scala execution context.
   * @return A Scala future guaranteed to contain a single result wrapped as an Option.
   */
  override def one()(
    implicit session: Session,
    keySpace: KeySpace,
    ev: =:=[Limit, Unlimited],
    ec: ExecutionContextExecutor
  ): ScalaFuture[Option[R]] = {
    singleFetch()
  }

  override def qb: CQLQuery = CQLQuery.empty
}

abstract class PreparedFlattener(qb: CQLQuery)(implicit session: Session, keySpace: KeySpace) {

  protected[this] val query: PreparedStatement = {
    blocking(session.prepare(qb.queryString))
  }

  def flattenOpt(param: Any): Any = {
    //noinspection ComparingUnrelatedTypes
    param match {
      case x if x.isInstanceOf[Some[_]] => flattenOpt(x.asInstanceOf[Some[_]].get)
      case x if x.isInstanceOf[None.type] => None.orNull.asInstanceOf[Any]
      case x if x.isInstanceOf[List[_]] => x.asInstanceOf[List[Any]].asJava
      case x if x.isInstanceOf[Set[_]] => x.asInstanceOf[Set[Any]].asJava
      case x if x.isInstanceOf[Map[_, _]] => x.asInstanceOf[Map[Any, Any]].asJava
      case x if x.isInstanceOf[DateTime] => x.asInstanceOf[DateTime].toDate
      case x if x.isInstanceOf[Enumeration#Value] => x.asInstanceOf[Enumeration#Value].toString
      case x if x.isInstanceOf[BigDecimal] => x.asInstanceOf[BigDecimal].bigDecimal
      case x if x.isInstanceOf[BigInt] => x.asInstanceOf[BigInt].bigInteger
      case x => x
    }
  }

  /**
    * Cleans up the series of parameters passed to the bind query to match
    * the codec registry collection that the Java Driver has internally.
    *
    * If the type of the object passed through to the driver doesn't match a known type for the specific Cassandra column
    * type, then the driver will crash with an error.
    *
    * There are known associations of (Cassandra Column Type -> Java Type) that we need to provide for binding to work.
    *
    * @param parameters The sequence of parameters to bind.
    * @return A clansed set of parameters.
    */
  protected[this] def flattenOpt(parameters: Seq[Any]): Seq[AnyRef] = {
    parameters map flattenOpt map (_.asInstanceOf[AnyRef])
  }
}

class PreparedBlock[PS <: HList](val qb: CQLQuery, val options: QueryOptions)
  (implicit session: Session, keySpace: KeySpace) extends PreparedFlattener(qb) {

  /**
    * Method used to bind a set of arguments to a prepared query in a typesafe manner.
    *
    * @param v1 The argument used for the assertion, inferred as a tuple by the compiler.
    * @param tp The Shapeless Tupler implicit builder to cast HList to a Tuple.
    * @param ev The equality parameter to check that the types provided in the tuple match the prepared query.
    * @tparam V1 The argument tuple type, auto-tupled by the compiler from varargs.
    * @tparam Out The type argument used to cast the HList to a Tuple.
    * @return An final form prepared select query that can be asynchronously executed.
    */
  def bind[V1 <: Product, Out <: Product](v1: V1)(
    implicit tp: Tupler.Aux[PS, Out],
    ev: V1 =:= Out
  ): ExecutablePreparedQuery = {
    val params = flattenOpt(v1.productIterator.toSeq)
    new ExecutablePreparedQuery(query.bind(params: _*), options)
  }

  /**
    * Method used to bind a single argument to a prepared statement.
    *
    * @param v A single argument that will be interpreted as a sequence of 1 for binding.
    * @tparam V The type of the argument.
    * @return An final form prepared select query that can be asynchronously executed.
    */
  def bind[V](v: V): ExecutablePreparedQuery = {
    val params = flattenOpt(Seq(v))
    new ExecutablePreparedQuery(query.bind(params: _*), options)
  }
}

class PreparedSelectBlock[
  T <: CassandraTable[T, _],
  R,
  Limit <: LimitBound,
  PS <: HList
](qb: CQLQuery, fn: Row => R, options: QueryOptions)
  (implicit session: Session, keySpace: KeySpace) extends PreparedFlattener(qb) {

  /**
    * Method used to bind a set of arguments to a prepared query in a typesafe manner.
    *
    * @param v1 The argument used for the assertion, inferred as a tuple by the compiler.
    * @param tp The Shapeless Tupler implicit builder to cast HList to a Tuple.
    * @param ev The equality parameter to check that the types provided in the tuple match the prepared query.
    * @tparam V1 The argument tuple type, auto-tupled by the compiler from varargs.
    * @tparam Out The type argument used to cast the HList to a Tuple.
    * @return An final form prepared select query that can be asynchronously executed.
    */
  def bind[V1 <: Product, Out <: Product](v1: V1)(
    implicit tp: Tupler.Aux[PS, Out],
    ev: V1 =:= Out
  ): ExecutablePreparedSelectQuery[T, R, Limit] = {
    val params = flattenOpt(v1.productIterator.toSeq)
    new ExecutablePreparedSelectQuery(query.bind(params: _*), fn, options)
  }

  /**
    * Method used to bind a single argument to a prepared statement.
    *
    * @param v A single argument that will be interpreted as a sequence of 1 for binding.
    * @tparam V The type of the argument.
    * @return An final form prepared select query that can be asynchronously executed.
    */
  def bind[V](v: V): ExecutablePreparedSelectQuery[T, R, Limit] = {
    val params = flattenOpt(Seq(v))
    new ExecutablePreparedSelectQuery(query.bind(params: _*), fn, options)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy