From 1f18ac49e7131f9b45c3de83c440837de5679a0d Mon Sep 17 00:00:00 2001 From: Sakib Hadziavdic Date: Tue, 9 Jan 2024 12:40:47 +0100 Subject: [PATCH] Add Oracle --- README.md | 9 +- build.sc | 6 +- docs/src/files/tutorials/GettingStarted.scala | 5 + squery/src/ba/sake/squery/oracle/reads.scala | 13 + squery/src/ba/sake/squery/oracle/writes.scala | 15 + squery/src/ba/sake/squery/query.scala | 14 +- squery/src/ba/sake/squery/squery.scala | 12 +- .../test/src/ba/sake/squery/dataTypes.scala | 13 +- .../ba/sake/squery/mariadb/MariaDbSuite.scala | 20 +- .../src/ba/sake/squery/mysql/MySqlSuite.scala | 19 +- .../ba/sake/squery/oracle/OracleSuite.scala | 368 ++++++++++++++++++ .../sake/squery/postgres/PostgresSuite.scala | 19 +- 12 files changed, 459 insertions(+), 54 deletions(-) create mode 100644 squery/src/ba/sake/squery/oracle/reads.scala create mode 100644 squery/src/ba/sake/squery/oracle/writes.scala create mode 100644 squery/test/src/ba/sake/squery/oracle/OracleSuite.scala diff --git a/README.md b/README.md index d03644e..58bb333 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/build.sc b/build.sc index af8ba82..6f0600b 100644 --- a/build.sc +++ b/build.sc @@ -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" ) } } diff --git a/docs/src/files/tutorials/GettingStarted.scala b/docs/src/files/tutorials/GettingStarted.scala index 0e5c689..49d2072 100644 --- a/docs/src/files/tutorials/GettingStarted.scala +++ b/docs/src/files/tutorials/GettingStarted.scala @@ -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(..) diff --git a/squery/src/ba/sake/squery/oracle/reads.scala b/squery/src/ba/sake/squery/oracle/reads.scala new file mode 100644 index 0000000..04ec2d4 --- /dev/null +++ b/squery/src/ba/sake/squery/oracle/reads.scala @@ -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) +} diff --git a/squery/src/ba/sake/squery/oracle/writes.scala b/squery/src/ba/sake/squery/oracle/writes.scala new file mode 100644 index 0000000..a148cf1 --- /dev/null +++ b/squery/src/ba/sake/squery/oracle/writes.scala @@ -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) +} diff --git a/squery/src/ba/sake/squery/query.scala b/squery/src/ba/sake/squery/query.scala index ac0d4d7..52d7ade 100644 --- a/squery/src/ba/sake/squery/query.scala +++ b/squery/src/ba/sake/squery/query.scala @@ -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) { diff --git a/squery/src/ba/sake/squery/squery.scala b/squery/src/ba/sake/squery/squery.scala index 1bf9ae8..1afa9e3 100644 --- a/squery/src/ba/sake/squery/squery.scala +++ b/squery/src/ba/sake/squery/squery.scala @@ -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() @@ -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") ) diff --git a/squery/test/src/ba/sake/squery/dataTypes.scala b/squery/test/src/ba/sake/squery/dataTypes.scala index 3beca28..3abce90 100644 --- a/squery/test/src/ba/sake/squery/dataTypes.scala +++ b/squery/test/src/ba/sake/squery/dataTypes.scala @@ -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 diff --git a/squery/test/src/ba/sake/squery/mariadb/MariaDbSuite.scala b/squery/test/src/ba/sake/squery/mariadb/MariaDbSuite.scala index 6fc6f9d..025fa21 100644 --- a/squery/test/src/ba/sake/squery/mariadb/MariaDbSuite.scala +++ b/squery/test/src/ba/sake/squery/mariadb/MariaDbSuite.scala @@ -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() @@ -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() @@ -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)) @@ -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} " } } @@ -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} @@ -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 diff --git a/squery/test/src/ba/sake/squery/mysql/MySqlSuite.scala b/squery/test/src/ba/sake/squery/mysql/MySqlSuite.scala index cf28d62..8748428 100644 --- a/squery/test/src/ba/sake/squery/mysql/MySqlSuite.scala +++ b/squery/test/src/ba/sake/squery/mysql/MySqlSuite.scala @@ -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() @@ -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)) @@ -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} " } } @@ -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} @@ -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 diff --git a/squery/test/src/ba/sake/squery/oracle/OracleSuite.scala b/squery/test/src/ba/sake/squery/oracle/OracleSuite.scala new file mode 100644 index 0000000..9a4118e --- /dev/null +++ b/squery/test/src/ba/sake/squery/oracle/OracleSuite.scala @@ -0,0 +1,368 @@ +package ba.sake.squery +package oracle + +import java.util.UUID +import java.time.Instant +import java.time.temporal.ChronoUnit +import scala.collection.decorators._ +import org.testcontainers.oracle.OracleContainer + +// UUID, enum.. Postgres specific +case class Datatypes( + d_int: Option[Int], + d_long: Option[Long], + d_double: Option[Double], + d_boolean: Option[Boolean], + d_string: Option[String], + d_uuid: Option[UUID], + d_tstz: Option[Instant], + d_clr: Option[Color] +) derives SqlReadRow: + def insertTuple = sql"(${d_int}, ${d_long}, ${d_double}, ${d_boolean}, ${d_string}, ${d_uuid}, ${d_tstz}, ${d_clr})" + +object Datatypes: + inline val * = "d_int, d_long, d_double, d_boolean, d_string, d_uuid, d_tstz, d_clr" + +enum Color derives SqlRead, SqlWrite: + case red, green, blue + +class OracleSuite extends munit.FunSuite { + + var customer1 = Customer(1, "a_customer", None) + var customer2 = Customer(1, "b_customer", Some("str1")) + val customers = Seq(customer1, customer2) + + var phone1 = Phone(1, "061 123 456") + var phone2 = Phone(1, "062 225 883") + val phones = Seq(phone1, phone2) + + var address1 = Address(1, Some("a1")) + var address2 = Address(1, None) + val addresses = Seq(address1, address2) + + val initDb = new Fixture[SqueryContext]("database") { + private var ctx: SqueryContext = null + private var container: OracleContainer = null + + def apply() = ctx + + override def beforeAll(): Unit = { + container = OracleContainer("gvenzl/oracle-free:slim-faststart") + container.start() + + val ds = com.zaxxer.hikari.HikariDataSource() + ds.setJdbcUrl(container.getJdbcUrl()) + ds.setUsername(container.getUsername()) + ds.setPassword(container.getPassword()) + + ctx = SqueryContext(ds) + + ctx.run { + sql""" + CREATE TABLE customers( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(32) NOT NULL, + street VARCHAR(20) + ) + """.update() + + sql""" + CREATE TABLE phones( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + customer_id INTEGER REFERENCES customers(id), + numbr VARCHAR(32) + ) + """.update() + + sql""" + CREATE TABLE addresses( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(32) + ) + """.update() + + sql""" + CREATE TABLE customer_address( + customer_id INTEGER REFERENCES customers(id), + address_id INTEGER REFERENCES addresses(id), + PRIMARY KEY (customer_id, address_id) + ) + """.update() + + // TODO document + // oracle supports RETURNING, but only for single-row inserts.. + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/INSERT.html#GUID-903F8043-0254-4EE9-ACC1-CB8AC0AF3423 + + // plus, you have to give it the autogenerated column name.... + // https://stackoverflow.com/a/55178347/4496364 + + val customer1Id = sql""" + INSERT INTO customers(name, street) + VALUES ${customer1.insertTuple} + """.insertReturningGenKey[Int](Some("id")) + val customer2Id = sql""" + INSERT INTO customers(name, street) + VALUES ${customer2.insertTuple} + """.insertReturningGenKey[Int](Some("id")) + customer1 = customer1.copy(id = customer1Id) + customer2 = customer2.copy(id = customer2Id) + + val phoneId1 = sql""" + INSERT INTO phones(customer_id, numbr) + VALUES ${phone1.insertTuple(customer1.id)} + """.insertReturningGenKey[Int](Some("id")) + val phoneId2 = sql""" + INSERT INTO phones(customer_id, numbr) + VALUES ${phone2.insertTuple(customer1.id)} + """.insertReturningGenKey[Int](Some("id")) + phone1 = phone1.copy(id = phoneId1) + phone2 = phone2.copy(id = phoneId2) + + val addressId1 = sql""" + INSERT INTO addresses(name) + VALUES ${address1.insertTuple} + """.insertReturningGenKey[Int](Some("id")) + val addressId2 = sql""" + INSERT INTO addresses(name) + VALUES ${address2.insertTuple} + """.insertReturningGenKey[Int](Some("id")) + address1 = address1.copy(id = addressId1) + address2 = address2.copy(id = addressId2) + + sql""" + INSERT INTO customer_address(customer_id, address_id) + VALUES + (${customer1.id}, ${address1.id}), + (${customer1.id}, ${address2.id}) + """.insert() + } + } + override def afterAll(): Unit = + if container != null then container.close() + + } + + override def munitFixtures = List(initDb) + + /* TESTS */ + test("SELECT plain values") { + val ctx = initDb() + ctx.run { + assertEquals( + sql"SELECT name FROM customers".readValues[String](), + customers.map(_.name) + ) + + assertEquals( + sql"SELECT numbr FROM phones WHERE customer_id = ${customer1.id}" + .readValues[String](), + phones.map(_.numbr) + ) + } + } + + test("SELECT rows") { + val ctx = initDb() + ctx.run { + // full join + assertEquals( + sql""" + SELECT c.id, c.name, c.street, + p.id, p.numbr + FROM customers c + JOIN phones p ON p.customer_id = c.id + WHERE c.id = ${customer1.id} + """.readRows[CustomerWithPhone](), + Seq( + CustomerWithPhone(customer1, phone1), + CustomerWithPhone(customer1, phone2) + ) + ) + + // outer/optional join + assertEquals( + sql""" + SELECT c.id, c.name, c.street, + p.id, p.numbr + FROM customers c + LEFT JOIN phones p ON p.customer_id = c.id + """.readRows[CustomerWithPhoneOpt](), + Seq( + CustomerWithPhoneOpt(customer1, Some(phone1)), + CustomerWithPhoneOpt(customer1, Some(phone2)), + CustomerWithPhoneOpt(customer2, None) + ) + ) + + // outer/optional join with many-to-many + assertEquals( + sql""" + SELECT c.id, c.name, c.street, + a.id, a.name + FROM customers c + LEFT JOIN customer_address ca ON ca.customer_id = c.id + LEFT JOIN addresses a ON a.id = ca.address_id + """.readRows[CustomerWithAddressOpt](), + Seq( + CustomerWithAddressOpt(customer1, Some(address1)), + CustomerWithAddressOpt(customer1, Some(address2)), + CustomerWithAddressOpt(customer2, None) + ) + ) + + } + } + + test("BAD SELECT throws") { + val ctx = initDb() + intercept[SqueryException] { + // street is nullable, but CustomerBad says it's mandatory + ctx.run { + sql""" + SELECT id, name, street + FROM customers + """.insertReturningRows[CustomerBad]() + } + } + } + + test("INSERT returning generated keys") { + val ctx = initDb() + ctx.run { + val customerId = sql""" + INSERT INTO customers(name) + VALUES ('abc') + """.insertReturningGenKey[Int](Some("id")) + assertEquals( + customerId, + customer2.id + 1 + ) + } + } + + // TODO seems like this only works with PL/SQL ... + /* + test("INSERT returning columns") { + val ctx = initDb() + ctx.run { + val customer = sql""" + INSERT INTO customers(name) + VALUES ('bla') + RETURNING id, name, street + INTO id, name, street + """.insertReturningRow[Customer]() + assertEquals(customer.name, "bla") + } + } + */ + test("UPDATE should return number of affected rows") { + val ctx = initDb() + ctx.run { + sql""" + INSERT INTO customers(name) + VALUES ('xyz_1'), ('xyz_2'), ('b_1') + """.insert() + val affected = sql""" + UPDATE customers + SET name = 'whatever' + WHERE name LIKE 'xyz_%' + """.update() + assertEquals(affected, 2) + } + } + + test("Data types") { + val ctx = initDb() + ctx.run { + sql""" + CREATE TABLE datatypes( + d_int INTEGER, + d_long INTEGER, + d_double DOUBLE PRECISION, + d_boolean BOOLEAN, + d_string VARCHAR(255), + d_uuid VARCHAR2(36), + d_tstz TIMESTAMP WITH TIME ZONE, + d_clr VARCHAR(10) CHECK (d_clr IN ('red', 'green', 'blue')) + ) + """.update() + + val dt1 = Datatypes( + Some(123), + Some(Int.MaxValue + 100), + Some(0.54543), + Some(true), + Some("abc"), + Some(UUID.randomUUID), + Some(Instant.now.truncatedTo(ChronoUnit.MICROS)), + Some(Color.red) + ) + val dt2 = Datatypes(None, None, None, None, None, None, None, None) + + val values = Seq(dt1, dt2) + .map(_.insertTuple) + .intersperse(sql",") + .reduce(_ ++ _) + sql""" + INSERT INTO datatypes(${Datatypes.*}) + VALUES ${values} + """.insert() + + val storedRows = sql""" + SELECT ${Datatypes.*} + FROM datatypes + """.readRows[Datatypes]() + assertEquals( + storedRows, + Seq(dt1) + ) + } + } + + test("Transaction") { + val ctx = initDb() + // create table normally + ctx.run { + sql""" + CREATE TABLE test_transactions( + name VARCHAR(32), + UNIQUE(name) + ) + """.update() + } + // all succeeds, + // or nothing succeeds! + intercept[Exception] { + ctx.runTransaction { + sql""" + INSERT INTO test_transactions(name) + VALUES ('abc') + """.insert() + // fail coz unique name + sql""" + INSERT INTO test_transactions(name) + VALUES ('abc') + """.insert() + } + } + intercept[Exception] { + ctx.runTransactionWithIsolation(TransactionIsolation.Serializable) { + sql""" + INSERT INTO test_transactions(name) + VALUES ('abc') + """.insert() + // fail coz unique name + sql""" + INSERT INTO test_transactions(name) + VALUES ('abc') + """.insert() + } + } + // check there is NO ENTRIES, coz transaction failed + ctx.run { + val values = sql"SELECT name FROM test_transactions".readValues[String]() + assertEquals(values, Seq.empty) + } + } + +} diff --git a/squery/test/src/ba/sake/squery/postgres/PostgresSuite.scala b/squery/test/src/ba/sake/squery/postgres/PostgresSuite.scala index 3fac28d..afb0314 100644 --- a/squery/test/src/ba/sake/squery/postgres/PostgresSuite.scala +++ b/squery/test/src/ba/sake/squery/postgres/PostgresSuite.scala @@ -73,7 +73,7 @@ class PostgresSuite extends munit.FunSuite { CREATE TABLE phones( id SERIAL PRIMARY KEY, customer_id INTEGER REFERENCES customers(id), - number VARCHAR + numbr VARCHAR ) """.update() @@ -101,9 +101,9 @@ class PostgresSuite 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)) @@ -141,13 +141,10 @@ class PostgresSuite 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} " } } @@ -158,7 +155,7 @@ class PostgresSuite 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} @@ -173,7 +170,7 @@ class PostgresSuite 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 """.readRows[CustomerWithPhoneOpt](),