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

net.fwbrasil.activate.storage.relational.idiom.SqlIdiom.scala Maven / Gradle / Ivy

package net.fwbrasil.activate.storage.relational.idiom

import net.fwbrasil.activate.OptimisticOfflineLocking.versionVarName
import net.fwbrasil.activate.util.RichList._
import net.fwbrasil.activate.storage.marshalling.BooleanStorageValue
import net.fwbrasil.activate.statement.StatementValue
import java.sql.ResultSet
import net.fwbrasil.activate.storage.marshalling.DoubleStorageValue
import net.fwbrasil.activate.statement.IsLessThan
import net.fwbrasil.activate.statement.StatementEntityValue
import net.fwbrasil.activate.statement.mass.MassUpdateStatement
import net.fwbrasil.activate.storage.marshalling.IntStorageValue
import net.fwbrasil.activate.storage.relational.ModifyStorageStatement
import net.fwbrasil.activate.statement.IsGreaterOrEqualTo
import net.fwbrasil.activate.storage.marshalling.StorageRenameTable
import net.fwbrasil.activate.storage.relational.NormalQlStatement
import net.fwbrasil.activate.statement.StatementEntitySourceValue
import net.fwbrasil.activate.storage.marshalling.StorageRemoveTable
import net.fwbrasil.activate.storage.marshalling.BigDecimalStorageValue
import net.fwbrasil.activate.storage.marshalling.LongStorageValue
import net.fwbrasil.activate.storage.marshalling.StringStorageValue
import net.fwbrasil.activate.storage.marshalling.StorageColumn
import net.fwbrasil.activate.statement.query.OrderBy
import net.fwbrasil.activate.statement.StatementEntitySourcePropertyValue
import net.fwbrasil.activate.storage.marshalling.StorageValue
import net.fwbrasil.activate.statement.StatementEntityInstanceValue
import net.fwbrasil.activate.storage.relational.QueryStorageStatement
import net.fwbrasil.activate.statement.IsEqualTo
import net.fwbrasil.activate.statement.IsNotEqualTo
import net.fwbrasil.activate.statement.Where
import net.fwbrasil.activate.storage.relational.DdlStorageStatement
import net.fwbrasil.activate.statement.query.OrderByCriteria
import net.fwbrasil.activate.statement.BooleanOperatorCriteria
import net.fwbrasil.activate.storage.marshalling.DateStorageValue
import net.fwbrasil.activate.statement.mass.MassDeleteStatement
import net.fwbrasil.activate.statement.SimpleValue
import net.fwbrasil.activate.statement.CompositeOperatorCriteria
import net.fwbrasil.activate.entity.EntityHelper
import net.fwbrasil.activate.storage.marshalling.StorageAddColumn
import net.fwbrasil.activate.storage.marshalling.ModifyStorageAction
import net.fwbrasil.activate.storage.relational.StorageStatement
import net.fwbrasil.activate.statement.SimpleStatementBooleanValue
import net.fwbrasil.activate.storage.marshalling.FloatStorageValue
import net.fwbrasil.activate.statement.Criteria
import net.fwbrasil.activate.storage.marshalling.StorageAddIndex
import net.fwbrasil.activate.storage.marshalling.StorageAddReference
import net.fwbrasil.activate.storage.marshalling.ReferenceStorageValue
import net.fwbrasil.activate.storage.marshalling.StorageRenameColumn
import net.fwbrasil.activate.storage.marshalling.StorageCreateTable
import net.fwbrasil.activate.statement.StatementSelectValue
import net.fwbrasil.activate.storage.marshalling.StorageRemoveReference
import net.fwbrasil.activate.statement.IsLessOrEqualTo
import net.fwbrasil.activate.statement.IsNotNull
import net.fwbrasil.activate.storage.marshalling.StorageRemoveColumn
import net.fwbrasil.activate.statement.StatementBooleanValue
import net.fwbrasil.activate.statement.IsNull
import net.fwbrasil.activate.statement.IsGreaterThan
import net.fwbrasil.activate.statement.mass.UpdateAssignment
import net.fwbrasil.activate.statement.From
import net.fwbrasil.activate.storage.marshalling.ByteArrayStorageValue
import net.fwbrasil.activate.statement.SimpleOperatorCriteria
import net.fwbrasil.activate.storage.marshalling.StorageRemoveIndex
import java.sql.PreparedStatement
import net.fwbrasil.activate.statement.Operator
import net.fwbrasil.activate.statement.And
import net.fwbrasil.activate.statement.query.Query
import net.fwbrasil.activate.storage.marshalling.Marshaller
import java.sql.Types
import java.util.Date
import net.fwbrasil.activate.statement.query.Select
import net.fwbrasil.activate.statement.Or
import java.sql.Timestamp
import net.fwbrasil.activate.statement.Matcher
import scala.collection.mutable.{ Map => MutableMap }
import net.fwbrasil.activate.statement.query.orderByAscendingDirection
import net.fwbrasil.activate.storage.relational.JdbcRelationalStorage
import net.fwbrasil.activate.storage.marshalling.ListStorageValue
import net.fwbrasil.activate.storage.marshalling.ListStorageValue
import ch.qos.logback.classic.db.names.TableName
import net.fwbrasil.activate.storage.marshalling.ListStorageValue
import net.fwbrasil.activate.storage.marshalling.StorageCreateListTable
import net.fwbrasil.activate.storage.marshalling.ListStorageValue
import net.fwbrasil.activate.storage.relational.DmlStorageStatement
import net.fwbrasil.activate.entity.ListEntityValue
import java.sql.Connection
import scala.collection.mutable.ListBuffer
import net.fwbrasil.activate.storage.marshalling.StorageRemoveTable
import net.fwbrasil.activate.storage.marshalling.StorageRemoveListTable
import net.fwbrasil.activate.util.Reflection
import net.fwbrasil.activate.statement.query.OrderedQuery
import net.fwbrasil.activate.statement.query.LimitedOrderedQuery
import net.fwbrasil.activate.OptimisticOfflineLocking.versionVarName
import net.fwbrasil.activate.entity.BaseEntity
import net.fwbrasil.activate.entity.LazyListEntityValue
import net.fwbrasil.activate.storage.marshalling.StringStorageValue
import net.fwbrasil.activate.statement.FunctionApply
import net.fwbrasil.activate.statement.ToUpperCase
import net.fwbrasil.activate.statement.ToLowerCase
import net.fwbrasil.activate.storage.relational.InsertStorageStatement
import net.fwbrasil.activate.storage.relational.DeleteStorageStatement
import net.fwbrasil.activate.storage.relational.UpdateStorageStatement
import net.fwbrasil.activate.storage.marshalling.StorageModifyColumnType
import net.fwbrasil.activate.storage.marshalling.StorageOptionalValue
import net.fwbrasil.activate.storage.relational.PooledJdbcRelationalStorage
import com.zaxxer.hikari.HikariConfig

object SqlIdiom {
    lazy val dialectsMap = {
        val dialects =
            Reflection
                .getAllImplementorsNames(
                    List(classOf[SqlIdiom]),
                    classOf[SqlIdiom])
                .map(classOf[SqlIdiom].getClassLoader.loadClass)
        dialects
            .map(Reflection.getObjectOption[SqlIdiom]).flatten
            .map(obj => obj.getClass.getSimpleName.split('$').last -> obj)
    }.toMap
    def dialect(name: String) =
        dialectsMap.getOrElse(name, throw new IllegalArgumentException("Invalid dialect " + name))
}

trait ActivateResultSet {

    def getString(i: Int): Option[String]
    def getBytes(i: Int): Option[Array[Byte]]
    def getInt(i: Int): Option[Int]
    def getBoolean(i: Int): Option[Boolean]
    def getFloat(i: Int): Option[Float]
    def getLong(i: Int): Option[Long]
    def getTimestamp(i: Int): Option[Timestamp]
    def getDouble(i: Int): Option[Double]
    def getBigDecimal(i: Int): Option[java.math.BigDecimal]
}

case class JdbcActivateResultSet(rs: ResultSet)
    extends ActivateResultSet {

    private def getValue[T](f: => T) =
        Some(f).filter(_ => !rs.wasNull)

    def getString(i: Int) = getValue(rs.getString(i))
    def getBytes(i: Int) = getValue(rs.getBytes(i))
    def getInt(i: Int) = getValue(rs.getInt(i))
    def getBoolean(i: Int) = getValue(rs.getBoolean(i))
    def getFloat(i: Int) = getValue(rs.getFloat(i))
    def getLong(i: Int) = getValue(rs.getLong(i))
    def getTimestamp(i: Int) = getValue(rs.getTimestamp(i))
    def getDouble(i: Int) = getValue(rs.getDouble(i))
    def getBigDecimal(i: Int) = getValue(rs.getBigDecimal(i))

}

trait HikariWithoutUrl {
    this: SqlIdiom =>
    
    def urlPrefix: String
    
