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

com.emarsys.rdb.connector.bigquery.BigQueryRawSelect.scala Maven / Gradle / Ivy

The newest version!
package com.emarsys.rdb.connector.bigquery

import akka.NotUsed
import akka.stream.scaladsl.{Sink, Source}
import com.emarsys.rdb.connector.common.ConnectorResponse

import scala.annotation.tailrec
import scala.concurrent.Future
import scala.concurrent.duration.FiniteDuration

trait BigQueryRawSelect {
  self: BigQueryConnector =>

  override def rawSelect(
      rawSql: String,
      limit: Option[Int],
      timeout: FiniteDuration
  ): ConnectorResponse[Source[Seq[String], NotUsed]] = {
    val query = removeEndingSemicolons(rawSql)
    val limitedQuery = limit.fold(query) { l =>
      wrapInLimit(query, l)
    }
    Future.successful(
      Right(
        bigQueryClient.streamingQuery(limitedQuery).completionTimeout(timeout).recoverWithRetries(1, streamErrorHandler)
      )
    )
  }

  override def projectedRawSelect(
      rawSql: String,
      fields: Seq[String],
      limit: Option[Int],
      timeout: FiniteDuration,
      allowNullFieldValue: Boolean
  ): ConnectorResponse[Source[Seq[String], NotUsed]] =
    Future.successful(
      Right(
        runProjectedSelectWith(
          rawSql,
          fields,
          limit,
          allowNullFieldValue,
          query =>
            bigQueryClient.streamingQuery(query).completionTimeout(timeout).recoverWithRetries(1, streamErrorHandler)
        )
      )
    )

  override def validateProjectedRawSelect(rawSql: String, fields: Seq[String]): ConnectorResponse[Unit] = {
    runProjectedSelectWith(
      rawSql,
      fields,
      None,
      allowNullFieldValue = true,
      query => bigQueryClient.streamingQuery(query, dryRun = true)
    ).runWith(Sink.seq)
      .map(_ => Right({}))
      .recover(eitherErrorHandler)
  }

  override def validateRawSelect(rawSql: String): ConnectorResponse[Unit] = {
    val modifiedSql = removeEndingSemicolons(rawSql)
    bigQueryClient
      .streamingQuery(modifiedSql, dryRun = true)
      .runWith(Sink.seq)
      .map(_ => Right({}))
      .recover(eitherErrorHandler)
  }

  override def analyzeRawSelect(rawSql: String): ConnectorResponse[Source[Seq[String], NotUsed]] = {
    val modifiedSql = removeEndingSemicolons(rawSql)
    Future(Right(bigQueryClient.streamingQuery(modifiedSql, dryRun = true)))
  }

  private def runProjectedSelectWith[R](
      rawSql: String,
      fields: Seq[String],
      limit: Option[Int],
      allowNullFieldValue: Boolean,
      queryRunner: String => R
  ) = {
    val fieldList    = concatenateProjection(fields)
    val projectedSql = wrapInProjection(rawSql, fieldList)
    val query =
      if (!allowNullFieldValue) wrapInCondition(projectedSql, fields)
      else projectedSql
    val limitedQuery = limit.fold(query)(l => s"$query LIMIT $l")

    queryRunner(limitedQuery)
  }

  private def concatenateProjection(fields: Seq[String]) =
    fields.map("t." + _).mkString(", ")

  private def wrapInLimit(query: String, l: Int) =
    s"SELECT * FROM ( $query ) AS query LIMIT $l"

  private def wrapInCondition(rawSql: String, fields: Seq[String]): String =
    removeEndingSemicolons(rawSql) + concatenateCondition(fields)

  private def concatenateCondition(fields: Seq[String]) =
    " WHERE " + fields.map("t." + _ + " IS NOT NULL ").mkString("AND ")

  private def wrapInProjection(rawSql: String, projection: String) =
    s"SELECT $projection FROM ( ${removeEndingSemicolons(rawSql)} ) t"

  @tailrec
  private def removeEndingSemicolons(query: String): String = {
    val qTrimmed = query.trim
    if (qTrimmed.last == ';') {
      removeEndingSemicolons(qTrimmed.dropRight(1))
    } else {
      qTrimmed
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy