Skip to content

Schema support #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -6199,6 +6199,37 @@ Buyer.select



## Schema
Additional tests to ensure schema mapping produces valid SQL
### Schema.schema

If your table belongs to a schema other than the default schema of your database,
you can specify this in your table definition with table.schemaName

```scala
Invoice.select
```


*
```sql
SELECT invoice0.id AS id, invoice0.total AS total, invoice0.vendor_name AS vendor_name
FROM otherschema.invoice invoice0
```



*
```scala
Seq(
Invoice[Sc](id = 1, total = 150.4, vendor_name = "Siemens"),
Invoice[Sc](id = 2, total = 213.3, vendor_name = "Samsung"),
Invoice[Sc](id = 3, total = 407.2, vendor_name = "Shell")
)
```



## SubQuery
Queries that explicitly use subqueries (e.g. for `JOIN`s) or require subqueries to preserve the Scala semantics of the various operators
### SubQuery.sortTakeJoin
Expand Down
6 changes: 5 additions & 1 deletion scalasql/query/src/From.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ class TableRef(val value: Table.Base) extends From {
def fromExprAliases(prevContext: Context): Seq[(Expr.Identity, SqlStr)] = Nil

def renderSql(name: SqlStr, prevContext: Context, liveExprs: LiveExprs) = {
SqlStr.raw(prevContext.config.tableNameMapper(Table.name(value))) + sql" " + name
val schemaStr = value.schemaName match {
case "" => ""
case str => s"$str."
}
SqlStr.raw(schemaStr + prevContext.config.tableNameMapper(Table.name(value))) + sql" " + name
}
}

Expand Down
3 changes: 3 additions & 0 deletions scalasql/query/src/Table.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ abstract class Table[V[_[_]]]()(implicit name: sourcecode.Name, metadata0: Table

protected[scalasql] def tableName = name.value

protected[scalasql] def schemaName = ""

protected implicit def tableSelf: Table[V] = this

protected def tableMetadata: Table.Metadata[V] = metadata0
Expand Down Expand Up @@ -54,6 +56,7 @@ object Table {
* Can be overriden to configure the table names
*/
protected[scalasql] def tableName: String
protected[scalasql] def schemaName: String
protected[scalasql] def tableLabels: Seq[String]

/**
Expand Down
32 changes: 32 additions & 0 deletions scalasql/test/resources/customer-data-plus-schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

INSERT INTO buyer (name, date_of_birth) VALUES
('James Bond', '2001-02-03'),
('叉烧包', '1923-11-12'),
('Li Haoyi', '1965-08-09');

INSERT INTO product (kebab_case_name, name, price) VALUES
('face-mask', 'Face Mask', 8.88),
('guitar', 'Guitar', 300),
('socks', 'Socks', 3.14),
('skate-board', 'Skate Board', 123.45),
('camera', 'Camera', 1000.00),
('cookie', 'Cookie', 0.10);

INSERT INTO shipping_info (buyer_id, shipping_date) VALUES
(2, '2010-02-03'),
(1, '2012-04-05'),
(2, '2012-05-06');

INSERT INTO purchase (shipping_info_id, product_id, count, total) VALUES
(1, 1, 100, 888),
(1, 2, 3, 900),
(1, 3, 5, 15.7),
(2, 4, 4, 493.8),
(2, 5, 10, 10000.00),
(3, 1, 5, 44.4),
(3, 6, 13, 1.30);

INSERT INTO otherschema.invoice (total, vendor_name) VALUES
(150.4, 'Siemens'),
(213.3, 'Samsung'),
(407.2, 'Shell');
10 changes: 10 additions & 0 deletions scalasql/test/resources/h2-customer-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ DROP TABLE IF EXISTS non_round_trip_types CASCADE;
DROP TABLE IF EXISTS opt_cols CASCADE;
DROP TABLE IF EXISTS nested CASCADE;
DROP TABLE IF EXISTS enclosing CASCADE;
DROP TABLE IF EXISTS invoice CASCADE;
DROP SCHEMA IF EXISTS otherschema CASCADE;

CREATE TABLE buyer (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
Expand Down Expand Up @@ -76,3 +78,11 @@ CREATE TABLE enclosing(
foo_id INTEGER,
my_boolean BOOLEAN
);

CREATE SCHEMA otherschema;

CREATE TABLE otherschema.invoice(
id INTEGER AUTO_INCREMENT PRIMARY KEY,
total DECIMAL(20, 2),
vendor_name VARCHAR(256)
);
12 changes: 10 additions & 2 deletions scalasql/test/resources/postgres-customer-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ DROP TABLE IF EXISTS non_round_trip_types CASCADE;
DROP TABLE IF EXISTS opt_cols CASCADE;
DROP TABLE IF EXISTS nested CASCADE;
DROP TABLE IF EXISTS enclosing CASCADE;
DROP TABLE IF EXISTS invoice CASCADE;
DROP TYPE IF EXISTS my_enum CASCADE;
DROP SCHEMA IF EXISTS otherschema CASCADE;

CREATE TABLE buyer (
id SERIAL PRIMARY KEY,
Expand Down Expand Up @@ -68,8 +70,6 @@ CREATE TABLE opt_cols(
my_int2 INTEGER
);



CREATE TABLE nested(
foo_id INTEGER,
my_boolean BOOLEAN
Expand All @@ -80,4 +80,12 @@ CREATE TABLE enclosing(
my_string VARCHAR(256),
foo_id INTEGER,
my_boolean BOOLEAN
);

CREATE SCHEMA otherschema;

CREATE TABLE otherschema.invoice(
id SERIAL PRIMARY KEY,
total DECIMAL(20, 2),
vendor_name VARCHAR(256)
);
10 changes: 9 additions & 1 deletion scalasql/test/src/ConcreteTestSuites.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import query.{
LateralJoinTests,
WindowFunctionTests,
GetGeneratedKeysTests,
WithCteTests
WithCteTests,
SchemaTests
}
import scalasql.dialects.{
MySqlDialectTests,
Expand Down Expand Up @@ -59,6 +60,7 @@ package postgres {
object LateralJoinTests extends LateralJoinTests with PostgresSuite
object WindowFunctionTests extends WindowFunctionTests with PostgresSuite
object GetGeneratedKeysTests extends GetGeneratedKeysTests with PostgresSuite
object SchemaTests extends SchemaTests with PostgresSuite

object SubQueryTests extends SubQueryTests with PostgresSuite
object WithCteTests extends WithCteTests with PostgresSuite
Expand Down Expand Up @@ -103,6 +105,7 @@ package hikari {
object LateralJoinTests extends LateralJoinTests with HikariSuite
object WindowFunctionTests extends WindowFunctionTests with HikariSuite
object GetGeneratedKeysTests extends GetGeneratedKeysTests with HikariSuite
object SchemaTests extends SchemaTests with HikariSuite

object SubQueryTests extends SubQueryTests with HikariSuite
object WithCteTests extends WithCteTests with HikariSuite
Expand Down Expand Up @@ -163,6 +166,8 @@ package mysql {
object ExprStringOpsTests extends ExprStringOpsTests with MySqlSuite
object ExprBlobOpsTests extends ExprBlobOpsTests with MySqlSuite
object ExprMathOpsTests extends ExprMathOpsTests with MySqlSuite
// In MySql, schemas are databases and this requires special treatment not yet implemented here
// object SchemaTests extends SchemaTests with MySqlSuite

object DataTypesTests extends datatypes.DataTypesTests with MySqlSuite
object OptionalTests extends datatypes.OptionalTests with MySqlSuite
Expand Down Expand Up @@ -208,6 +213,8 @@ package sqlite {
object ExprBlobOpsTests extends ExprBlobOpsTests with SqliteSuite
// Sqlite doesn't support all these math operations
// object ExprMathOpsTests extends ExprMathOpsTests with SqliteSuite
// Sqlite doesn't support schemas
// object SchemaTests extends SchemaTests with SqliteSuite

object DataTypesTests extends datatypes.DataTypesTests with SqliteSuite
object OptionalTests extends datatypes.OptionalTests with SqliteSuite
Expand Down Expand Up @@ -240,6 +247,7 @@ package h2 {
// object LateralJoinTests extends LateralJoinTests with H2Suite
object WindowFunctionTests extends WindowFunctionTests with H2Suite
object GetGeneratedKeysTests extends GetGeneratedKeysTests with H2Suite
object SchemaTests extends SchemaTests with H2Suite

object SubQueryTests extends SubQueryTests with H2Suite
object WithCteTests extends WithCteTests with H2Suite
Expand Down
5 changes: 5 additions & 0 deletions scalasql/test/src/UnitTestData.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ object Product extends Table[Product]
case class Buyer[T[_]](id: T[Int], name: T[String], dateOfBirth: T[LocalDate])
object Buyer extends Table[Buyer]

case class Invoice[T[_]](id: T[Int], total: T[Double], vendor_name: T[String])
object Invoice extends Table[Invoice] {
override def schemaName = "otherschema"
}

case class ShippingInfo[T[_]](id: T[Int], buyerId: T[Int], shippingDate: T[LocalDate])
object ShippingInfo extends Table[ShippingInfo]

Expand Down
33 changes: 33 additions & 0 deletions scalasql/test/src/query/SchemaTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package scalasql.query

import scalasql._
import sourcecode.Text
import utest._
import utils.ScalaSqlSuite

import java.time.LocalDate

trait SchemaTests extends ScalaSqlSuite {
def description = "Additional tests to ensure schema mapping produces valid SQL"

def tests = Tests {
test("schema") - checker(
query = Text {
Invoice.select
},
sql = """
SELECT invoice0.id AS id, invoice0.total AS total, invoice0.vendor_name AS vendor_name
FROM otherschema.invoice invoice0
""",
value = Seq(
Invoice[Sc](id = 1, total = 150.4, vendor_name = "Siemens"),
Invoice[Sc](id = 2, total = 213.3, vendor_name = "Samsung"),
Invoice[Sc](id = 3, total = 407.2, vendor_name = "Shell")
),
docs = """
If your table belongs to a schema other than the default schema of your database,
you can specify this in your table definition with table.schemaName
"""
)
}
}
6 changes: 3 additions & 3 deletions scalasql/test/src/utils/ScalaSqlSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ trait H2Suite extends ScalaSqlSuite with H2Dialect {
val checker = new TestChecker(
scalasql.example.H2Example.h2Client,
"h2-customer-schema.sql",
"customer-data.sql",
"customer-data-plus-schema.sql",
getClass.getName,
suiteLine.value,
description
Expand All @@ -44,7 +44,7 @@ trait PostgresSuite extends ScalaSqlSuite with PostgresDialect {
val checker = new TestChecker(
scalasql.example.PostgresExample.postgresClient,
"postgres-customer-schema.sql",
"customer-data.sql",
"customer-data-plus-schema.sql",
getClass.getName,
suiteLine.value,
description
Expand All @@ -57,7 +57,7 @@ trait HikariSuite extends ScalaSqlSuite with PostgresDialect {
val checker = new TestChecker(
scalasql.example.HikariCpExample.hikariClient,
"postgres-customer-schema.sql",
"customer-data.sql",
"customer-data-plus-schema.sql",
getClass.getName,
suiteLine.value,
description
Expand Down
Loading