    override def hikariConfigFor(storage: PooledJdbcRelationalStorage, jdbcDataSourceName: String) = {
        import storage._
        val config = new HikariConfig
        val (url, connectionAttributes) =
            storage.url.split(";").toList match {
                case url :: connectionAttributes :: Nil =>
                    (url, connectionAttributes)
                case url :: Nil =>
                    (url, "")
                case other =>
                    throw new IllegalStateException("Invalid derby url")
            }
        config.addDataSourceProperty("databaseName", url.replace(urlPrefix, ""))
        config.addDataSourceProperty("connectionAttributes", connectionAttributes)
        config.setDataSourceClassName(jdbcDataSourceName)
        user map { u => config.addDataSourceProperty("user", u) }
        password map { p => config.addDataSourceProperty("password", p) }
        config.setAutoCommit(true)
        config.setMaximumPoolSize(poolSize)
        config
    }
}

trait SqlIdiom extends QlIdiom {

    def supportsLimitedQueries = true

    def supportsRegex = true

    def prepareDatabase(storage: JdbcRelationalStorage) = {}

    def findTableStatement(tableName: String): String

    def findTableColumnStatement(tableName: String, columnName: String): String

    def findIndexStatement(tableName: String, indexName: String): String

    def findConstraintStatement(tableName: String, constraintName: String): String
    
    def hikariConfigFor(storage: PooledJdbcRelationalStorage, jdbcDataSourceName: String) = {
        import storage._
        val config = new HikariConfig
        config.setDataSourceClassName(jdbcDataSourceName)
        config.addDataSourceProperty("url", url)
        user map { u => config.addDataSourceProperty("user", u)}
        password map {p => config.addDataSourceProperty("password", p)}
        config.setAutoCommit(true)
        config.setMaximumPoolSize(poolSize)
        config
    }

    protected def setValue[V](ps: PreparedStatement, f: (V) => Unit, i: Int, optionValue: Option[V], sqlType: Int): Unit =
        if (optionValue.isEmpty || optionValue == null)
            ps.setNull(i, sqlType)
        else
            f(optionValue.get)

    def setValue(ps: PreparedStatement, i: Int, storageValue: StorageValue): Unit = {
        storageValue match {
            case value: IntStorageValue =>
                setValue(ps, (v: Int) => ps.setInt(i, v), i, value.value, Types.INTEGER)
            case value: LongStorageValue =>
                setValue(ps, (v: Long) => ps.setLong(i, v), i, value.value, Types.DECIMAL)
            case value: BooleanStorageValue =>
                setValue(ps, (v: Boolean) => ps.setBoolean(i, v), i, value.value, Types.BIT)
            case value: StringStorageValue =>
                setValue(ps, (v: String) => ps.setString(i, v), i, value.value, Types.VARCHAR)
            case value: FloatStorageValue =>
                setValue(ps, (v: Float) => ps.setFloat(i, v), i, value.value, Types.FLOAT)
            case value: DateStorageValue =>
                setValue(ps, (v: Date) => ps.setTimestamp(i, new java.sql.Timestamp(v.getTime)), i, value.value, Types.TIMESTAMP)
            case value: DoubleStorageValue =>
                setValue(ps, (v: Double) => ps.setDouble(i, v), i, value.value, Types.DOUBLE)
            case value: BigDecimalStorageValue =>
                setValue(ps, (v: BigDecimal) => ps.setBigDecimal(i, v.bigDecimal), i, value.value, Types.BIGINT)
            case value: ByteArrayStorageValue =>
                setValue(ps, (v: Array[Byte]) => ps.setBytes(i, v), i, value.value, Types.BINARY)
            case value: ListStorageValue =>
                if (value.value.isDefined)
                    ps.setInt(i, 1)
                else
                    ps.setInt(i, 0)
            case value: ReferenceStorageValue =>
                setValue(ps, i, value.value)
        }
    }

    def getValue(resultSet: ResultSet, i: Int, storageValue: StorageValue): StorageValue =
        getValue(JdbcActivateResultSet(resultSet), i, storageValue)

    def getValue(resultSet: ActivateResultSet, i: Int, storageValue: StorageValue): StorageValue = {
        storageValue match {
            case value: IntStorageValue =>
                IntStorageValue(resultSet.getInt(i))
            case value: LongStorageValue =>
                LongStorageValue(resultSet.getLong(i))
            case value: BooleanStorageValue =>
                BooleanStorageValue(resultSet.getBoolean(i))
            case value: StringStorageValue =>
                StringStorageValue(resultSet.getString(i))
            case value: FloatStorageValue =>
                FloatStorageValue(resultSet.getFloat(i))
            case value: DateStorageValue =>
                DateStorageValue((resultSet.getTimestamp(i)).map((t: Timestamp) => new Date(t.getTime)))
            case value: DoubleStorageValue =>
                DoubleStorageValue(resultSet.getDouble(i))
            case value: BigDecimalStorageValue =>
                BigDecimalStorageValue(resultSet.getBigDecimal(i).map(BigDecimal(_)))
            case value: ByteArrayStorageValue =>
                ByteArrayStorageValue(resultSet.getBytes(i))
            case value: ReferenceStorageValue =>
                ReferenceStorageValue(getValue(resultSet, i, value.value).asInstanceOf[StorageOptionalValue])
        }
    }

