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

insert.InsertBuilder.scala Maven / Gradle / Ivy

/*
* Copyright 2021 Kári Magnússon
*
* 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 kuzminki.insert

import kuzminki.api.{Model, CacheArg}
import kuzminki.column.{ModelCol, TypeCol}
import kuzminki.assign.ValueEq
import kuzminki.api.KuzminkiError
import kuzminki.section.WhereSec
import kuzminki.model.ModelTable
import kuzminki.shape.ParamShape
import kuzminki.section._
import kuzminki.render.{Prefix, Renderable, SectionCollector}


case class InsertBuilder[M <: Model, P](
  model: M,
  paramShape: ParamShape[P]
) extends BuilderMethods[M] {

  val cols = paramShape.cols
  def values = Vector.fill(paramShape.size)(CacheArg)

  def toValuesBuilder(params: P) =
    ValuesBuilder(model, cols, paramShape.conv.fromShape(params))

  def whereNotExistsReuse(uniqueCols: Vector[TypeCol[_]]) = {
    new ParamConvReuse(
      paramShape.conv,
      Reuse.fromIndex(cols, uniqueCols)
    )
  }

  def onConflictColDoUpdateReuse(updateCols: Vector[TypeCol[_]]) = {
    new ParamConvReuse(
      paramShape.conv,
      Reuse.fromIndex(cols, updateCols)
    )
  }
}


case class ValuesBuilder[M <: Model](
  model: M,
  cols: Vector[TypeCol[_]],
  values: Vector[Any]
) extends BuilderMethods[M]


trait BuilderMethods[M <: Model] {

  val model: M
  val cols: Vector[TypeCol[_]]
  def values: Vector[Any]
  val prefix = Prefix.forModel(model)

  def collector = {
    SectionCollector(prefix, Vector(
      InsertIntoSec(ModelTable(model)),
      InsertColumnsSec(cols),
      InsertValuesSec(values),
    ))
  }

  def returning(returningCols: Vector[TypeCol[_]]) = {
    SectionCollector(prefix, Vector(
      InsertIntoSec(ModelTable(model)),
      InsertColumnsSec(cols),
      InsertValuesSec(values),
      ReturningSec(returningCols)
    ))
  }

  def fromSelect(sub: Renderable) = {
    SectionCollector(prefix, Vector(
      InsertIntoSec(ModelTable(model)),
      InsertColumnsSec(cols),
      InsertSubquerySec(sub)
    ))
  }

  def cacheBlank = {
    SectionCollector(prefix, Vector(
      InsertIntoSec(ModelTable(model)),
      InsertColumnsSec(cols),
      InsertValuesSec(values)
    ))
  }

  def whereNotExists(uniqueCols: Vector[TypeCol[_]]) = {
    SectionCollector(prefix, Vector(
      InsertIntoSec(ModelTable(model)),
      InsertColumnsSec(cols),
      InsertWhereNotExistsSec(
        values,
        ModelTable(model),
        WhereSec(conditional(uniqueCols))
      )
    ))
  }

  def onConflictDoNothing = {
    SectionCollector(prefix, Vector(
      InsertIntoSec(ModelTable(model)),
      InsertColumnsSec(cols),
      InsertValuesSec(values),
      InsertOnConflictSec,
      InsertDoNothingSec
    ))
  }

  def onConflictColDoNothing(conflictCol: TypeCol[_]) = {
    SectionCollector(prefix, Vector(
      InsertIntoSec(ModelTable(model)),
      InsertColumnsSec(cols),
      InsertValuesSec(values),
      InsertOnConflictColumnSec(conflictCol),
      InsertDoNothingSec
    ))
  }

  def onConflictColDoUpdate(conflictCol: TypeCol[_], updateCols: Vector[TypeCol[_]]) = {
    SectionCollector(prefix, Vector(
      InsertIntoSec(ModelTable(model)),
      InsertColumnsSec(cols),
      InsertValuesSec(values),
      InsertOnConflictColumnSec(conflictCol),
      InsertDoUpdateSec(upsert(conflictCol, updateCols))
    ))
  }

  private def upsert(conflictCol: TypeCol[_], updateCols: Vector[TypeCol[_]]) = {
    
    updateCols.map {
      case col: ModelCol =>
      case _ => throw KuzminkiError("update columns cannot be a function")
    }

    if (updateCols.isEmpty) {
      throw KuzminkiError("no update columns selected")
    }

    if (updateCols.contains(conflictCol)) {
      throw KuzminkiError("cannot update the conflicting column")
    }

    val colValue = (cols zip values).toMap
    
    updateCols.map { col => 
      colValue.get(col) match {
        case Some(value) => ValueEq(col, value)
        case None => throw KuzminkiError(
          s"column must be an insert column to do upsert"
        )
      }
    }
  }

  private def conditional(uniqueCols: Vector[TypeCol[_]]) = {
    
    uniqueCols.map {
      case col: ModelCol =>
      case _ => throw KuzminkiError("update columns cannot be a function")
    }

    if (uniqueCols.isEmpty) {
      throw KuzminkiError("no update columns selected")
    }

    val colValue = (cols zip values).toMap
    
    uniqueCols.map { col => 
      colValue.get(col) match {
        case Some(value) => ValueEq(col, value)
        case None => throw KuzminkiError(
          s"column must be an insert column to do upsert"
        )
      }
    }
  }
}





























© 2015 - 2025 Weber Informatics LLC | Privacy Policy