diff --git a/balta/src/main/scala/za/co/absa/db/balta/classes/QueryResult.scala b/balta/src/main/scala/za/co/absa/db/balta/classes/QueryResult.scala index aa3a987..f286292 100644 --- a/balta/src/main/scala/za/co/absa/db/balta/classes/QueryResult.scala +++ b/balta/src/main/scala/za/co/absa/db/balta/classes/QueryResult.scala @@ -56,4 +56,9 @@ class QueryResult(resultSet: ResultSet) extends Iterator[QueryResultRow] { throw new NoSuchElementException("No more rows in the result set") } } + + /** + * A naturally easily understandable opposite of `hasNext` + */ + def noMore: Boolean = !hasNext } diff --git a/balta/src/main/scala/za/co/absa/db/balta/classes/QueryResultRow.scala b/balta/src/main/scala/za/co/absa/db/balta/classes/QueryResultRow.scala index 337aceb..d19ab6f 100644 --- a/balta/src/main/scala/za/co/absa/db/balta/classes/QueryResultRow.scala +++ b/balta/src/main/scala/za/co/absa/db/balta/classes/QueryResultRow.scala @@ -37,14 +37,27 @@ class QueryResultRow private[classes](val rowNumber: Int, def columnCount: Int = fields.length def columnNumber(columnLabel: String): Int = columnNames(columnLabel.toLowerCase) - def apply(column: Int): Option[Object] = fields(column - 1) - def apply(columnLabel: String): Option[Object] = apply(columnNumber(columnLabel)) - - def getAs[T](column: Int, transformer: TransformerFnc[T]): Option[T] = apply(column).map(transformer) - def getAs[T](column: Int): Option[T] = apply(column)map(_.asInstanceOf[T]) + /** + * Extracts a value from the row by column number. + * @param column - the number of the column, 1 based + * @return - the value stored in the column, type `Any` is for warningless comparison with any type + */ + def apply(column: Int): Option[Any] = getObject(column - 1) + /** + * Extracts a value from the row by column name. + * @param columnLabel - the name of the column + * @return - the value stored in the column, type `Any` is for warningless comparison with any type + */ + def apply(columnLabel: String): Option[Any] = getObject(columnNumber(columnLabel)) + + def getAs[T](column: Int, transformer: TransformerFnc[T]): Option[T] = getObject(column).map(transformer) + def getAs[T](column: Int): Option[T] = getObject(column)map(_.asInstanceOf[T]) def getAs[T](columnLabel: String, transformer: TransformerFnc[T]): Option[T] = getAs(columnNumber(columnLabel), transformer) - def getAs[T](columnLabel: String): Option[T] = apply(columnNumber(columnLabel)).map(_.asInstanceOf[T]) + def getAs[T](columnLabel: String): Option[T] = getObject(columnNumber(columnLabel)).map(_.asInstanceOf[T]) + + def getObject(column: Int): Option[Object] = fields(column - 1) + def getObject(columnLabel: String): Option[Object] = getObject(columnNumber(columnLabel)) def getBoolean(column: Int): Option[Boolean] = getAs(column: Int, {item: Object => item.asInstanceOf[Boolean]}) def getBoolean(columnLabel: String): Option[Boolean] = getBoolean(columnNumber(columnLabel)) @@ -59,7 +72,6 @@ class QueryResultRow private[classes](val rowNumber: Int, } def getChar(columnLabel: String): Option[Char] = getChar(columnNumber(columnLabel)) - def getString(column: Int): Option[String] = getAs(column: Int, {item: Object => item.asInstanceOf[String]}) def getString(columnLabel: String): Option[String] = getString(columnNumber(columnLabel)) diff --git a/balta/src/main/scala/za/co/absa/db/balta/classes/setter/SetterFnc.scala b/balta/src/main/scala/za/co/absa/db/balta/classes/setter/SetterFnc.scala index 735566b..09be127 100644 --- a/balta/src/main/scala/za/co/absa/db/balta/classes/setter/SetterFnc.scala +++ b/balta/src/main/scala/za/co/absa/db/balta/classes/setter/SetterFnc.scala @@ -16,12 +16,12 @@ package za.co.absa.db.balta.classes.setter -import java.sql.{Date, PreparedStatement, Time, Timestamp, Types => SqlTypes} +import java.sql.{Date, PreparedStatement, Time, Types => SqlTypes} import java.util.UUID import org.postgresql.util.PGobject import za.co.absa.db.balta.classes.simple.{JsonBString, SimpleJsonString} -import java.time.{Instant, LocalDate, LocalTime, OffsetDateTime, ZoneId, ZoneOffset} +import java.time.{Instant, LocalDate, LocalTime, OffsetDateTime, ZoneOffset} /** * This is a trait representing a function that sets a value in a prepared statement. diff --git a/balta/src/main/scala/za/co/absa/db/balta/implicits/OptionImplicits.scala b/balta/src/main/scala/za/co/absa/db/balta/implicits/OptionImplicits.scala new file mode 100644 index 0000000..9d91e49 --- /dev/null +++ b/balta/src/main/scala/za/co/absa/db/balta/implicits/OptionImplicits.scala @@ -0,0 +1,40 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * 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 za.co.absa.db.balta.implicits + +object OptionImplicits { + implicit class OptionEnhancements[T](val option: Option[T]) extends AnyVal { + /** + * Gets the `option` value or throws the provided exception + * + * @param exception the exception to throw in case the `option` is None + * @return + */ + def getOrThrow(exception: => Throwable): T = { + option.getOrElse(throw exception) + } + + /** + * The function is an alias for `contains` method, but shorter and suitable for inflix usage + * + * @param value the value to check if present in the `option` + * @return true if the `option` is defined and contains the provided value, false otherwise + */ + def @=(value: T): Boolean = option.contains(value) + } + +} diff --git a/balta/src/test/scala/za/co/absa/db/balta/classes/QueryResultRowIntegrationTests.scala b/balta/src/test/scala/za/co/absa/db/balta/classes/QueryResultRowIntegrationTests.scala index bace35c..bd82773 100644 --- a/balta/src/test/scala/za/co/absa/db/balta/classes/QueryResultRowIntegrationTests.scala +++ b/balta/src/test/scala/za/co/absa/db/balta/classes/QueryResultRowIntegrationTests.scala @@ -54,6 +54,15 @@ class QueryResultRowIntegrationTests extends AnyFunSuiteLike with DBTestingConne assert(result == expecedResult) } + test("columnCount") { + assert(tableRows.head.columnCount == 14) + } + + test("rowNumber") { + assert(tableRows.head.rowNumber == 1) + assert(tableRows.tail.head.rowNumber == 2) + } + test("getLong") { //first row assert(tableRows.head.getLong(1).contains(1)) diff --git a/balta/src/test/scala/za/co/absa/db/balta/implicits/OptionImplicitsUnitTests.scala b/balta/src/test/scala/za/co/absa/db/balta/implicits/OptionImplicitsUnitTests.scala new file mode 100644 index 0000000..4ef9065 --- /dev/null +++ b/balta/src/test/scala/za/co/absa/db/balta/implicits/OptionImplicitsUnitTests.scala @@ -0,0 +1,49 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * 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 za.co.absa.db.balta.implicits + +import org.scalatest.funsuite.AnyFunSuiteLike +import za.co.absa.db.balta.implicits.OptionImplicits.OptionEnhancements + + +class OptionImplicitsUnitTests extends AnyFunSuiteLike { + test("getOrThrow returns the value if it is defined") { + val opt = Some(true) + assert(opt.getOrThrow(new Exception("Foo"))) + } + + test("getOrThrow throws an exception if the value is not defined") { + val opt = None + assertThrows[Exception](opt.getOrThrow(new Exception("Foo"))) + } + + test("@= returns true if the value is defined and equals the provided value") { + val opt = Some(42) + assert(opt @= 42) + } + + test("@= returns false if the value is defined but does not equal the provided value") { + val opt = Some(42) + assert(!(opt @= 43)) + } + + test("@= returns false if the value is not defined") { + val opt = None + assert(!(opt @= 42)) + } + +}