Permalink
Browse files

Support schema names for tables in all statements except INSERTs.

The InsertBuilder API needs to change in a binary-incompatible way so we
cannot do this last bit in 1.0.x. The new quoteTableName method belongs
into BasicSQLUtilsComponent next to quoteIdentifier. It is placed as a
private method into BasicStatementBuilderComponent to ensure binary
compatibility with Slick 1.0.0. There should be a TableNode.schemaName
method but we cannot add that, either, without breaking binary
compatibility, so we pattern-match in quoteTableName instead. This needs
to be cleaned up after merging 1.0 back into master.

Test case in SchemaSupportTest. Ideally, all TestKit tests would use
schema names, but that will require some major work on TestKit.

Fixes issue #19.
  • Loading branch information...
szeiger committed Apr 10, 2013
1 parent e9d48e9 commit 5c8b9bfc3ac6b872dfd069d98dec2018f8b53b13
@@ -0,0 +1,40 @@
package scala.slick.test.lifted
import org.junit.Test
import org.junit.Assert._
/** Test case for the SQL schema support in table definitions */
class SchemaSupportTest {
@Test def testSchemaSupport {
import scala.slick.driver.H2Driver.simple._
object T extends Table[Int](Some("myschema"), "mytable") {
def id = column[Int]("id")
def * = id
}
val s1 = Query(T).filter(_.id < 5).selectStatement
println(s1)
assertTrue("select ... from uses schema name", s1 contains """from "myschema"."mytable" """)
//val s2 = T.insertStatement
//println(s2)
val s3 = Query(T).filter(_.id < 5).updateStatement
println(s3)
assertTrue("update uses schema name", s3 contains """update "myschema"."mytable" """)
val s4 = Query(T).filter(_.id < 5).deleteStatement
println(s4)
assertTrue("delete uses schema name", s4 contains """delete from "myschema"."mytable" """)
val s5 = T.ddl.createStatements
s5.foreach(println)
s5.foreach(s => assertTrue("DDL (create) uses schema name", s contains """ "myschema"."mytable" """))
val s6 = T.ddl.dropStatements
s6.foreach(println)
s6.foreach(s => assertTrue("DDL (drop) uses schema name", s contains """ "myschema"."mytable" """))
}
}
@@ -393,6 +393,7 @@ object Path {
abstract class TableNode extends Node {
def nodeShaped_* : ShapedValue[_, _]
def tableName: String
//def schemaName: Option[String]
def nodeTableSymbol: TableSymbol = TableSymbol(tableName)
override def toString = "Table " + tableName
}
@@ -162,8 +162,8 @@ trait BasicStatementBuilderComponent { driver: BasicDriver =>
protected def buildFrom(n: Node, alias: Option[Symbol], skipParens: Boolean = false): Unit = building(FromPart) {
def addAlias = alias foreach { s => b += ' ' += symbolName(s) }
n match {
case t @ TableNode(name) =>
b += quoteIdentifier(name)
case t: TableNode =>
b += quoteTableName(t)
if(alias != Some(t.nodeTableSymbol)) addAlias
case j @ Join(leftGen, rightGen, left, right, jt, on) =>
buildFrom(left, Some(leftGen))
@@ -308,7 +308,7 @@ trait BasicStatementBuilderComponent { driver: BasicDriver =>
case o => throw new SlickException("A query for an UPDATE statement must resolve to a comprehension with a single table -- Unsupported shape: "+o)
}
val qtn = quoteIdentifier(from.tableName)
val qtn = quoteTableName(from)
symbolName(gen) = qtn // Alias table to itself because UPDATE does not support aliases
b"update $qtn set "
b.sep(select, ", ")(field => b += symbolName(field) += " = ?")
@@ -324,7 +324,7 @@ trait BasicStatementBuilderComponent { driver: BasicDriver =>
case Comprehension(Seq((sym, from: TableNode)), where, _, _, Some(Pure(select)), None, None) => (sym, from, where)
case o => throw new SlickException("A query for a DELETE statement must resolve to a comprehension with a single table -- Unsupported shape: "+o)
}
val qtn = quoteIdentifier(from.tableName)
val qtn = quoteTableName(from)
symbolName(gen) = qtn // Alias table to itself because DELETE does not support aliases
b"delete from $qtn"
if(!where.isEmpty) {
@@ -487,7 +487,7 @@ trait BasicStatementBuilderComponent { driver: BasicDriver =>
protected def dropPhase2 = primaryKeys.map(dropPrimaryKey) ++ Iterable(dropTable)
protected def createTable: String = {
val b = new StringBuilder append "create table " append quoteIdentifier(table.tableName) append " ("
val b = new StringBuilder append "create table " append quoteTableName(table) append " ("
var first = true
for(c <- columns) {
if(first) first = false else b append ","
@@ -500,34 +500,34 @@ trait BasicStatementBuilderComponent { driver: BasicDriver =>
protected def addTableOptions(b: StringBuilder) {}
protected def dropTable: String = "drop table "+quoteIdentifier(table.tableName)
protected def dropTable: String = "drop table "+quoteTableName(table)
protected def createIndex(idx: Index): String = {
val b = new StringBuilder append "create "
if(idx.unique) b append "unique "
b append "index " append quoteIdentifier(idx.name) append " on " append quoteIdentifier(table.tableName) append " ("
b append "index " append quoteIdentifier(idx.name) append " on " append quoteTableName(table) append " ("
addIndexColumnList(idx.on, b, idx.table.tableName)
b append ")"
b.toString
}
protected def createForeignKey(fk: ForeignKey[_ <: TableNode, _]): String = {
val sb = new StringBuilder append "alter table " append quoteIdentifier(table.tableName) append " add "
val sb = new StringBuilder append "alter table " append quoteTableName(table) append " add "
addForeignKey(fk, sb)
sb.toString
}
protected def addForeignKey(fk: ForeignKey[_ <: TableNode, _], sb: StringBuilder) {
sb append "constraint " append quoteIdentifier(fk.name) append " foreign key("
addForeignKeyColumnList(fk.linearizedSourceColumns, sb, table.tableName)
sb append ") references " append quoteIdentifier(fk.targetTable.tableName) append "("
sb append ") references " append quoteTableName(fk.targetTable) append "("
addForeignKeyColumnList(fk.linearizedTargetColumnsForOriginalTargetTable, sb, fk.targetTable.tableName)
sb append ") on update " append fk.onUpdate.action
sb append " on delete " append fk.onDelete.action
}
protected def createPrimaryKey(pk: PrimaryKey): String = {
val sb = new StringBuilder append "alter table " append quoteIdentifier(table.tableName) append " add "
val sb = new StringBuilder append "alter table " append quoteTableName(table) append " add "
addPrimaryKey(pk, sb)
sb.toString
}
@@ -539,10 +539,10 @@ trait BasicStatementBuilderComponent { driver: BasicDriver =>
}
protected def dropForeignKey(fk: ForeignKey[_ <: TableNode, _]): String =
"alter table " + quoteIdentifier(table.tableName) + " drop constraint " + quoteIdentifier(fk.name)
"alter table " + quoteTableName(table) + " drop constraint " + quoteIdentifier(fk.name)
protected def dropPrimaryKey(pk: PrimaryKey): String =
"alter table " + quoteIdentifier(table.tableName) + " drop constraint " + quoteIdentifier(pk.name)
"alter table " + quoteTableName(table) + " drop constraint " + quoteIdentifier(pk.name)
protected def addIndexColumnList(columns: IndexedSeq[Node], sb: StringBuilder, requiredTableName: String) =
addColumnList(columns, sb, requiredTableName, "index")
@@ -618,6 +618,17 @@ trait BasicStatementBuilderComponent { driver: BasicDriver =>
DDL(b.toString, "drop sequence " + quoteIdentifier(seq.name))
}
}
private final def quoteTableName(t: TableNode): String = {
t match {
case a: AbstractTable[_] =>
a.schemaName match {
case Some(s) => quoteIdentifier(s) + "." + quoteIdentifier(t.tableName)
case None => quoteIdentifier(t.tableName)
}
case t => quoteIdentifier(t.tableName)
}
}
}
case class QueryBuilderInput(state: CompilationState, linearizer: ValueLinearizer[_]) {

0 comments on commit 5c8b9bf

Please sign in to comment.