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

org.ufoss.kotysa.DefaultSqlClientDeleteOrUpdate.kt Maven / Gradle / Ivy

/*
 * This is free and unencumbered software released into the public domain, following 
 */

package org.ufoss.kotysa

import org.ufoss.kolog.Logger
import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.OffsetDateTime
import java.util.*

private val logger = Logger.of()


public open class DefaultSqlClientDeleteOrUpdate protected constructor() : DefaultSqlClientCommon() {

    public class Properties internal constructor(
        override val tables: Tables,
        /**
         * targeted table to update
         */
        public val table: KotysaTable,
        override val dbAccessType: DbAccessType,
        override val module: Module,
    ) : DefaultSqlClientCommon.Properties {
        override val parameters: MutableList = mutableListOf()
        override val fromClauses: MutableList = mutableListOf()
        override val whereClauses: MutableList = mutableListOf()

        override val availableTables: MutableMap, KotysaTable<*>> = mutableMapOf()
        override val availableColumns: MutableMap, KotysaColumn<*, *>> = mutableMapOf()
        override var index: Int = 0

        public val updateClauses: MutableList> = mutableListOf()
    }

    public interface WithProperties {
        public val properties: Properties
    }

    public abstract class FirstDeleteOrUpdate, V : SqlClientQuery.Where>
    protected constructor(
        private val dbAccessType: DbAccessType,
        private val module: Module,
    ) : FromTableWhereable(), FromTable {
        protected abstract val tables: Tables
        protected abstract val table: Table

        override val properties: Properties by lazy {
            val kotysaTable = tables.getTable(table)
            val properties = Properties(tables, kotysaTable, dbAccessType, module)
            // init availableColumns with table columns
            addFromTable(properties, kotysaTable)
            properties
        }
    }

    public abstract class DeleteOrUpdate, V : SqlClientQuery.Where>
    protected constructor(
    ) : FromTableWhereable(), FromTable


    public abstract class Update, V : SqlClientQuery.Where,
            W : SqlClientQuery.Update, X : UpdateInt> protected constructor(
        dbAccessType: DbAccessType,
        module: Module
    ) : FirstDeleteOrUpdate(dbAccessType, module), SqlClientQuery.Update, UpdateInt {

        protected abstract val update: W
        protected abstract val updateInt: X

        private val updateOpStringColumnNotNull: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpStringColumnNullable: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpLocalDateTimeColumnNotNull
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpLocalDateTimeColumnNullable
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpKotlinxLocalDateTimeColumnNotNull
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpKotlinxLocalDateTimeColumnNullable
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpLocalDateColumnNotNull: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpLocalDateColumnNullable: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpKotlinxLocalDateColumnNotNull
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpKotlinxLocalDateColumnNullable
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpOffsetDateTimeColumnNotNull
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpOffsetDateTimeColumnNullable
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpLocalTimeColumnNotNull: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpLocalTimeColumnNullable: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpKotlinxLocalTimeColumnNotNull
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpKotlinxLocalTimeColumnNullable
                : UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpBooleanColumnNotNull: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpIntColumnNotNull: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpIntColumnNullable: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpLongColumnNotNull: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpLongColumnNullable: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpFloatColumnNotNull: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpFloatColumnNullable: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpDoubleColumnNotNull: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpDoubleColumnNullable: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpBigDecimalColumnNotNull: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpBigDecimalColumnNullable: UpdateOpIntColumn, X> by lazy {
            UpdateOpIntColumn(update, updateInt, properties)
        }
        private val updateOpUuidColumnNotNull: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpUuidColumnNullable: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpByteArrayColumnNotNull: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }
        private val updateOpByteArrayColumnNullable: UpdateOpColumn, X> by lazy {
            UpdateOpColumn(update, properties)
        }

