Skip to content

Commit

Permalink
v0.15.0 Unenforced FK constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
marcgrue committed Dec 20, 2024
1 parent a4fb26d commit 47eb9e3
Show file tree
Hide file tree
Showing 38 changed files with 260 additions and 247 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ sbt.version = 1.10.6
`project/plugins.sbt`:

```scala
addSbtPlugin("org.scalamolecule" % "sbt-molecule" % "1.10.0")
addSbtPlugin("org.scalamolecule" % "sbt-molecule" % "1.11.0")
```

`build.sbt`:
Expand All @@ -207,12 +207,12 @@ lazy val yourProject = project.in(file("app"))
.settings(
libraryDependencies ++= Seq(
// One or more of:
"org.scalamolecule" %%% "molecule-sql-postgres" % "0.14.1",
"org.scalamolecule" %%% "molecule-sql-sqlite" % "0.14.1",
"org.scalamolecule" %%% "molecule-sql-mysql" % "0.14.1",
"org.scalamolecule" %%% "molecule-sql-mariadb" % "0.14.1",
"org.scalamolecule" %%% "molecule-sql-h2" % "0.14.1",
"org.scalamolecule" %%% "molecule-datalog-datomic" % "0.14.1",
"org.scalamolecule" %%% "molecule-sql-postgres" % "0.15.0",
"org.scalamolecule" %%% "molecule-sql-sqlite" % "0.15.0",
"org.scalamolecule" %%% "molecule-sql-mysql" % "0.15.0",
"org.scalamolecule" %%% "molecule-sql-mariadb" % "0.15.0",
"org.scalamolecule" %%% "molecule-sql-h2" % "0.15.0",
"org.scalamolecule" %%% "molecule-datalog-datomic" % "0.15.0",
),
moleculeSchemas := Seq("app/dataModel") // paths to directories with Data Model definition files
)
Expand Down
24 changes: 1 addition & 23 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ inThisBuild(
organizationName := "ScalaMolecule",
organizationHomepage := Some(url("http://www.scalamolecule.org")),
versionScheme := Some("early-semver"),
version := "0.14.1",
version := "0.15.0",
scalaVersion := scala213,
crossScalaVersions := allScala,

Expand Down Expand Up @@ -162,28 +162,6 @@ lazy val coreTests = crossProject(JSPlatform, JVMPlatform)
),
)
.dependsOn(core)
.settings(

TaskKey[Unit]("checkSources") := (updateClassifiers map checkSources).value,
TaskKey[Unit]("checkBinaries") := (update map checkBinaries).value
)

def getSources(report: UpdateReport) = report.matching(artifactFilter(`classifier` = "sources"))
def checkSources(report: UpdateReport): Unit = {
val srcs = getSources(report)
if (srcs.isEmpty)
sys.error(s"No sources retrieved\n\n$report")
else if (srcs.size != 2)
sys.error("Incorrect sources retrieved:\n\t" + srcs.mkString("\n\t"))
else
()
}

def checkBinaries(report: UpdateReport): Unit = {
val srcs = getSources(report)
if (srcs.nonEmpty) sys.error("Sources retrieved:\n\t" + srcs.mkString("\n\t"))
else ()
}


lazy val datalogCore = crossProject(JSPlatform, JVMPlatform)
Expand Down
10 changes: 4 additions & 6 deletions core/shared/src/main/scala/molecule/core/api/Api_async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,13 @@ trait Api_async_transact { api: Api_async with Spi_async =>
}


def unitOfWork[T](body: => Future[T])
def unitOfWork[T](runUOW: => Future[T])
(implicit conn: Conn, ec: EC): Future[T] = {
conn.setInsideUOW(true)
conn.waitCommitting()
body
runUOW
.map { t =>
// Commit all actions
conn.commit()
conn.setInsideUOW(false)
t
}
.recover { case e =>
Expand All @@ -117,8 +115,8 @@ trait Api_async_transact { api: Api_async with Spi_async =>
}


def savepoint[T](body: Savepoint => Future[T])
def savepoint[T](runSavepoint: Savepoint => Future[T])
(implicit conn: Conn, ec: EC): Future[T] = {
conn.savepoint_async(body)
conn.savepoint_async(runSavepoint)
}
}
10 changes: 4 additions & 6 deletions core/shared/src/main/scala/molecule/core/api/Api_io.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,12 @@ trait Api_io_transact { api: Api_io with Spi_io =>
// }


def unitOfWork[T](body: => IO[T])(implicit conn: Conn): IO[T] = {
conn.setInsideUOW(true)
def unitOfWork[T](runUOW: => IO[T])(implicit conn: Conn): IO[T] = {
conn.waitCommitting()
body.attempt.map {
runUOW.attempt.map {
case Right(t) =>
// Commit all actions
conn.commit()
conn.setInsideUOW(false)
t
case Left(error: Throwable) =>
// Rollback all executed actions so far
Expand All @@ -108,7 +106,7 @@ trait Api_io_transact { api: Api_io with Spi_io =>
}
}

def savepoint[T](body: Savepoint => IO[T])(implicit conn: Conn): IO[T] = {
conn.savepoint_io(body)
def savepoint[T](runSavepoint: Savepoint => IO[T])(implicit conn: Conn): IO[T] = {
conn.savepoint_io(runSavepoint)
}
}
10 changes: 4 additions & 6 deletions core/shared/src/main/scala/molecule/core/api/Api_sync.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,12 @@ trait Api_sync_transact { api: Api_sync with Spi_sync =>
}


def unitOfWork[T](body: => T)(implicit conn: Conn): T = {
conn.setInsideUOW(true)
def unitOfWork[T](runUOW: => T)(implicit conn: Conn): T = {
conn.waitCommitting()
try {
val result = body
val result = runUOW
// Commit all actions
conn.commit()
conn.setInsideUOW(false)
result
} catch {
case NonFatal(e) =>
Expand All @@ -103,7 +101,7 @@ trait Api_sync_transact { api: Api_sync with Spi_sync =>
}
}

def savepoint[T](body: Savepoint => T)(implicit conn: Conn): T = {
conn.savepoint_sync(body)
def savepoint[T](runSavepoint: Savepoint => T)(implicit conn: Conn): T = {
conn.savepoint_sync(runSavepoint)
}
}
10 changes: 4 additions & 6 deletions core/shared/src/main/scala/molecule/core/api/Api_zio.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,14 @@ trait Api_zio_transact { api: Api_zio with Spi_zio =>
)
}

def unitOfWork[T](body: => ZIO[Conn, MoleculeError, T]): ZIO[Conn, MoleculeError, T] = {
def unitOfWork[T](runUOW: => ZIO[Conn, MoleculeError, T]): ZIO[Conn, MoleculeError, T] = {
for {
conn <- ZIO.service[Conn]
_ = conn.setInsideUOW(true)
_ = conn.waitCommitting()
result <- body
result <- runUOW
.map { t =>
// Commit all actions
conn.commit()
conn.setInsideUOW(false)
t
}
.mapError { error =>
Expand All @@ -97,10 +95,10 @@ trait Api_zio_transact { api: Api_zio with Spi_zio =>
} yield result
}

def savepoint[T](body: Savepoint => ZIO[Conn, MoleculeError, T]): ZIO[Conn, MoleculeError, T] = {
def savepoint[T](runSavepoint: Savepoint => ZIO[Conn, MoleculeError, T]): ZIO[Conn, MoleculeError, T] = {
for {
conn <- ZIO.service[Conn]
result <- conn.savepoint_zio(body)
result <- conn.savepoint_zio(runSavepoint)
} yield result
}
}
16 changes: 2 additions & 14 deletions core/shared/src/main/scala/molecule/core/spi/Conn.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,28 +55,16 @@ abstract class Conn(val proxy: ConnProxy)

// Transaction handling ------------------------------------------------------

private var uow_ = false
protected var commit_ = true

def waitCommitting(): Unit = ???
def commit(): Unit = ???
def rollback(): Unit = ???

def savepoint_sync[T](body: Savepoint => T): T = ???
def savepoint_async[T](body: Savepoint => Future[T])
(implicit ec: ExecutionContext): Future[T] = ???

def savepoint_zio[T](
body: Savepoint => ZIO[Conn, MoleculeError, T]
): ZIO[Conn, MoleculeError, T] = ???

def savepoint_async[T](body: Savepoint => Future[T])(implicit ec: ExecutionContext): Future[T] = ???
def savepoint_zio[T](body: Savepoint => ZIO[Conn, MoleculeError, T]): ZIO[Conn, MoleculeError, T] = ???
def savepoint_io[T](body: Savepoint => IO[T]): IO[T] = ???


def setInsideUOW(inside: Boolean): Unit = uow_ = inside
def isInsideUOW: Boolean = uow_

def isInsideSavepoint: Boolean = ???

def setAutoCommit(bool: Boolean): Unit = ???
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,54 @@ trait Delete_id extends CoreTestSuite with Api_async { spi: Spi_async =>
}


"Orphan refs" - {

"delete ref" - refs { implicit conn =>
for {
b <- B.i(2).save.transact.map(_.id)
_ <- A.i(1).b(b).save.transact

// Delete B entity
_ <- B(b).delete.transact
_ <- B(b).i.query.get.map(_ ==> List())

_ <- if (database == "Datomic") {
// In Datomic the ref id and the referenced entity id is the same and therefore gone
A.b.query.get.map(_ ==> List())
} else {
// Orphan ref to B remains in the SQL A table
A.b.query.get.map(_ ==> List(b))
}

// But orphan ref points to nothing (the deleted ref entity)
_ <- A.i.B.i.query.get.map(_ ==> List())

// Add foreign key constraint to database schema manually to forbid deleting
// entities that other entities refer to (creating orphans).
// Copy constraints from the generated schema and add them to your live schema.
} yield ()
}

"delete ref and orphan ref id manually" - refs { implicit conn =>
for {
b <- B.i(2).save.transact.map(_.id)
a <- A.i(1).b(b).save.transact.map(_.id)

// Delete B entity
_ <- B(b).delete.transact
_ <- B(b).i.query.get.map(_ ==> List())

// Delete ref id to avoid orphan ref id hanging around pointing to nothing
_ <- A(a).b().update.transact

// No orphan ref and no relationship
_ <- A.b.query.get.map(_ ==> List())
_ <- A.i.B.i.query.get.map(_ ==> List())
} yield ()
}
}


"Owned entities" - {

"Card-one" - refs { implicit conn =>
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2")

addSbtPlugin("org.scalamolecule" % "sbt-molecule" % "1.10.0")
addSbtPlugin("org.scalamolecule" % "sbt-molecule" % "1.11.0")

libraryDependencies += "org.scala-js" %% "scalajs-env-jsdom-nodejs" % "1.1.0"

5 changes: 5 additions & 0 deletions project/releases/v0.1.0 Initial release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
v0.1.0 Initial release of molecule for multiple databases

This is a complete re-coded library based on years of active development of the [molecule-old](https://github.com/scalamolecule/molecule-old) library that only targeted the Datomic database.

This new library targets both Datomic and JDBC-compliant sql databases, and more are to be added. The Datomic implementation is quite complete while the JDBC implementation is still work-in-progress.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
v0.12.0 Map attr ops aligned
v0.12.1 Map attr ops aligned

Map attributes can now be filtered by key or value in a consistent way that corresponds to operations on a Scala `Map`. This should make working with Map attributes in Molecule feel natural for a Scala programmer.

Expand Down
File renamed without changes.
File renamed without changes.
28 changes: 28 additions & 0 deletions project/releases/v0.15.0 Unenforced FK constraints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
v0.15.0 Unenforced FK constraints

Using Molecule with `sbt-molecule` 1.11.0 no longer enforces foreign key constraints for SQL databases.


### Power and orphans

This is a double-edged sword since we can then delete any entity we want now.

But if another entity references it, that reference id becomes an orphan reference pointing nowhere (to the deleted entity). So we get freedom to delete what we want at the cost of risking having orphaned reference ids hanging around.


### Avoiding orphan refs

To avoid orphan ref ids, we can either

- delete orphan ref ids manually,
- add a foreign key constraint to our database schema or
- not care


### Adding a foreign key constraint

In the generated SQL schemas, you can copy any foreign key constraints you'd like to enforce and copy them to your live schema. Here's an example from an H2 schema:

```
// ALTER TABLE A ADD CONSTRAINT `_a` FOREIGN KEY (a) REFERENCES A (id);
```
5 changes: 5 additions & 0 deletions project/releases/v0.2.0 SPI implemented for H2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
v0.2.0 SPI implemented for H2

Molecule is now fully implemented for the [H2](https://h2database.com/html/main.html) database on both the JVM and JS platforms!

Composites and TxMetaData has been dropped - see comments [here](https://github.com/scalamolecule/molecule/commit/ff1b75585ddbc3218f10ffa431fc8df40cadccab). This makes compilation much faster and simplifies the api to core functionality that will satisfy the majority of all query and transaction needs of most projects.
3 changes: 3 additions & 0 deletions project/releases/v0.3.0 PostgreSQL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
v0.3.0 PostgreSQL implemented

Molecule SPI fully implemented for PostgreSQL.
3 changes: 3 additions & 0 deletions project/releases/v0.4.0 Mysql.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
v0.4.0 Mysql

SPI implemented for Mysql
3 changes: 3 additions & 0 deletions project/releases/v0.5.0 MariaDB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
v0.5.0 MariaDB

SPI implemented for MariaDB
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v0.5.1 Remove public dependency on CoreTests
12 changes: 12 additions & 0 deletions project/releases/v0.6.0 java.time types implemented.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
v0.6.0 java.time types implemented

The following `java.time._` types are now transparently mapped to all databases:

- `Duration`
- `Instant`
- `LocalDate`
- `LocalTime`
- `LocalDateTime`
- `OffsetTime`
- `OffsetDateTime`
- `ZonedDateTime`
Loading

0 comments on commit 47eb9e3

Please sign in to comment.