diff --git a/docs/reference.md b/docs/reference.md index 342647c1..6fdb8ee4 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -6276,7 +6276,7 @@ Buyer.select ## Schema Additional tests to ensure schema mapping produces valid SQL -### Schema.schema +### Schema.schema.select 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 @@ -6305,6 +6305,184 @@ Invoice.select +### Schema.schema.insert.columns + +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.insert.columns( + _.total := 200.3, + _.vendor_name := "Huawei" +) +``` + + +* + ```sql + INSERT INTO otherschema.invoice (total, vendor_name) VALUES (?, ?) + ``` + + + +* + ```scala + 1 + ``` + + + +### Schema.schema.insert.values + +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.insert + .values( + Invoice[Sc]( + id = 0, + total = 200.3, + vendor_name = "Huawei" + ) + ) + .skipColumns(_.id) +``` + + +* + ```sql + INSERT INTO otherschema.invoice (total, vendor_name) VALUES (?, ?) + ``` + + + +* + ```scala + 1 + ``` + + + +### Schema.schema.update + +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 + .update(_.id === 1) + .set( + _.total := 200.3, + _.vendor_name := "Huawei" + ) +``` + + +* + ```sql + UPDATE otherschema.invoice + SET + total = ?, + vendor_name = ? + WHERE + (invoice.id = ?) + ``` + + + +* + ```scala + 1 + ``` + + + +### Schema.schema.delete + +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.delete(_.id === 1) +``` + + +* + ```sql + DELETE FROM otherschema.invoice WHERE (invoice.id = ?) + ``` + + + +* + ```scala + 1 + ``` + + + +### Schema.schema.insert into + +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.insert.select( + i => (i.total, i.vendor_name), + Invoice.select.map(i => (i.total, i.vendor_name)) +) +``` + + +* + ```sql + INSERT INTO + otherschema.invoice (total, vendor_name) + SELECT + invoice0.total AS res_0, + invoice0.vendor_name AS res_1 + FROM + otherschema.invoice invoice0 + ``` + + + +* + ```scala + 4 + ``` + + + +### Schema.schema.join + +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.join(Invoice)(_.id `=` _.id).map(_._1.id) +``` + + +* + ```sql + SELECT + invoice0.id AS res + FROM + otherschema.invoice invoice0 + JOIN otherschema.invoice invoice1 ON (invoice0.id = invoice1.id) + ``` + + + +* + ```scala + Seq(2, 3, 4, 5, 6, 7, 8, 9) + ``` + + + ## 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 diff --git a/scalasql/query/src/Delete.scala b/scalasql/query/src/Delete.scala index 447bb09f..a118deb0 100644 --- a/scalasql/query/src/Delete.scala +++ b/scalasql/query/src/Delete.scala @@ -22,9 +22,9 @@ object Delete { } class Renderer(table: TableRef, expr: Expr[Boolean], prevContext: Context) { - lazy val tableNameStr = - SqlStr.raw(prevContext.config.tableNameMapper(Table.name(table.value))) implicit val implicitCtx: Context = Context.compute(prevContext, Nil, Some(table)) + lazy val tableNameStr = + SqlStr.raw(Table.resolve(table.value)) def render() = sql"DELETE FROM $tableNameStr WHERE $expr" } diff --git a/scalasql/query/src/From.scala b/scalasql/query/src/From.scala index 006b4486..7ed8e82a 100644 --- a/scalasql/query/src/From.scala +++ b/scalasql/query/src/From.scala @@ -15,11 +15,8 @@ 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) = { - val schemaStr = value.schemaName match { - case "" => "" - case str => s"$str." - } - SqlStr.raw(schemaStr + prevContext.config.tableNameMapper(Table.name(value))) + sql" " + name + val resolvedTable = Table.resolve(value)(prevContext) + SqlStr.raw(resolvedTable + sql" " + name) } } diff --git a/scalasql/query/src/InsertColumns.scala b/scalasql/query/src/InsertColumns.scala index 1b44e1f8..d166bda9 100644 --- a/scalasql/query/src/InsertColumns.scala +++ b/scalasql/query/src/InsertColumns.scala @@ -24,7 +24,7 @@ object InsertColumns { protected def expr: V[Column] = WithSqlExpr.get(insert) private[scalasql] override def renderSql(ctx: Context) = - new Renderer(columns, ctx, valuesLists, Table.name(table.value)).render() + new Renderer(columns, ctx, valuesLists, Table.resolve(table.value)(ctx)).render() override protected def queryConstruct(args: Queryable.ResultSetIterator): Int = args.get(IntType) @@ -48,7 +48,7 @@ object InsertColumns { SqlStr.commaSep ) def render() = { - sql"INSERT INTO ${SqlStr.raw(ctx.config.tableNameMapper(tableName))} ($columns) VALUES $values" + sql"INSERT INTO ${SqlStr.raw(tableName)} ($columns) VALUES $values" } } } diff --git a/scalasql/query/src/InsertSelect.scala b/scalasql/query/src/InsertSelect.scala index 341a25c9..65016846 100644 --- a/scalasql/query/src/InsertSelect.scala +++ b/scalasql/query/src/InsertSelect.scala @@ -20,7 +20,7 @@ object InsertSelect { def table = insert.table private[scalasql] override def renderSql(ctx: Context) = - new Renderer(select, select.qr.walkExprs(columns), ctx, Table.name(table.value)) + new Renderer(select, select.qr.walkExprs(columns), ctx, Table.resolve(table.value)(ctx)) .render() override protected def queryConstruct(args: Queryable.ResultSetIterator): Int = @@ -45,7 +45,7 @@ object InsertSelect { lazy val selectSql = Renderable.renderSql(select).withCompleteQuery(false) - lazy val tableNameStr = SqlStr.raw(ctx.config.tableNameMapper(tableName)) + lazy val tableNameStr = SqlStr.raw(tableName) def render() = sql"INSERT INTO $tableNameStr ($columns) $selectSql" } } diff --git a/scalasql/query/src/InsertValues.scala b/scalasql/query/src/InsertValues.scala index c949b64e..a2146527 100644 --- a/scalasql/query/src/InsertValues.scala +++ b/scalasql/query/src/InsertValues.scala @@ -22,7 +22,7 @@ object InsertValues { override private[scalasql] def renderSql(ctx: Context): SqlStr = { new Renderer( - Table.name(insert.table.value), + Table.resolve(insert.table.value)(ctx), Table.labels(insert.table.value), values, qr, @@ -75,7 +75,7 @@ object InsertValues { lazy val values = SqlStr.join(valuesSqls, SqlStr.commaSep) def render() = { - sql"INSERT INTO ${SqlStr.raw(ctx.config.tableNameMapper(tableName))} ($columns) VALUES $values" + sql"INSERT INTO ${SqlStr.raw(tableName)} ($columns) VALUES $values" } } } diff --git a/scalasql/query/src/Table.scala b/scalasql/query/src/Table.scala index 4dd811a0..6449d49e 100644 --- a/scalasql/query/src/Table.scala +++ b/scalasql/query/src/Table.scala @@ -1,6 +1,7 @@ package scalasql.query import scalasql.core.{DialectTypeMappers, Sc, Queryable, Expr} +import scalasql.core.Context /** * In-code representation of a SQL table, associated with a given `case class` [[V]]. @@ -49,6 +50,13 @@ object Table { def name(t: Table.Base) = t.tableName def labels(t: Table.Base) = t.tableLabels def columnNameOverride[V[_[_]]](t: Table.Base)(s: String) = t.tableColumnNameOverride(s) + def resolve(t: Table.Base)(implicit context: Context) = { + val mappedTableName = context.config.tableNameMapper(t.tableName) + t.schemaName match { + case "" => mappedTableName + case str => s"$str." + mappedTableName + } + } trait Base { /** diff --git a/scalasql/query/src/Update.scala b/scalasql/query/src/Update.scala index 29349256..eb088a95 100644 --- a/scalasql/query/src/Update.scala +++ b/scalasql/query/src/Update.scala @@ -94,7 +94,7 @@ object Update { implicit lazy val implicitCtx: Context = Context.compute(prevContext, froms, Some(table)) lazy val tableName = - SqlStr.raw(implicitCtx.config.tableNameMapper(Table.name(table.value))) + SqlStr.raw(Table.resolve(table.value)) lazy val updateList = set0.map { case assign => val kStr = SqlStr.raw(prevContext.config.columnNameMapper(assign.column.name)) diff --git a/scalasql/test/src/query/SchemaTests.scala b/scalasql/test/src/query/SchemaTests.scala index ff0f8000..7f7f910f 100644 --- a/scalasql/test/src/query/SchemaTests.scala +++ b/scalasql/test/src/query/SchemaTests.scala @@ -1,6 +1,7 @@ package scalasql.query import scalasql._ +import scalasql.core.JoinNullable import sourcecode.Text import utest._ import utils.ScalaSqlSuite @@ -11,23 +12,129 @@ 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 - """ - ) + test("schema") { + test("select") { + 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 + """ + ) + } + test("insert.columns") { + checker( + query = Invoice.insert.columns( + _.total := 200.3, + _.vendor_name := "Huawei" + ), + sql = "INSERT INTO otherschema.invoice (total, vendor_name) VALUES (?, ?)", + value = 1, + 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 + """ + ) + } + test("insert.values") { + checker( + query = Invoice.insert + .values( + Invoice[Sc]( + id = 0, + total = 200.3, + vendor_name = "Huawei" + ) + ) + .skipColumns(_.id), + sql = "INSERT INTO otherschema.invoice (total, vendor_name) VALUES (?, ?)", + value = 1, + 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 + """ + ) + } + test("update") { + checker( + query = Invoice + .update(_.id === 1) + .set( + _.total := 200.3, + _.vendor_name := "Huawei" + ), + sql = """UPDATE otherschema.invoice + SET + total = ?, + vendor_name = ? + WHERE + (invoice.id = ?)""", + value = 1, + 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 + """ + ) + } + test("delete") { + checker( + query = Invoice.delete(_.id === 1), + sql = "DELETE FROM otherschema.invoice WHERE (invoice.id = ?)", + value = 1, + 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 + """ + ) + } + test("insert into") { + checker( + query = Invoice.insert.select( + i => (i.total, i.vendor_name), + Invoice.select.map(i => (i.total, i.vendor_name)) + ), + sql = """INSERT INTO + otherschema.invoice (total, vendor_name) + SELECT + invoice0.total AS res_0, + invoice0.vendor_name AS res_1 + FROM + otherschema.invoice invoice0""", + value = 4, + 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 + """ + ) + } + test("join") { + checker( + query = Text { + Invoice.select.join(Invoice)(_.id `=` _.id).map(_._1.id) + }, + sql = """SELECT + invoice0.id AS res + FROM + otherschema.invoice invoice0 + JOIN otherschema.invoice invoice1 ON (invoice0.id = invoice1.id)""", + value = Seq(2, 3, 4, 5, 6, 7, 8, 9), + 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 + """ + ) + } + } } }