Skip to content

Commit c2b8348

Browse files
authored
#43: Upgrade Slick version and add Slick-pg dependency (#45)
* Introduced slick-pg optional dependency to increase the number of supported types * created a Slick profile to use the Slick-pg types (most of them) * added implicits to use with UUID type * `SlickPgFunction` and `SlickPgFunctionWithStatusSupport` traits were renamed to `SlickFunction` and `SlickFunctionWithStatusSupport`as they are not really Postgres dependent * added new alternative constructor to `DBSchema` and `DBFunction` * extended `README.md` to contain info on Slick module and integration tests
1 parent cff0bb6 commit c2b8348

File tree

13 files changed

+539
-21
lines changed

13 files changed

+539
-21
lines changed

README.md

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ ___
1313
- [What is fa-db](#what-is-fa-db)
1414
- [Usage](#usage)
1515
- [Concepts](#concepts)
16-
- [How to generate code coverage report](#how-to-generate-code-coverage-report)
16+
- [Slick module](#slick-module)
17+
- [Testing](#testing)
1718
- [How to Release](#how-to-release)
1819
<!-- tocstop -->
1920

@@ -49,7 +50,7 @@ within the application.**
4950
Currently, the library is developed with Postgres as the target DB. But the approach is applicable to any DB supporting stored procedure/functions – Oracle, MS-SQL, ...
5051

5152

52-
### Usage
53+
## Usage
5354

5455
#### Sbt
5556

@@ -105,15 +106,65 @@ Modules:
105106

106107
Text about status codes returned from the database function can be found [here](core/src/main/scala/za/co/absa/fadb/status/README.md).
107108

108-
## How to generate code coverage report
109+
110+
## Slick module
111+
112+
Slick module is the first (and so far only) implementation of fa-db able to execute. As the name suggests it runs on
113+
[Slick library](https://github.com/slick/slick) and also brings in the [Slickpg library](https://github.com/tminglei/slick-pg/) for extended Postgres type support.
114+
115+
It brings:
116+
117+
* `class SlickPgEngine` - implementation of _Core_'s `DBEngine` executing the queries via Slick
118+
* `trait SlickFunction` and `trait SlickFunctionWithStatusSupport` - mix-in traits to use with `FaDbFunction` descendants
119+
* `trait FaDbPostgresProfile` - to bring support for Postgres and its extended data types in one class (except JSON, as there are multiple implementations for this data type in _Slick-Pg_)
120+
* `object FaDbPostgresProfile` - instance of the above trait for direct use
121+
122+
#### Known issues
123+
124+
When getting result from `PositionedResult` for these types `HStore` -> `Option[Map[String, String]]` and
125+
`macaddr` -> `MacAddrString` type inference doesn't work well.
126+
So instead of:
127+
```scala
128+
val pr: PositionedResult = ???
129+
val hStore: Option[Map[String, String]] = pr.<<
130+
val macAddr: Option[MacAddrString] = pr.<<
131+
```
132+
133+
explicit extraction needs to be used:
134+
```scala
135+
val pr: PositionedResult = ???
136+
val hStore: Option[Map[String, String]] = pr.nextHStoreOption
137+
val macAddr: Option[MacAddrString] = pr.nextMacAddrOption
138+
```
139+
140+
## Testing
141+
142+
### How to generate unit tests code coverage report
143+
109144
```sbt
110145
sbt jacoco
111146
```
147+
112148
Code coverage will be generated on path:
149+
113150
```
114151
{project-root}/fa-db/{module}/target/scala-{scala_version}/jacoco/report/html
115152
```
116153

154+
### Integration tests
155+
156+
There are now integration tests as part of the project (at the time of writing they are in the _Slick_ module).
157+
158+
For the tests to work properly a running Postgres instance is needed. And then the following setup:
159+
* execute (content of) all `*.sql` files within `it/resources/sql/` folder within a posgres query tool
160+
* modify `it/resources/application.conf` to point to the database used in the previous point
161+
162+
How to execute the tests:
163+
164+
```sbt
165+
sbt it:test
166+
```
167+
117168
## How to Release
118169

119170
Please see [this file](RELEASE.md) for more details.

build.sbt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ lazy val commonJacocoReportSettings: JacocoReportSettings = JacocoReportSettings
4242
formats = Seq(JacocoReportFormats.HTML, JacocoReportFormats.XML)
4343
)
4444

45+
46+
/**
47+
* add `za.co.absa.fadb.naming.NamingConvention` to filter a class
48+
* or `za.co.absa.fadb.naming.NamingConvention*` to filter the class and all related objects
49+
*/
4550
lazy val commonJacocoExcludes: Seq[String] = Seq(
46-
"za.co.absa.fadb.package*"
47-
// "za.co.absa.fadb.naming_conventions.SnakeCaseNaming*", // class and related objects
48-
// "za.co.absa.fadb.naming_conventions.AsIsNaming" // class only
4951
)
5052

5153
lazy val parent = (project in file("."))
@@ -55,36 +57,41 @@ lazy val parent = (project in file("."))
5557
libraryDependencies ++= rootDependencies(scalaVersion.value),
5658
javacOptions ++= commonJavacOptions,
5759
scalacOptions ++= commonScalacOptions,
58-
publish / skip := true
60+
publish / skip := true,
61+
Defaults.itSettings
5962
)
6063

6164
lazy val faDbCore = (project in file("core"))
65+
.configs(IntegrationTest)
6266
.settings(
6367
name := "core",
6468
libraryDependencies ++= coreDependencies(scalaVersion.value),
6569
javacOptions ++= commonJavacOptions,
6670
scalacOptions ++= commonScalacOptions,
67-
(Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value // printScalaVersion is run with compile
71+
(Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile
6872
)
6973
.settings(
7074
jacocoReportSettings := commonJacocoReportSettings.withTitle(s"fa-db:core Jacoco Report - scala:${scalaVersion.value}"),
7175
jacocoExcludes := commonJacocoExcludes
7276
)
7377

7478
lazy val faDBSlick = (project in file("slick"))
79+
.configs(IntegrationTest)
7580
.settings(
7681
name := "slick",
7782
libraryDependencies ++= slickDependencies(scalaVersion.value),
7883
javacOptions ++= commonJavacOptions,
7984
scalacOptions ++= commonScalacOptions,
80-
(Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value // printScalaVersion is run with compile
85+
(Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile
86+
Defaults.itSettings,
8187
).dependsOn(faDbCore)
8288
.settings(
8389
jacocoReportSettings := commonJacocoReportSettings.withTitle(s"fa-db:slick Jacoco Report - scala:${scalaVersion.value}"),
8490
jacocoExcludes := commonJacocoExcludes
8591
)
8692

8793
lazy val faDBExamples = (project in file("examples"))
94+
.configs(IntegrationTest)
8895
.settings(
8996
name := "examples",
9097
libraryDependencies ++= examplesDependencies(scalaVersion.value),

core/src/main/scala/za/co/absa/fadb/DBFunction.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ abstract class DBFunction[I, R, E <: DBEngine](functionNameOverride: Option[Stri
3434
(implicit val schema: DBSchema, val dBEngine: E) extends DBFunctionFabric {
3535

3636
/* alternative constructors for different availability of input parameters */
37+
def this(functionNameOverride: String)
38+
(implicit schema: DBSchema, dBEngine: E) = {
39+
this(Option(functionNameOverride))(schema, dBEngine)
40+
}
41+
3742
def this(schema: DBSchema, functionNameOverride: String)
3843
(implicit dBEngine: E) = {
3944
this(Option(functionNameOverride))(schema, dBEngine)
@@ -106,6 +111,11 @@ object DBFunction {
106111
(implicit schema: DBSchema, dBEngine: E)
107112
extends DBFunction[I, R, E](functionNameOverride) {
108113

114+
def this(functionNameOverride: String)
115+
(implicit schema: DBSchema, dBEngine: E) = {
116+
this(Option(functionNameOverride))(schema, dBEngine)
117+
}
118+
109119
def this(schema: DBSchema, functionNameOverride: String)
110120
(implicit dBEngine: E) = {
111121
this(Option(functionNameOverride))(schema, dBEngine)
@@ -150,6 +160,11 @@ object DBFunction {
150160
(implicit schema: DBSchema, dBEngine: E)
151161
extends DBFunction[I, R, E](functionNameOverride) {
152162

163+
def this(functionNameOverride: String)
164+
(implicit schema: DBSchema, dBEngine: E) = {
165+
this(Option(functionNameOverride))(schema, dBEngine)
166+
}
167+
153168
def this(schema: DBSchema, functionNameOverride: String)
154169
(implicit dBEngine: E) = {
155170
this(Option(functionNameOverride))(schema, dBEngine)
@@ -193,6 +208,11 @@ object DBFunction {
193208
(implicit schema: DBSchema, dBEngine: E)
194209
extends DBFunction[I, R, E](functionNameOverride) {
195210

211+
def this(functionNameOverride: String)
212+
(implicit schema: DBSchema, dBEngine: E) = {
213+
this(Option(functionNameOverride))(schema, dBEngine)
214+
}
215+
196216
def this(schema: DBSchema, functionNameOverride: String)
197217
(implicit dBEngine: E) = {
198218
this(Option(functionNameOverride))(schema, dBEngine)

core/src/main/scala/za/co/absa/fadb/DBSchema.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ import za.co.absa.fadb.naming.NamingConvention
3030
abstract class DBSchema(schemaNameOverride: Option[String] = None)
3131
(implicit dBEngine: DBEngine, implicit val namingConvention: NamingConvention) {
3232

33+
def this(schemaNameOverride: String)
34+
(implicit dBEngine: DBEngine, namingConvention: NamingConvention) {
35+
this(Option(schemaNameOverride))(dBEngine, namingConvention)
36+
}
37+
3338
def this(dBEngine: DBEngine, schemaNameOverride: String)
3439
(implicit namingConvention: NamingConvention) {
3540
this(Option(schemaNameOverride))(dBEngine, namingConvention)

examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package za.co.absa.fadb.examples.enceladus
1818

1919
import za.co.absa.fadb.DBSchema
20-
import za.co.absa.fadb.slick.{SlickPgEngine, SlickPgFunction, SlickPgFunctionWithStatusSupport}
20+
import za.co.absa.fadb.slick.{SlickPgEngine, SlickFunction, SlickFunctionWithStatusSupport}
2121
import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention
2222
import slick.jdbc.{GetResult, SQLActionBuilder}
2323
import slick.jdbc.PostgresProfile.api._
@@ -63,7 +63,7 @@ object DatasetSchema {
6363

6464
final class AddSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine)
6565
extends DBSingleResultFunction[SchemaInput, Long, SlickPgEngine]
66-
with SlickPgFunctionWithStatusSupport[SchemaInput, Long]
66+
with SlickFunctionWithStatusSupport[SchemaInput, Long]
6767
with UserDefinedStatusHandling {
6868

6969
override protected def sql(values: SchemaInput): SQLActionBuilder = {
@@ -81,7 +81,7 @@ object DatasetSchema {
8181

8282
final class GetSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine)
8383
extends DBSingleResultFunction[(String, Option[Int]), Schema, SlickPgEngine]
84-
with SlickPgFunctionWithStatusSupport[(String, Option[Int]), Schema]
84+
with SlickFunctionWithStatusSupport[(String, Option[Int]), Schema]
8585
with UserDefinedStatusHandling {
8686

8787
/* This is an example of how to deal with overloaded DB functions - see different input type: Long vs what's in the class type: (String, Option[Int]) */
@@ -108,7 +108,7 @@ object DatasetSchema {
108108

109109
final class List(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine)
110110
extends DBMultipleResultFunction[Boolean, SchemaHeader, SlickPgEngine]()
111-
with SlickPgFunction[Boolean, SchemaHeader] {
111+
with SlickFunction[Boolean, SchemaHeader] {
112112

113113
override def apply(values: Boolean = false): Future[Seq[SchemaHeader]] = super.apply(values)
114114

project/Dependencies.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ object Dependencies {
2020

2121

2222
private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq(
23-
"org.scalatest" %% "scalatest" % "3.1.0" % Test,
24-
"org.scalatest" %% "scalatest-flatspec" % "3.2.0" % Test,
25-
"org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % Test
23+
"org.scalatest" %% "scalatest" % "3.1.0" % "test,it",
24+
"org.scalatest" %% "scalatest-flatspec" % "3.2.0" % "test,it",
25+
"org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % "test,it"
2626
)
2727

2828
def rootDependencies(scalaVersion: String): Seq[ModuleID] = Seq()
@@ -35,9 +35,10 @@ object Dependencies {
3535
def slickDependencies(scalaVersion: String): Seq[ModuleID] = {
3636
commonDependencies(scalaVersion) ++ Seq(
3737
"com.typesafe.slick" %% "slick" % "3.3.3",
38-
"org.slf4j" % "slf4j-nop" % "1.6.4",
38+
"org.slf4j" % "slf4j-nop" % "1.7.26",
3939
"com.typesafe.slick" %% "slick-hikaricp" % "3.3.3",
40-
"org.postgresql" % "postgresql" % "9.4-1206-jdbc42",
40+
"org.postgresql" % "postgresql" % "42.6.0",
41+
"com.github.tminglei" %% "slick-pg" % "0.20.4" % Optional
4142
)
4243
}
4344

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
postgrestestdb = {
2+
connectionPool = "HikariCP" //use HikariCP for our connection pool
3+
dataSourceClass = "org.postgresql.ds.PGSimpleDataSource" //Simple datasource with no connection pooling. The connection pool has already been specified with HikariCP.
4+
properties = {
5+
serverName = "localhost"
6+
portNumber = "5432"
7+
databaseName = "postgres"
8+
user = "postgres"
9+
password = "changeme"
10+
}
11+
numThreads = 10
12+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2022 ABSA Group Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
18+
CREATE EXTENSION IF NOT EXISTS hstore;
19+
20+
CREATE EXTENSION IF NOT EXISTS ltree;
21+
22+
CREATE OR REPLACE FUNCTION public.test_function(
23+
IN i_uuid1 UUID,
24+
IN i_dateTime1 DATE,
25+
IN i_dateTime2 TIME,
26+
IN i_dateTime3 TIMESTAMP WITHOUT TIME ZONE,
27+
IN i_dateTime4 INTERVAL,
28+
IN i_dateTime5 TIMESTAMP WITH TIME ZONE,
29+
IN i_dateTime6 TIMESTAMP WITH TIME ZONE,
30+
IN i_range1 INT4RANGE,
31+
IN i_ltree1 LTREE,
32+
IN i_map1 HSTORE,
33+
IN i_inet1 INET,
34+
IN i_macaddr1 MACADDR,
35+
OUT uuid1 UUID,
36+
OUT dateTime1 DATE,
37+
OUT dateTime2 TIME,
38+
OUT dateTime3 TIMESTAMP WITHOUT TIME ZONE,
39+
OUT dateTime4 INTERVAL,
40+
OUT dateTime5 TIMESTAMP WITH TIME ZONE,
41+
OUT dateTime6 TIMESTAMP WITH TIME ZONE,
42+
OUT range1 INT4RANGE,
43+
OUT ltree1 LTREE,
44+
OUT map1 HSTORE,
45+
OUT inet1 INET,
46+
OUT macaddr1 MACADDR
47+
) RETURNS record AS
48+
$$
49+
-------------------------------------------------------------------------------
50+
--
51+
-- Function: test_function(12)
52+
-- A function to test Fa-Db Slick Posgres special time enhancement. Function works as a mirror. Returns what came in.
53+
--
54+
--
55+
-- Returns:
56+
-- input is returned unchanged
57+
--
58+
-------------------------------------------------------------------------------
59+
DECLARE
60+
BEGIN
61+
uuid1 := i_uuid1;
62+
dateTime1 := i_dateTime1;
63+
dateTime2 := i_dateTime2;
64+
dateTime3 := i_dateTime3;
65+
dateTime4 := i_dateTime4;
66+
dateTime5 := i_dateTime5;
67+
dateTime6 := i_dateTime6;
68+
range1 := i_range1;
69+
ltree1 := i_ltree1;
70+
map1 := i_map1;
71+
inet1 := i_inet1;
72+
macaddr1 := i_macaddr1;
73+
74+
RETURN;
75+
END;
76+
$$
77+
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
78+
79+
GRANT EXECUTE ON FUNCTION test_function(
80+
UUID,
81+
DATE,
82+
TIME,
83+
TIMESTAMP WITHOUT TIME ZONE,
84+
INTERVAL,
85+
TIMESTAMP WITH TIME ZONE,
86+
TIMESTAMP WITH TIME ZONE,
87+
INT4RANGE,
88+
LTREE,
89+
HSTORE,
90+
INET,
91+
MACADDR
92+
) TO postgres;

0 commit comments

Comments
 (0)