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

main.app.cash.backfila.client.jooq.CompoundKeyComparer.kt Maven / Gradle / Ivy

Go to download

Backfila is a service that manages backfill state, calling into other services to do batched work.

There is a newer version: 2024.10.28.205607-fab304f
Show newest version
package app.cash.backfila.client.jooq

import org.jooq.Condition
import org.jooq.Field
import org.jooq.Record

/**
 * Responsible for building JOOQ conditions when comparing compound key values.
 * @param compoundKeyFields the list of fields that make up the compound key, in order.
 */
class CompoundKeyComparer(private val compoundKeyFields: List>) {
  /**
   * Builds JOOQ conditions for comparing compound key values using greater than (>) semantics.
   *
   * Some examples will make it clear how this works.
   *
   * For a single-field (f1) comparison against a single value (v1), this produces:
   * f1 > v1
   *
   * For a double-field (f1, f2) comparison against a compound value (v1, v2), this produces:
   * f1 > v1 OR (f1 = v1 AND f2 > v2)
   *
   * For a triple-field (f1, f2, f3) comparison against a compound value (v1, v2, v3), this produces:
   * f1 > v1 OR (f1 = v1 AND f2 > v2) OR (f1 = v1 AND f2 = v2 AND f3 > v3)
   *
   * @param compoundKeyValue JOOQ record containing the field values.
   * @return JOOQ condition
   */
  fun gt(compoundKeyValue: Record): Condition {
    return buildCondition(
      compoundKeyValue,
    ) { obj: Field, value: T -> obj.greaterThan(value) }
  }

  /**
   * Builds JOOQ conditions for comparing compound key values using greater than or equal to (>=) semantics.
   *
   * Some examples will make it clear how this works.
   *
   * For a single-field (f1) comparison against a single value (v1), this produces:
   * f1 >= v1
   *
   * For a double-field (f1, f2) comparison against a compound value (v1, v2), this produces:
   * f1 > v1 OR (f1 = v1 AND f2 >= v2)
   *
   * For a triple-field (f1, f2, f3) comparison against a compound value (v1, v2, v3), this produces:
   * f1 > v1 OR (f1 = v1 AND f2 > v2) OR (f1 = v1 AND f2 = v2 AND f3 >= v3)
   *
   * @param compoundKeyValue JOOQ record containing the field values.
   * @return JOOQ condition
   */
  fun gte(compoundKeyValue: Record): Condition {
    return buildCondition(
      compoundKeyValue,
      { obj: Field, value: T -> obj.greaterThan(value) },
      { obj: Field, value: T -> obj.greaterOrEqual(value) },
    )
  }

  /**
   * Builds JOOQ conditions for comparing compound key values using less than (<) semantics.
   *
   * Some examples will make it clear how this works.
   *
   * For a single-field (f1) comparison against a single value (v1), this produces:
   * f1 < v1
   *
   * For a double-field (f1, f2) comparison against a compound value (v1, v2), this produces:
   * f1 < v1 OR (f1 = v1 AND f2 < v2)
   *
   * For a triple-field (f1, f2, f3) comparison against a compound value (v1, v2, v3), this produces:
   * f1 < v1 OR (f1 = v1 AND f2 < v2) OR (f1 = v1 AND f2 = v2 AND f3 < v3)
   *
   * @param compoundKeyValue JOOQ record containing the field values.
   * @return JOOQ condition
   */
  fun lt(compoundKeyValue: Record): Condition {
    return buildCondition(
      compoundKeyValue,
    ) { obj: Field, value: T -> obj.lessThan(value) }
  }

  /**
   * Builds JOOQ conditions for comparing compound key values using less than or equal to (<=) semantics.
   *
   * For a single-field (f1) comparison against a single value (v1), this produces:
   * f1 <= v1
   *
   * For a double-field (f1, f2) comparison against a compound value (v1, v2), this produces:
   * f1 < v1 OR (f1 = v1 AND f2 <= v2)
   *
   * For a triple-field (f1, f2, f3) comparison against a compound value (v1, v2, v3), this produces:
   * f1 < v1 OR (f1 = v1 AND f2 < v2) OR (f1 = v1 AND f2 = v2 AND f3 <= v3)
   *
   * @param compoundKeyValue JOOQ record containing the field values.
   * @return JOOQ condition
   */
  fun lte(compoundKeyValue: Record): Condition {
    return buildCondition(
      compoundKeyValue,
      { obj: Field, value: T -> obj.lessThan(value) },
      { obj: Field, value: T -> obj.lessOrEqual(value) },
    )
  }

  private fun buildCondition(
    compoundKeyValue: Record,
    comparison: (field: Field, value: T) -> Condition,
  ): Condition {
    return buildCondition(compoundKeyValue, comparison, comparison)
  }

  private fun buildCondition(
    compoundKeyValue: Record,
    mainComparison: (field: Field, value: T) -> Condition,
    lastFieldComparison: (field: Field, value: T) -> Condition,
  ): Condition {
    var overallCondition: Condition? = null
    var priorFieldsEqualCondition: Condition? = null
    for (i in compoundKeyFields.indices) {
      val field = compoundKeyFields[i]
      val comparison = if (i == compoundKeyFields.size - 1) lastFieldComparison else mainComparison
      overallCondition = or(
        overallCondition,
        and(
          priorFieldsEqualCondition,
          @Suppress("UNCHECKED_CAST")
          comparison(field as Field, compoundKeyValue.get(field)),
        ),
      )
      priorFieldsEqualCondition = and(
        priorFieldsEqualCondition,
        field.eq(compoundKeyValue.get(field)),
      )
    }
    return overallCondition ?: throw IllegalStateException("overall condition cannot be null")
  }

  companion object {
    /**
     * Like [Condition.and] but tolerates a null condition so we don't have to
     * pollute our generated SQL with an "always true" condition.
     */
    private fun and(c1: Condition?, c2: Condition): Condition {
      return c1?.and(c2) ?: c2
    }

    /**
     * Like [Condition.or] but tolerates a null condition so we don't have to
     * pollute our generated SQL with an "always false" condition.
     */
    private fun or(c1: Condition?, c2: Condition): Condition {
      return c1?.or(c2) ?: c2
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy