Skip to content

Commit

Permalink
Add Oracle
Browse files Browse the repository at this point in the history
  • Loading branch information
sake92 committed Jan 9, 2024
1 parent f87aa1b commit 1f18ac4
Show file tree
Hide file tree
Showing 12 changed files with 459 additions and 54 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@ Simple SQL queries in Scala 3.

No DSLs, no fuss, just plain SQL.

Scastie example https://scastie.scala-lang.org/93LkJTTVRVad2jNv1KE4HA
Supports *any* JDBC driver.
Additional support for Postgres, MySql, MariaDb, Oracle.


---
Scastie example: https://scastie.scala-lang.org/93LkJTTVRVad2jNv1KE4HA


---
Hello world:
```scala

Expand Down
6 changes: 3 additions & 3 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ object squery extends CommonScalaModule with SqueryPublishModule {
ivy"org.testcontainers:mysql:1.19.3",
ivy"mysql:mysql-connector-java:8.0.33",
ivy"org.testcontainers:mariadb:1.19.3",
ivy"org.mariadb.jdbc:mariadb-java-client:3.3.2"


ivy"org.mariadb.jdbc:mariadb-java-client:3.3.2",
ivy"org.testcontainers:oracle-free:1.19.3",
ivy"com.oracle.database.jdbc:ojdbc8:23.3.0.23.09"
)
}
}
Expand Down
5 changes: 5 additions & 0 deletions docs/src/files/tutorials/GettingStarted.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ object GettingStarted extends TutorialPage {

```scala
import ba.sake.squery.*
// import one of these if needed:
// import ba.sake.squery.postgres.*
// import ba.sake.squery.mysql.*
// import ba.sake.squery.mariadb.*
// import ba.sake.squery.oracle.*
val ds = com.zaxxer.hikari.HikariDataSource()
ds.setJdbcUrl(..)
Expand Down
13 changes: 13 additions & 0 deletions squery/src/ba/sake/squery/oracle/reads.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ba.sake.squery.oracle

import java.{sql => jsql}
import java.util.UUID
import ba.sake.squery.read.*

given SqlRead[UUID] with {
def readByName(jRes: jsql.ResultSet, colName: String): Option[UUID] =
Option(jRes.getString(colName)).map(UUID.fromString)

def readByIdx(jRes: jsql.ResultSet, colIdx: Int): Option[UUID] =
Option(jRes.getString(colIdx)).map(UUID.fromString)
}
15 changes: 15 additions & 0 deletions squery/src/ba/sake/squery/oracle/writes.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ba.sake.squery.oracle

import java.{sql => jsql}
import java.util.UUID
import ba.sake.squery.write.*

given SqlWrite[UUID] with {
def write(
ps: jsql.PreparedStatement,
idx: Int,
valueOpt: Option[UUID]
): Unit = valueOpt match
case Some(value) => ps.setString(idx, value.toString)
case None => ps.setString(idx, null)
}
14 changes: 8 additions & 6 deletions squery/src/ba/sake/squery/query.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,23 @@ case class Query(

private[squery] def newPreparedStatement(
c: jsql.Connection,
retGenKeys: Boolean = false
retGenKeys: Boolean = false,
colNames: Seq[String] = Seq.empty
): jsql.PreparedStatement = {
val enrichedQueryString = Query.enrichSqlQuery(sqlString)
// TODO reorder enriched issue..
// println("enriched: " + enrichedQueryString)
logger.debug(s"Executing statement: $enrichedQueryString")
val stat =
c.prepareStatement(
enrichedQueryString,
if retGenKeys then jsql.Statement.RETURN_GENERATED_KEYS
else jsql.Statement.NO_GENERATED_KEYS
)
if retGenKeys then
if colNames.isEmpty then c.prepareStatement(enrichedQueryString, jsql.Statement.RETURN_GENERATED_KEYS)
else c.prepareStatement(enrichedQueryString, colNames.toArray)
else c.prepareStatement(enrichedQueryString)

arguments.zipWithIndex.foreach { (arg, i) =>
arg.sqlWrite.write(stat, i + 1, Option(arg.value))
}

// print warnings if any
var warning = stat.getWarnings()
while (warning != null) {
Expand Down
12 changes: 6 additions & 6 deletions squery/src/ba/sake/squery/squery.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ extension (query: Query) {
* @return
* generated keys
*/
def insertReturningGenKeys[A]()(using
def insertReturningGenKeys[A](colNames: Seq[String] = Seq.empty)(using
c: SqueryConnection,
r: SqlRead[A]
): Seq[A] =
Using.resource(
query.newPreparedStatement(c.underlying, retGenKeys = true)
query.newPreparedStatement(c.underlying, retGenKeys = true, colNames)
) { stmt =>
stmt.executeUpdate()
val keysRes = stmt.getGeneratedKeys()
Expand All @@ -66,17 +66,17 @@ extension (query: Query) {
elems.result()
}

def insertReturningGenKeyOpt[A]()(using
def insertReturningGenKeyOpt[A](colName: Option[String] = None)(using
c: SqueryConnection,
r: SqlRead[A]
): Option[A] =
insertReturningGenKeys().headOption
insertReturningGenKeys(colName.toSeq).headOption

def insertReturningGenKey[A]()(using
def insertReturningGenKey[A](colName: Option[String] = None)(using
c: SqueryConnection,
r: SqlRead[A]
): A =
insertReturningGenKeyOpt().getOrElse(
insertReturningGenKeyOpt(colName).getOrElse(
throw SqueryException("No value returned from query")
)

Expand Down
13 changes: 8 additions & 5 deletions squery/test/src/ba/sake/squery/dataTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ package ba.sake.squery
import java.util.UUID
import java.time.Instant

case class Customer(id: Int, name: String, street: Option[String]) derives SqlReadRow
case class Customer(id: Int, name: String, street: Option[String]) derives SqlReadRow:
def insertTuple = sql"(${name}, ${street})"

case class CustomerBad(id: Int, name: String, street: String) derives SqlReadRow

// customer:phone 1:many
case class Phone(id: Int, number: String) derives SqlReadRow
case class Phone(id: Int, numbr: String) derives SqlReadRow:
def insertTuple(customerId: Int) = sql"(${customerId}, ${numbr})"

case class CustomerWithPhone(c: Customer, p: Phone) derives SqlReadRow
case class CustomerWithPhoneOpt(c: Customer, p: Option[Phone]) derives SqlReadRow

// customer:address many:many
case class Address(id: Int, name: Option[String]) derives SqlReadRow
case class CustomerWithAddressOpt(c: Customer, a: Option[Address]) derives SqlReadRow

case class Address(id: Int, name: Option[String]) derives SqlReadRow:
def insertTuple = sql"(${name})"

case class CustomerWithAddressOpt(c: Customer, a: Option[Address]) derives SqlReadRow
20 changes: 9 additions & 11 deletions squery/test/src/ba/sake/squery/mariadb/MariaDbSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class MariaDbSuite extends munit.FunSuite {

override def beforeAll(): Unit = {
container = MariaDBContainer("mariadb:11.2.2")
// // https://mariadb.com/kb/en/about-mariadb-connector-j/
container.withUrlParam("returnMultiValuesGeneratedIds", "true") // TODO document
container.start()

Expand All @@ -71,7 +72,7 @@ class MariaDbSuite extends munit.FunSuite {
CREATE TABLE phones(
id SERIAL PRIMARY KEY,
customer_id BIGINT(20) UNSIGNED REFERENCES customers(id),
number TEXT
numbr TEXT
)
""".update()

Expand Down Expand Up @@ -103,9 +104,9 @@ class MariaDbSuite extends munit.FunSuite {
customer2 = customer2.copy(id = customerIds(1))

val phoneIds = sql"""
INSERT INTO phones(customer_id, number) VALUES
(${customer1.id}, ${phone1.number}),
(${customer1.id}, ${phone2.number})
INSERT INTO phones(customer_id, numbr) VALUES
(${customer1.id}, ${phone1.numbr}),
(${customer1.id}, ${phone2.numbr})
""".insertReturningGenKeys[Int]()
phone1 = phone1.copy(id = phoneIds(0))
phone2 = phone2.copy(id = phoneIds(1))
Expand Down Expand Up @@ -143,13 +144,10 @@ class MariaDbSuite extends munit.FunSuite {
)

assertEquals(
sql"SELECT number FROM phones WHERE customer_id = ${customer1.id}"
sql"SELECT numbr FROM phones WHERE customer_id = ${customer1.id}"
.readValues[String](),
phones.map(_.number)
phones.map(_.numbr)
)

val q1 = sql""
val q2 = sql" ${q1} "
}
}

Expand All @@ -160,7 +158,7 @@ class MariaDbSuite extends munit.FunSuite {
assertEquals(
sql"""
SELECT c.id, c.name, c.street,
p.id, p.number
p.id, p.numbr
FROM customers c
JOIN phones p ON p.customer_id = c.id
WHERE c.id = ${customer1.id}
Expand All @@ -176,7 +174,7 @@ class MariaDbSuite extends munit.FunSuite {
assertEquals(
sql"""
SELECT c.id, c.name, c.street,
p.id, p.number
p.id, p.numbr
FROM customers c
LEFT JOIN phones p ON p.customer_id = c.id
ORDER BY c.id ASC, p.id ASC
Expand Down
19 changes: 8 additions & 11 deletions squery/test/src/ba/sake/squery/mysql/MySqlSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class MySqlSuite extends munit.FunSuite {
CREATE TABLE phones(
id SERIAL PRIMARY KEY,
customer_id INTEGER REFERENCES customers(id),
number TEXT
numbr TEXT
)
""".update()

Expand Down Expand Up @@ -98,9 +98,9 @@ class MySqlSuite extends munit.FunSuite {
customer2 = customer2.copy(id = customerIds(1))

val phoneIds = sql"""
INSERT INTO phones(customer_id, number) VALUES
(${customer1.id}, ${phone1.number}),
(${customer1.id}, ${phone2.number})
INSERT INTO phones(customer_id, numbr) VALUES
(${customer1.id}, ${phone1.numbr}),
(${customer1.id}, ${phone2.numbr})
""".insertReturningGenKeys[Int]()
phone1 = phone1.copy(id = phoneIds(0))
phone2 = phone2.copy(id = phoneIds(1))
Expand Down Expand Up @@ -138,13 +138,10 @@ class MySqlSuite extends munit.FunSuite {
)

assertEquals(
sql"SELECT number FROM phones WHERE customer_id = ${customer1.id}"
sql"SELECT numbr FROM phones WHERE customer_id = ${customer1.id}"
.readValues[String](),
phones.map(_.number)
phones.map(_.numbr)
)

val q1 = sql""
val q2 = sql" ${q1} "
}
}

Expand All @@ -155,7 +152,7 @@ class MySqlSuite extends munit.FunSuite {
assertEquals(
sql"""
SELECT c.id, c.name, c.street,
p.id, p.number
p.id, p.numbr
FROM customers c
JOIN phones p ON p.customer_id = c.id
WHERE c.id = ${customer1.id}
Expand All @@ -171,7 +168,7 @@ class MySqlSuite extends munit.FunSuite {
assertEquals(
sql"""
SELECT c.id, c.name, c.street,
p.id, p.number
p.id, p.numbr
FROM customers c
LEFT JOIN phones p ON p.customer_id = c.id
ORDER BY c.id ASC, p.id ASC
Expand Down
Loading

0 comments on commit 1f18ac4

Please sign in to comment.