    def toSqlDdlAction(action: ModifyStorageAction): List[NormalQlStatement] =
        action match {
            case action: StorageCreateListTable =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifNotExistsRestriction(findTableStatement(action.listTableName), action.ifNotExists))) ++
                    toSqlDdlAction(action.addOwnerIndexAction)
            case action: StorageRemoveListTable =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifExistsRestriction(findTableStatement(action.listTableName), action.ifExists)))
            case action: StorageCreateTable =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifNotExistsRestriction(findTableStatement(action.tableName), action.ifNotExists)))
            case action: StorageRenameTable =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifExistsRestriction(findTableStatement(action.oldName), action.ifExists)))
            case action: StorageRemoveTable =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifExistsRestriction(findTableStatement(action.name), action.ifExists)))
            case action: StorageAddColumn =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifNotExistsRestriction(findTableColumnStatement(action.tableName, action.column.name), action.ifNotExists)))
            case action: StorageRenameColumn =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifExistsRestriction(findTableColumnStatement(action.tableName, action.oldName), action.ifExists)))
            case action: StorageModifyColumnType =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifExistsRestriction(findTableColumnStatement(action.tableName, action.column.name), action.ifExists)))
            case action: StorageRemoveColumn =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifExistsRestriction(findTableColumnStatement(action.tableName, action.name), action.ifExists)))
            case action: StorageAddIndex =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifNotExistsRestriction(findIndexStatement(action.tableName, action.indexName), action.ifNotExists)))
            case action: StorageRemoveIndex =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifExistsRestriction(findIndexStatement(action.tableName, action.name), action.ifExists)))
            case action: StorageAddReference =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifNotExistsRestriction(findConstraintStatement(action.tableName, action.constraintName), action.ifNotExists)))
            case action: StorageRemoveReference =>
                List(new NormalQlStatement(
                    statement = toSqlDdl(action),
                    entityClass = classOf[BaseEntity],
                    restrictionQuery = ifExistsRestriction(findConstraintStatement(action.tableName, action.constraintName), action.ifExists)))
        }

    def versionVerifyQueries(reads: Map[Class[BaseEntity], List[(ReferenceStorageValue, Long)]], queryLimit: Int) =
        (for ((clazz, versions) <- reads if (versions.nonEmpty)) yield {
            val baseReferenceStorageValue = versions.head._1
            val conditions =
                for (i <- 0 until versions.size) yield {
                    val (id, version) = versions(i)
                    val condition = "(" + escape("id") + " = :id" + i + " and " + escape(versionVarName) + " is not null and " + escapeVersionField(versionVarName) + s" != :$versionVarName" + i + ")"
                    val bindId = ("id" + i) -> id
                    val bindVersion = (versionVarName + i) -> new LongStorageValue(Some(version))
                    (condition, bindId, bindVersion)
                }
            val groupedConditions = conditions.grouped(queryLimit)
            for (slice <- groupedConditions) yield {
                val queryConditions = slice.map(_._1)
                val binds: Map[String, StorageValue] = slice.map(_._2).toMap ++ slice.map(_._3).toMap
                val query = "SELECT " + escape("id") + " FROM " + toTableName(clazz) + " WHERE " + queryConditions.mkString(" OR ")
                (new NormalQlStatement(query, clazz, binds), baseReferenceStorageValue, clazz)
            }
        }).flatten
        
    def escapeVersionField(name: String) =
        escape(name)

    def ifExistsRestriction(statement: String, boolean: Boolean) =
        if (boolean)
            Option(statement, 1)
        else
            None

    def ifNotExistsRestriction(statement: String, boolean: Boolean) =
        if (boolean)
            Option(statement, 0)
        else
            None

    def toValue(storageValue: StorageValue): Any =
        storageValue match {
            case value: ListStorageValue =>
                if (value.value.isDefined)
                    "1"
                else
                    "0"
            case value: StorageOptionalValue =>
                value.value.getOrElse(null)
            case value: ReferenceStorageValue =>
                toValue(value.value)
        }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy