Skip to content

Commit 68f690d

Browse files
authored
Schema support (#23)
Quick fix for this issue #14 I got a macro error when I made `schemaName: Option[String]`, so to avoid that I used a `schemaName: String` with a default empty string. I was only concerned with getting the `FROM` use case to work, but I can certainly add more tests to try other cases, just let me know. Also let me know if there's another place that needs a change for this to work everywhere. No support for MySQL for now.
1 parent b8719f5 commit 68f690d

File tree

10 files changed

+141
-7
lines changed

10 files changed

+141
-7
lines changed

docs/reference.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6199,6 +6199,37 @@ Buyer.select
61996199
62006200
62016201
6202+
## Schema
6203+
Additional tests to ensure schema mapping produces valid SQL
6204+
### Schema.schema
6205+
6206+
If your table belongs to a schema other than the default schema of your database,
6207+
you can specify this in your table definition with table.schemaName
6208+
6209+
```scala
6210+
Invoice.select
6211+
```
6212+
6213+
6214+
*
6215+
```sql
6216+
SELECT invoice0.id AS id, invoice0.total AS total, invoice0.vendor_name AS vendor_name
6217+
FROM otherschema.invoice invoice0
6218+
```
6219+
6220+
6221+
6222+
*
6223+
```scala
6224+
Seq(
6225+
Invoice[Sc](id = 1, total = 150.4, vendor_name = "Siemens"),
6226+
Invoice[Sc](id = 2, total = 213.3, vendor_name = "Samsung"),
6227+
Invoice[Sc](id = 3, total = 407.2, vendor_name = "Shell")
6228+
)
6229+
```
6230+
6231+
6232+
62026233
## SubQuery
62036234
Queries that explicitly use subqueries (e.g. for `JOIN`s) or require subqueries to preserve the Scala semantics of the various operators
62046235
### SubQuery.sortTakeJoin

scalasql/query/src/From.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ class TableRef(val value: Table.Base) extends From {
1515
def fromExprAliases(prevContext: Context): Seq[(Expr.Identity, SqlStr)] = Nil
1616

1717
def renderSql(name: SqlStr, prevContext: Context, liveExprs: LiveExprs) = {
18-
SqlStr.raw(prevContext.config.tableNameMapper(Table.name(value))) + sql" " + name
18+
val schemaStr = value.schemaName match {
19+
case "" => ""
20+
case str => s"$str."
21+
}
22+
SqlStr.raw(schemaStr + prevContext.config.tableNameMapper(Table.name(value))) + sql" " + name
1923
}
2024
}
2125

scalasql/query/src/Table.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ abstract class Table[V[_[_]]]()(implicit name: sourcecode.Name, metadata0: Table
1111

1212
protected[scalasql] def tableName = name.value
1313

14+
protected[scalasql] def schemaName = ""
15+
1416
protected implicit def tableSelf: Table[V] = this
1517

1618
protected def tableMetadata: Table.Metadata[V] = metadata0
@@ -54,6 +56,7 @@ object Table {
5456
* Can be overriden to configure the table names
5557
*/
5658
protected[scalasql] def tableName: String
59+
protected[scalasql] def schemaName: String
5760
protected[scalasql] def tableLabels: Seq[String]
5861

5962
/**
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
INSERT INTO buyer (name, date_of_birth) VALUES
3+
('James Bond', '2001-02-03'),
4+
('叉烧包', '1923-11-12'),
5+
('Li Haoyi', '1965-08-09');
6+
7+
INSERT INTO product (kebab_case_name, name, price) VALUES
8+
('face-mask', 'Face Mask', 8.88),
9+
('guitar', 'Guitar', 300),
10+
('socks', 'Socks', 3.14),
11+
('skate-board', 'Skate Board', 123.45),
12+
('camera', 'Camera', 1000.00),
13+
('cookie', 'Cookie', 0.10);
14+
15+
INSERT INTO shipping_info (buyer_id, shipping_date) VALUES
16+
(2, '2010-02-03'),
17+
(1, '2012-04-05'),
18+
(2, '2012-05-06');
19+
20+
INSERT INTO purchase (shipping_info_id, product_id, count, total) VALUES
21+
(1, 1, 100, 888),
22+
(1, 2, 3, 900),
23+
(1, 3, 5, 15.7),
24+
(2, 4, 4, 493.8),
25+
(2, 5, 10, 10000.00),
26+
(3, 1, 5, 44.4),
27+
(3, 6, 13, 1.30);
28+
29+
INSERT INTO otherschema.invoice (total, vendor_name) VALUES
30+
(150.4, 'Siemens'),
31+
(213.3, 'Samsung'),
32+
(407.2, 'Shell');

scalasql/test/resources/h2-customer-schema.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ DROP TABLE IF EXISTS non_round_trip_types CASCADE;
77
DROP TABLE IF EXISTS opt_cols CASCADE;
88
DROP TABLE IF EXISTS nested CASCADE;
99
DROP TABLE IF EXISTS enclosing CASCADE;
10+
DROP TABLE IF EXISTS invoice CASCADE;
11+
DROP SCHEMA IF EXISTS otherschema CASCADE;
1012

1113
CREATE TABLE buyer (
1214
id INTEGER AUTO_INCREMENT PRIMARY KEY,
@@ -76,3 +78,11 @@ CREATE TABLE enclosing(
7678
foo_id INTEGER,
7779
my_boolean BOOLEAN
7880
);
81+
82+
CREATE SCHEMA otherschema;
83+
84+
CREATE TABLE otherschema.invoice(
85+
id INTEGER AUTO_INCREMENT PRIMARY KEY,
86+
total DECIMAL(20, 2),
87+
vendor_name VARCHAR(256)
88+
);

scalasql/test/resources/postgres-customer-schema.sql

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ DROP TABLE IF EXISTS non_round_trip_types CASCADE;
77
DROP TABLE IF EXISTS opt_cols CASCADE;
88
DROP TABLE IF EXISTS nested CASCADE;
99
DROP TABLE IF EXISTS enclosing CASCADE;
10+
DROP TABLE IF EXISTS invoice CASCADE;
1011
DROP TYPE IF EXISTS my_enum CASCADE;
12+
DROP SCHEMA IF EXISTS otherschema CASCADE;
1113

1214
CREATE TABLE buyer (
1315
id SERIAL PRIMARY KEY,
@@ -68,8 +70,6 @@ CREATE TABLE opt_cols(
6870
my_int2 INTEGER
6971
);
7072

71-
72-
7373
CREATE TABLE nested(
7474
foo_id INTEGER,
7575
my_boolean BOOLEAN
@@ -80,4 +80,12 @@ CREATE TABLE enclosing(
8080
my_string VARCHAR(256),
8181
foo_id INTEGER,
8282
my_boolean BOOLEAN
83+
);
84+
85+
CREATE SCHEMA otherschema;
86+
87+
CREATE TABLE otherschema.invoice(
88+
id SERIAL PRIMARY KEY,
89+
total DECIMAL(20, 2),
90+
vendor_name VARCHAR(256)
8391
);

scalasql/test/src/ConcreteTestSuites.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import query.{
2828
LateralJoinTests,
2929
WindowFunctionTests,
3030
GetGeneratedKeysTests,
31-
WithCteTests
31+
WithCteTests,
32+
SchemaTests
3233
}
3334
import scalasql.dialects.{
3435
MySqlDialectTests,
@@ -59,6 +60,7 @@ package postgres {
5960
object LateralJoinTests extends LateralJoinTests with PostgresSuite
6061
object WindowFunctionTests extends WindowFunctionTests with PostgresSuite
6162
object GetGeneratedKeysTests extends GetGeneratedKeysTests with PostgresSuite
63+
object SchemaTests extends SchemaTests with PostgresSuite
6264

6365
object SubQueryTests extends SubQueryTests with PostgresSuite
6466
object WithCteTests extends WithCteTests with PostgresSuite
@@ -103,6 +105,7 @@ package hikari {
103105
object LateralJoinTests extends LateralJoinTests with HikariSuite
104106
object WindowFunctionTests extends WindowFunctionTests with HikariSuite
105107
object GetGeneratedKeysTests extends GetGeneratedKeysTests with HikariSuite
108+
object SchemaTests extends SchemaTests with HikariSuite
106109

107110
object SubQueryTests extends SubQueryTests with HikariSuite
108111
object WithCteTests extends WithCteTests with HikariSuite
@@ -163,6 +166,8 @@ package mysql {
163166
object ExprStringOpsTests extends ExprStringOpsTests with MySqlSuite
164167
object ExprBlobOpsTests extends ExprBlobOpsTests with MySqlSuite
165168
object ExprMathOpsTests extends ExprMathOpsTests with MySqlSuite
169+
// In MySql, schemas are databases and this requires special treatment not yet implemented here
170+
// object SchemaTests extends SchemaTests with MySqlSuite
166171

167172
object DataTypesTests extends datatypes.DataTypesTests with MySqlSuite
168173
object OptionalTests extends datatypes.OptionalTests with MySqlSuite
@@ -208,6 +213,8 @@ package sqlite {
208213
object ExprBlobOpsTests extends ExprBlobOpsTests with SqliteSuite
209214
// Sqlite doesn't support all these math operations
210215
// object ExprMathOpsTests extends ExprMathOpsTests with SqliteSuite
216+
// Sqlite doesn't support schemas
217+
// object SchemaTests extends SchemaTests with SqliteSuite
211218

212219
object DataTypesTests extends datatypes.DataTypesTests with SqliteSuite
213220
object OptionalTests extends datatypes.OptionalTests with SqliteSuite
@@ -240,6 +247,7 @@ package h2 {
240247
// object LateralJoinTests extends LateralJoinTests with H2Suite
241248
object WindowFunctionTests extends WindowFunctionTests with H2Suite
242249
object GetGeneratedKeysTests extends GetGeneratedKeysTests with H2Suite
250+
object SchemaTests extends SchemaTests with H2Suite
243251

244252
object SubQueryTests extends SubQueryTests with H2Suite
245253
object WithCteTests extends WithCteTests with H2Suite

scalasql/test/src/UnitTestData.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ object Product extends Table[Product]
99
case class Buyer[T[_]](id: T[Int], name: T[String], dateOfBirth: T[LocalDate])
1010
object Buyer extends Table[Buyer]
1111

12+
case class Invoice[T[_]](id: T[Int], total: T[Double], vendor_name: T[String])
13+
object Invoice extends Table[Invoice] {
14+
override def schemaName = "otherschema"
15+
}
16+
1217
case class ShippingInfo[T[_]](id: T[Int], buyerId: T[Int], shippingDate: T[LocalDate])
1318
object ShippingInfo extends Table[ShippingInfo]
1419

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package scalasql.query
2+
3+
import scalasql._
4+
import sourcecode.Text
5+
import utest._
6+
import utils.ScalaSqlSuite
7+
8+
import java.time.LocalDate
9+
10+
trait SchemaTests extends ScalaSqlSuite {
11+
def description = "Additional tests to ensure schema mapping produces valid SQL"
12+
13+
def tests = Tests {
14+
test("schema") - checker(
15+
query = Text {
16+
Invoice.select
17+
},
18+
sql = """
19+
SELECT invoice0.id AS id, invoice0.total AS total, invoice0.vendor_name AS vendor_name
20+
FROM otherschema.invoice invoice0
21+
""",
22+
value = Seq(
23+
Invoice[Sc](id = 1, total = 150.4, vendor_name = "Siemens"),
24+
Invoice[Sc](id = 2, total = 213.3, vendor_name = "Samsung"),
25+
Invoice[Sc](id = 3, total = 407.2, vendor_name = "Shell")
26+
),
27+
docs = """
28+
If your table belongs to a schema other than the default schema of your database,
29+
you can specify this in your table definition with table.schemaName
30+
"""
31+
)
32+
}
33+
}

scalasql/test/src/utils/ScalaSqlSuite.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ trait H2Suite extends ScalaSqlSuite with H2Dialect {
3131
val checker = new TestChecker(
3232
scalasql.example.H2Example.h2Client,
3333
"h2-customer-schema.sql",
34-
"customer-data.sql",
34+
"customer-data-plus-schema.sql",
3535
getClass.getName,
3636
suiteLine.value,
3737
description
@@ -44,7 +44,7 @@ trait PostgresSuite extends ScalaSqlSuite with PostgresDialect {
4444
val checker = new TestChecker(
4545
scalasql.example.PostgresExample.postgresClient,
4646
"postgres-customer-schema.sql",
47-
"customer-data.sql",
47+
"customer-data-plus-schema.sql",
4848
getClass.getName,
4949
suiteLine.value,
5050
description
@@ -57,7 +57,7 @@ trait HikariSuite extends ScalaSqlSuite with PostgresDialect {
5757
val checker = new TestChecker(
5858
scalasql.example.HikariCpExample.hikariClient,
5959
"postgres-customer-schema.sql",
60-
"customer-data.sql",
60+
"customer-data-plus-schema.sql",
6161
getClass.getName,
6262
suiteLine.value,
6363
description

0 commit comments

Comments
 (0)