        override fun set(stringColumnNotNull: StringColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpStringColumnNotNull.apply { this.column = stringColumnNotNull }

        override fun set(stringColumnNullable: StringColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpStringColumnNullable.apply { this.column = stringColumnNullable }

        override fun set(localDateTimeColumnNotNull: LocalDateTimeColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpLocalDateTimeColumnNotNull.apply { this.column = localDateTimeColumnNotNull }

        override fun set(localDateTimeColumnNullable: LocalDateTimeColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpLocalDateTimeColumnNullable.apply { this.column = localDateTimeColumnNullable }

        override fun set(kotlinxLocalDateTimeColumnNotNull: KotlinxLocalDateTimeColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpKotlinxLocalDateTimeColumnNotNull.apply { this.column = kotlinxLocalDateTimeColumnNotNull }

        override fun set(kotlinxLocalDateTimeColumnNullable: KotlinxLocalDateTimeColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpKotlinxLocalDateTimeColumnNullable.apply { this.column = kotlinxLocalDateTimeColumnNullable }

        override fun set(localDateColumnNotNull: LocalDateColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpLocalDateColumnNotNull.apply { this.column = localDateColumnNotNull }

        override fun set(localDateColumnNullable: LocalDateColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpLocalDateColumnNullable.apply { this.column = localDateColumnNullable }

        override fun set(kotlinxLocalDateColumnNotNull: KotlinxLocalDateColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpKotlinxLocalDateColumnNotNull.apply { this.column = kotlinxLocalDateColumnNotNull }

        override fun set(kotlinxLocalDateColumnNullable: KotlinxLocalDateColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpKotlinxLocalDateColumnNullable.apply { this.column = kotlinxLocalDateColumnNullable }

        override fun set(offsetDateTimeColumnNotNull: OffsetDateTimeColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpOffsetDateTimeColumnNotNull.apply { this.column = offsetDateTimeColumnNotNull }

        override fun set(offsetDateTimeColumnNullable: OffsetDateTimeColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpOffsetDateTimeColumnNullable.apply { this.column = offsetDateTimeColumnNullable }

        override fun set(localTimeColumnNotNull: LocalTimeColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpLocalTimeColumnNotNull.apply { this.column = localTimeColumnNotNull }

        override fun set(localTimeColumnNullable: LocalTimeColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpLocalTimeColumnNullable.apply { this.column = localTimeColumnNullable }

        override fun set(kotlinxLocalTimeColumnNotNull: KotlinxLocalTimeColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpKotlinxLocalTimeColumnNotNull.apply { this.column = kotlinxLocalTimeColumnNotNull }

        override fun set(kotlinxLocalTimeColumnNullable: KotlinxLocalTimeColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpKotlinxLocalTimeColumnNullable.apply { this.column = kotlinxLocalTimeColumnNullable }
        
        override fun set(booleanColumnNotNull: BooleanColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpBooleanColumnNotNull.apply { this.column = booleanColumnNotNull }

        override fun set(intColumnNotNull: IntColumnNotNull)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpIntColumnNotNull.apply { this.column = intColumnNotNull }

        override fun set(intColumnNullable: IntColumnNullable)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpIntColumnNullable.apply { this.column = intColumnNullable }

        override fun set(longColumnNotNull: LongColumnNotNull)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpLongColumnNotNull.apply { this.column = longColumnNotNull }

        override fun set(longColumnNullable: LongColumnNullable)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpLongColumnNullable.apply { this.column = longColumnNullable }

        override fun set(floatColumnNotNull: FloatColumnNotNull)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpFloatColumnNotNull.apply { this.column = floatColumnNotNull }

        override fun set(floatColumnNullable: FloatColumnNullable)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpFloatColumnNullable.apply { this.column = floatColumnNullable }

        override fun set(doubleColumnNotNull: DoubleColumnNotNull)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpDoubleColumnNotNull.apply { this.column = doubleColumnNotNull }

        override fun set(doubleColumnNullable: DoubleColumnNullable)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpDoubleColumnNullable.apply { this.column = doubleColumnNullable }

        override fun set(bigDecimalColumnNotNull: BigDecimalColumnNotNull)
                : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpBigDecimalColumnNotNull.apply { this.column = bigDecimalColumnNotNull }

        override fun set(bigDecimalColumnNullable: BigDecimalColumnNullable)
        : SqlClientQuery.UpdateOpIntColumn, X> =
            updateOpBigDecimalColumnNullable.apply { this.column = bigDecimalColumnNullable }

        override fun set(uuidColumnNotNull: UuidColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpUuidColumnNotNull.apply { this.column = uuidColumnNotNull }

        override fun set(uuidColumnNullable: UuidColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpUuidColumnNullable.apply { this.column = uuidColumnNullable }

        override fun set(byteArrayColumnNotNull: ByteArrayColumnNotNull)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpByteArrayColumnNotNull.apply { this.column = byteArrayColumnNotNull }

        override fun set(byteArrayColumnNullable: ByteArrayColumnNullable)
                : SqlClientQuery.UpdateOpColumn, X> =
            updateOpByteArrayColumnNullable.apply { this.column = byteArrayColumnNullable }

        override fun plus(increment: Int): W {
            val lastUpdate = properties.updateClauses.last() as UpdateClauseColumn
            lastUpdate.increment = increment
            return update
        }

        override fun minus(decrement: Int): W {
            val lastUpdate = properties.updateClauses.last() as UpdateClauseColumn
            lastUpdate.increment = -decrement
            return update
        }
    }

    internal class UpdateOpColumn, V, W : Column<*, *>,
            X : UpdateInt> internal constructor(
        internal val update: U,
        internal val properties: Properties,
    ) : SqlClientQuery.UpdateOpColumn {
        internal lateinit var column: DbColumn

        override fun eq(value: V): U = with(properties) {
            updateClauses.add(UpdateClauseValue(column))
            parameters.add(value)
            update
        }

        override fun eq(otherColumn: W): U = with(properties) {
            updateClauses.add(UpdateClauseColumn(column, otherColumn))
            update
        }
    }

    internal class UpdateOpIntColumn, V, W : Column<*, *>,
            X : UpdateInt> internal constructor(
        private val update: U,
        private val updateInt: X,
        private val properties: Properties,
    ) : SqlClientQuery.UpdateOpIntColumn {
        internal lateinit var column: DbColumn

        override fun eq(value: V): U = with(properties) {
            updateClauses.add(UpdateClauseValue(column))
            parameters.add(value)
            update
        }

        override fun eq(otherColumn: W): X = with(properties) {
            updateClauses.add(UpdateClauseColumn(column, otherColumn))
            updateInt
        }
    }

    public abstract class Where>
        : DefaultSqlClientCommon.Where(), WithProperties, Return

    public interface Return : DefaultSqlClientCommon.Return, WithProperties {

        public fun deleteFromTableSql(): String = with(properties) {
            val deleteSql = "DELETE FROM ${table.name}"
            val joinsAndWheres = joinsWithExistsAndWheres()
            logger.debug { "Exec SQL (${tables.dbType.name}) : $deleteSql $joinsAndWheres" }

            "$deleteSql $joinsAndWheres"
        }

        public fun updateTableSql(): String = with(properties) {
            val updateSql = "UPDATE ${table.name}"
            val setSql = updateClauses.joinToString(prefix = "SET ") { updateClause ->
                updateClause.run {
                    val fieldName = column.getKotysaColumn(availableColumns).name
                    when (this) {
                        is UpdateClauseValue -> "$fieldName = ${variable()}"
                        is UpdateClauseColumn -> {
                            var updateColumn = "$fieldName = ${
                                otherColumn.getFieldName(
                                    availableColumns,
                                    tables.dbType
                                )
                            }"
                            if (this.increment != null) {
                                if (this.increment!! > 0) {
                                    updateColumn = updateColumn + "+" + this.increment
                                } else {
                                    updateColumn += this.increment
                                }
                            }
                            updateColumn
                        }
                    }
                }
            }
            val joinsAndWheres = joinsWithExistsAndWheres()
            logger.debug { "Exec SQL (${tables.dbType.name}) : $updateSql $setSql $joinsAndWheres" }

            "$updateSql $setSql $joinsAndWheres"
        }

        /**
         * Handle joins as EXISTS + nested SELECT
         * Then other WHERE clauses
         */
        public fun joinsWithExistsAndWheres(withWhere: Boolean = true): String = with(properties) {
            val joins = joinsWithExists()

            var wheres = wheres(false)

            if (joins.isEmpty() && wheres.isEmpty()) {
                ""
            } else {
                val prefix = if (withWhere) {
                    "WHERE "
                } else {
                    ""
                }
                if (joins.isNotEmpty()) {
                    if (wheres.isNotEmpty()) {
                        wheres = "AND $wheres"
                    }
                    "$prefix$joins $wheres )"
                } else {
                    "$prefix$wheres"
                }
            }
        }

        /**
         * Handle froms as EXISTS + nested SELECT
         */
        private fun joinsWithExists() = with(properties) {
            val rootJoinClauses = (fromClauses[0] as FromClauseTable<*>).joinClauses
            if (fromClauses.size > 1 || rootJoinClauses.isNotEmpty()) {
                // fixme handle other cases
                if (rootJoinClauses.isNotEmpty()) {
                    val firstFroms = rootJoinClauses
                        .joinToString { joinClause ->
                            joinClause.table.getFieldName(tables.allTables)
                        }
                    val wheres = rootJoinClauses
                        .flatMap { joinClause -> joinClause.references.asIterable() }
                        .joinToString(" AND ", "(", ")") { joinClause ->
                            "${joinClause.key.getFieldName(tables.allColumns, tables.dbType)} = " +
                                    joinClause.value.getFieldName(tables.allColumns, tables.dbType)
                        }
                    // remaining froms
                    fromClauses.removeAt(0)
                    val froms = froms(false)

                    "EXISTS ( SELECT * FROM $firstFroms $froms WHERE $wheres"
                } else {
                    // remaining froms
                    fromClauses.removeAt(0)
                    val froms = froms()
                    "EXISTS ( SELECT * $froms"
                }

            } else {
                ""
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy