Skip to content

Commit

Permalink
Add Functions for Nullable values
Browse files Browse the repository at this point in the history
  • Loading branch information
Tayfun Oztemel committed Sep 20, 2024
1 parent 1bb5660 commit 21eba13
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,45 @@ trait EmptyFunctions { self: Magnets =>

sealed abstract class EmptyFunction[+V](val innerCol: Column) extends ExpressionColumn[V](innerCol)

case class Empty(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class NotEmpty(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class IsNull(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class Empty(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class NotEmpty(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class IsNull(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class IsNullable(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class IsNotNull(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class IsNotDistinctFrom(col: EmptyNonEmptyCol[_], other: EmptyNonEmptyCol[_])
extends EmptyFunction[Boolean](col.column)
case class IsZeroOrNull(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class IfNull(col: EmptyNonEmptyCol[_], alt: String) extends EmptyFunction[Boolean](col.column)
case class NullIf(col: EmptyNonEmptyCol[_], other: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class AssumeNotNull(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)
case class ToNullable(col: EmptyNonEmptyCol[_]) extends EmptyFunction[Boolean](col.column)

trait EmptyOps[C] { self: EmptyNonEmptyCol[_] =>

def empty(): Empty = Empty(self)
def notEmpty(): NotEmpty = NotEmpty(self)
def isNull(): IsNull = IsNull(self)
def empty(): Empty = Empty(self)
def notEmpty(): NotEmpty = NotEmpty(self)
def isNull(): IsNull = IsNull(self)
def isNotNull(): IsNotNull = IsNotNull(self)
def isNullable(): IsNullable = IsNullable(self)
def isNotDistinctFrom(other: EmptyNonEmptyCol[_]): IsNotDistinctFrom = IsNotDistinctFrom(self, other)
def isZeroOrNull(): IsZeroOrNull = IsZeroOrNull(self)
def ifNull(alternative: String): IfNull = IfNull(self, alternative)
def nullIf(other: EmptyNonEmptyCol[_]): NullIf = NullIf(self, other)
def assumeNotNull(): AssumeNotNull = AssumeNotNull(self)
def toNullable(): ToNullable = ToNullable(self)
}

def empty(col: EmptyNonEmptyCol[_]): Empty = Empty(col: EmptyNonEmptyCol[_])
def notEmpty(col: EmptyNonEmptyCol[_]): NotEmpty = NotEmpty(col: EmptyNonEmptyCol[_])
def isNull(col: EmptyNonEmptyCol[_]): IsNull = IsNull(col: EmptyNonEmptyCol[_])
def empty(col: EmptyNonEmptyCol[_]): Empty = Empty(col: EmptyNonEmptyCol[_])
def notEmpty(col: EmptyNonEmptyCol[_]): NotEmpty = NotEmpty(col: EmptyNonEmptyCol[_])
def isNull(col: EmptyNonEmptyCol[_]): IsNull = IsNull(col: EmptyNonEmptyCol[_])
def isNotNull(col: EmptyNonEmptyCol[_]): IsNotNull = IsNotNull(col: EmptyNonEmptyCol[_])
def isNullable(col: EmptyNonEmptyCol[_]): IsNullable = IsNullable(col: EmptyNonEmptyCol[_])
def isNotDistinctFrom(col: EmptyNonEmptyCol[_], other: EmptyNonEmptyCol[_]): IsNotDistinctFrom =
IsNotDistinctFrom(col: EmptyNonEmptyCol[_], other: EmptyNonEmptyCol[_])
def isZeroOrNull(col: EmptyNonEmptyCol[_]): IsZeroOrNull = IsZeroOrNull(col: EmptyNonEmptyCol[_])
def ifNull(col: EmptyNonEmptyCol[_], alternative: String): IfNull = IfNull(col: EmptyNonEmptyCol[_], alternative)
def nullIf(col: EmptyNonEmptyCol[_], other: EmptyNonEmptyCol[_]): NullIf =
NullIf(col: EmptyNonEmptyCol[_], other: EmptyNonEmptyCol[_])
def assumeNotNull(col: EmptyNonEmptyCol[_]): AssumeNotNull = AssumeNotNull(col: EmptyNonEmptyCol[_])
def toNullable(col: EmptyNonEmptyCol[_]): ToNullable = ToNullable(col: EmptyNonEmptyCol[_])
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,13 @@ trait EmptyFunctionTokenizer {
s"${tokenizeColumn(c.column)} != 0"
case _ => s"isNull(${tokenizeColumn(c.column)})"
}
case IsNotNull(c) => s"isNotNull(${tokenizeColumn(c.column)})"
case IsNullable(c) => s"isNullable(${tokenizeColumn(c.column)})"
case IsNotDistinctFrom(c, o) => s"isNotDistinctFrom(${tokenizeColumn(c.column)}, ${tokenizeColumn(o.column)})"
case IsZeroOrNull(c) => s"isZeroOrNull(${tokenizeColumn(c.column)})"
case IfNull(c, alt) => s"ifNull(${tokenizeColumn(c.column)}, '$alt')"
case NullIf(c, o) => s"nullIf(${tokenizeColumn(c.column)}, ${tokenizeColumn(o.column)})"
case AssumeNotNull(c) => s"assumeNotNull(${tokenizeColumn(c.column)})"
case ToNullable(c) => s"toNullable(${tokenizeColumn(c.column)})"
}
}
Original file line number Diff line number Diff line change
@@ -1,60 +1,145 @@
package com.crobox.clickhouse.dsl.language

import com.crobox.clickhouse.dsl._
import com.crobox.clickhouse.{dsl, DslTestSpec}
import com.crobox.clickhouse.{DslTestSpec, dsl}

class EmptyFunctionTokenizerTest extends DslTestSpec {

it should "UUID empty" in {
val result = toSQL(dsl.empty(nativeUUID))
if (serverVersion.minimalVersion(21, 8)) {
toSQL(dsl.empty(nativeUUID)) should matchSQL("empty(uuid)")
result should matchSQL("empty(uuid)")
} else {
toSQL(dsl.empty(nativeUUID)) should matchSQL("uuid == 0")
result should matchSQL("uuid == 0")
}
}

it should "UUID notEmpty" in {
val result = toSQL(dsl.notEmpty(nativeUUID))
if (serverVersion.minimalVersion(21, 8)) {
toSQL(dsl.notEmpty(nativeUUID)) should matchSQL("notEmpty(uuid)")
result should matchSQL("notEmpty(uuid)")
} else {
toSQL(dsl.notEmpty(nativeUUID)) should matchSQL("uuid != 0")
result should matchSQL("uuid != 0")
}
}

it should "rewrite empty to empty(0)" in {
val query = select(All()).from(TwoTestTable).where(nativeUUID.empty())
val query = select(All()).from(TwoTestTable).where(nativeUUID.empty())
val result = toSql(query.internalQuery, None)
if (serverVersion.minimalVersion(21, 8)) {
toSql(query.internalQuery, None) should matchSQL(
s"SELECT * FROM $database.twoTestTable WHERE empty(uuid)"
)
result should matchSQL(s"SELECT * FROM $database.twoTestTable WHERE empty(uuid)")
} else {
toSql(query.internalQuery, None) should matchSQL(
s"SELECT * FROM $database.twoTestTable WHERE uuid == 0"
)
result should matchSQL(s"SELECT * FROM $database.twoTestTable WHERE uuid == 0")
}
}

it should "rewrite notEmpty to notEquals(0)" in {
var query = select(All()).from(TwoTestTable).where(nativeUUID.notEmpty())
val query = select(All()).from(TwoTestTable).where(nativeUUID.notEmpty())
val result = toSql(query.internalQuery, None)
if (serverVersion.minimalVersion(21, 8)) {
toSql(query.internalQuery, None) should matchSQL(
s"SELECT * FROM $database.twoTestTable WHERE notEmpty(uuid)"
)
result should matchSQL(s"SELECT * FROM $database.twoTestTable WHERE notEmpty(uuid)")
} else {
toSql(query.internalQuery, None) should matchSQL(
s"SELECT * FROM $database.twoTestTable WHERE uuid != 0"
)
result should matchSQL(s"SELECT * FROM $database.twoTestTable WHERE uuid != 0")
}

query = select(All()).from(TwoTestTable).where(notEmpty(nativeUUID))
val query2 = select(All()).from(TwoTestTable).where(notEmpty(nativeUUID))
val result2 = toSql(query2.internalQuery, None)
if (serverVersion.minimalVersion(21, 8)) {
toSql(query.internalQuery, None) should matchSQL(
s"SELECT * FROM $database.twoTestTable WHERE notEmpty(uuid)"
)
result2 should matchSQL(s"SELECT * FROM $database.twoTestTable WHERE notEmpty(uuid)")
} else {
toSql(query.internalQuery, None) should matchSQL(
s"SELECT * FROM $database.twoTestTable WHERE uuid != 0"
)
result2 should matchSQL(s"SELECT * FROM $database.twoTestTable WHERE uuid != 0")
}
}

it should "tokenize IsNull" in {
val expected = s"SELECT * FROM $database.twoTestTable WHERE isNull(uuid)"

val query = select(All()).from(TwoTestTable).where(nativeUUID.isNull())
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(All()).from(TwoTestTable).where(isNull(nativeUUID))
toSql(query2.internalQuery, None) should matchSQL(expected)
}

it should "tokenize IsNotNull" in {
val expected = s"SELECT * FROM $database.twoTestTable WHERE isNotNull(uuid)"

val query = select(All()).from(TwoTestTable).where(nativeUUID.isNotNull())
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(All()).from(TwoTestTable).where(isNotNull(nativeUUID))
toSql(query2.internalQuery, None) should matchSQL(expected)
}

it should "tokenize IsNullable" in {
val expected = s"SELECT isNullable(uuid) FROM $database.twoTestTable"

val query = select(nativeUUID.isNullable()).from(TwoTestTable)
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(isNullable(nativeUUID)).from(TwoTestTable)
toSql(query2.internalQuery, None) should matchSQL(expected)
}

it should "tokenize IsNotDistinctFrom" in {
val expected = s"SELECT isNotDistinctFrom(uuid, uuid) FROM $database.twoTestTable"

val query = select(nativeUUID.isNotDistinctFrom(nativeUUID)).from(TwoTestTable)
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(isNotDistinctFrom(nativeUUID, nativeUUID)).from(TwoTestTable)
toSql(query2.internalQuery, None) should matchSQL(expected)
}

it should "tokenize IsZeroOrNull" in {
val expected = s"SELECT isZeroOrNull(uuid) FROM $database.twoTestTable"

val query = select(nativeUUID.isZeroOrNull()).from(TwoTestTable)
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(isZeroOrNull(nativeUUID)).from(TwoTestTable)
toSql(query2.internalQuery, None) should matchSQL(expected)
}

it should "tokenize IfNull" in {
val defaultValue = "alternative"
val expected = s"SELECT ifNull(uuid, '$defaultValue') FROM $database.twoTestTable"

val query = select(nativeUUID.ifNull(defaultValue)).from(TwoTestTable)
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(ifNull(nativeUUID, defaultValue)).from(TwoTestTable)
toSql(query2.internalQuery, None) should matchSQL(expected)
}

it should "tokenize NullIf" in {
val expected = s"SELECT nullIf(uuid, uuid) FROM $database.twoTestTable"

val query = select(nativeUUID.nullIf(nativeUUID)).from(TwoTestTable)
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(nullIf(nativeUUID, nativeUUID)).from(TwoTestTable)
toSql(query2.internalQuery, None) should matchSQL(expected)
}

it should "tokenize AssumeNotNull" in {
val expected = s"SELECT assumeNotNull(uuid) FROM $database.twoTestTable"

val query = select(nativeUUID.assumeNotNull()).from(TwoTestTable)
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(assumeNotNull(nativeUUID)).from(TwoTestTable)
toSql(query2.internalQuery, None) should matchSQL(expected)
}

it should "tokenize ToNullable" in {
val expected = s"SELECT toNullable(uuid) FROM $database.twoTestTable"

val query = select(nativeUUID.toNullable()).from(TwoTestTable)
toSql(query.internalQuery, None) should matchSQL(expected)

val query2 = select(toNullable(nativeUUID)).from(TwoTestTable)
toSql(query2.internalQuery, None) should matchSQL(expected)
}

}

0 comments on commit 21eba13

Please sign in to comment.