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

com.dbobjekts.metadata.joins.DerivedJoin.kt Maven / Gradle / Ivy

There is a newer version: 0.6.0-RC2
Show newest version
package com.dbobjekts.metadata.joins

import com.dbobjekts.api.AnyColumn
import com.dbobjekts.api.AnyTable
import com.dbobjekts.api.exception.StatementBuilderException
import com.dbobjekts.metadata.column.IsForeignKey
import com.dbobjekts.statement.SQLOptions
import com.dbobjekts.statement.whereclause.SubClause
import com.dbobjekts.util.StringUtil

class DerivedJoin(
    table: AnyTable
) : Cloneable, JoinChain(table) {

    val pairs = mutableListOf>()

    constructor(joinChain: DerivedJoin) : this(joinChain.table)

    val joins = mutableListOf()

    fun on(clause: SubClause): ManualJoinChain {
        if (pairs.size != 1)
            throw StatementBuilderException("Illegal state")
        val (tableToJoin, joinType) = pairs[0]
        return when (joinType) {
            JoinType.LEFT -> ManualJoinChain(table).leftJoin(tableToJoin).on(clause)
            JoinType.INNER -> ManualJoinChain(table).innerJoin(tableToJoin).on(clause)
            JoinType.RIGHT -> ManualJoinChain(table).rightJoin(tableToJoin).on(clause)
        }
    }

    @Suppress("UNCHECKED_CAST")
    internal fun join(table: AnyTable, joinType: JoinType): DerivedJoin {
        pairs.add(Pair(table, joinType))
        return this
    }

    fun leftJoin(table: AnyTable): DerivedJoin = join(table, JoinType.LEFT)
    fun innerJoin(table: AnyTable): DerivedJoin = join(table, JoinType.INNER)
    fun rightJoin(table: AnyTable): DerivedJoin = join(table, JoinType.RIGHT)


    private fun createJoin(joinType: JoinType, tuple: Pair) = when (joinType) {
        JoinType.LEFT -> LeftJoin(tuple.first, tuple.second)
        JoinType.RIGHT -> RightJoin(tuple.first, tuple.second)
        JoinType.INNER -> InnerJoin(tuple.first, tuple.second)
    }

    private fun lastJoinTable(): AnyTable = if (joins.isEmpty()) table else joins.last().rightPart.table

    private fun checkTableNotJoinedAlready(table: AnyTable) {
        if (joins.any { it.containsTable(table) })
            throw StatementBuilderException("Table ${table.tableName} is already present in join. You cannot add it again: ${toSQL()}")
    }

    private fun extractJoinedColumnPair(table: AnyTable): Pair {
        val head =
            if (joins.isEmpty()) getJoinPair(lastJoinTable(), lastJoinTable(), table)
            else joins.reversed().map { getJoinPair(it.leftPart.table, it.rightPart.table, table) }.filterNotNull().firstOrNull()

        return if (head == null) throw StatementBuilderException("Cannot join ${table.toSQL()} to ${lastJoinTable().toSQL()}") else head
    }

    private fun getJoinPair(left: AnyTable, right: AnyTable, tableToAdd: AnyTable): Pair? {

        left.getForeignKeyToParent(tableToAdd)?.let {
            return Pair(
                it.column,
                tableToAdd.columnByName(it.parentColumn.nameInTable)
                    ?: throw StatementBuilderException("Cannot resolve foreign key for $tableToAdd")
            )
        }

        right.getForeignKeyToParent(tableToAdd)?.let {
            return Pair(
                it.column,
                tableToAdd.columnByName(it.parentColumn.nameInTable)
                    ?: throw StatementBuilderException("Cannot resolve foreign key for $tableToAdd")
            )
        }

        tableToAdd.getForeignKeyToParent(left)?.let {
            return Pair(
                left.columnByName(it.parentColumn.nameInTable)
                    ?: throw StatementBuilderException("Cannot resolve foreign key for $tableToAdd"), it.column
            )
        }

        tableToAdd.getForeignKeyToParent(right)?.let {
            return Pair(
                right.columnByName(it.parentColumn.nameInTable)
                    ?: throw StatementBuilderException("Cannot resolve foreign key for $tableToAdd"), it.column
            )
        }
        return null
    }

    override fun hasJoins() = pairs.isNotEmpty()

    override fun toSQL(options: SQLOptions): String {
        pairs.forEach { (t, j) ->
            checkTableNotJoinedAlready(t)
            joins += createJoin(j, extractJoinedColumnPair(t))
        }
        return StringUtil.concat(listOf(table.toSQL(options), JoinFactory.toSQL(joins.toList())))
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy