|
package ru.circumflex.orm
import java.util.Date
|
Dialect
This little thingy does all dirty SQL rendering.
We are orienting the default dialect on the world's most advanced open-source database, PostgreSQL.
If you feel that some of the statements do not work with your RDBMS vendor, trace the exact method and provide it's implementation in your own class. After that, set the orm.dialect configuration parameter accordingly.
|
class Dialect {
|
SQL types
|
def longType = "BIGINT"
def integerType = "INTEGER"
def numericType(precision: Int, scale: Int): String =
"NUMERIC" + (if (precision == -1) "" else "(" + precision + "," + scale + ")")
def textType = "TEXT"
def varcharType(length: Int): String =
"VARCHAR" + (if (length == -1) "" else "(" + length + ")")
def booleanType = "BOOLEAN"
def dateType = "DATE"
def timeType = "TIME"
def timestampType = "TIMESTAMP"
def xmlType = "TEXT"
|
Actions for Foreign Keys
|
def fkNoAction = "NO ACTION"
def fkCascade = "CASCADE"
def fkRestrict = "RESTRICT"
def fkSetNull = "SET NULL"
def fkSetDefault = "SET DEFAULT"
|
Join Keywords
|
def innerJoin = "INNER JOIN"
def leftJoin = "LEFT JOIN"
def rightJoin = "RIGHT JOIN"
def fullJoin = "FULL JOIN"
|
Predicates
|
def EQ = "= ?"
def NE = "<> ?"
def GT = "> ?"
def GE = ">= ?"
def LT = "< ?"
def LE = "<= ?"
def emptyPredicate = "1 = 1"
def isNull = "IS NULL"
def isNotNull = "IS NOT NULL"
def like = "LIKE ?"
def ilike = "ILIKE ?"
def between = "BETWEEN ? AND ?"
def in = "IN"
def notIn = "NOT IN"
def parameterizedIn(params: Seq[_]) =
"IN (" + params.map(p => "?").mkString(", ") + ")"
def and = "AND"
def or = "OR"
def not = "NOT"
def all = "ALL"
def some = "SOME"
def exists = "EXISTS"
def notExists = "NOT EXISTS"
|
Functions and others
|
def NULL = "NULL"
def distinct = "DISTINCT"
def count = "COUNT"
def max = "MAX"
def min = "MIN"
def sum = "SUM"
def avg = "AVG"
|
Set operations
|
def union = "UNION"
def unionAll = "UNION ALL"
def except = "EXCEPT"
def exceptAll = "EXCEPT ALL"
def intersect = "INTERSECT"
def intersectAll = "INTERSECT ALL"
|
Order specificators
|
def asc = "ASC"
def desc = "DESC"
|
Features Compliance
|
def supportsSchema_?(): Boolean = true
def supportsDropConstraints_?(): Boolean = true
|
Commons
|
def quoteLiteral(expr: String) = "'" + expr.replace("'", "''") + "'"
def quoteIdentifier(identifier: String) = identifier
def escapeParameter(value: Any): String = value match {
case Some(v) => escapeParameter(v)
case None | null => "NULL"
case v => quoteLiteral(v.toString)
}
def relationQualifiedName(relation: Relation[_, _]) =
quoteIdentifier(relation.schema.name) + "." + quoteIdentifier(relation.relationName)
def alias(expression: String, alias: String) =
expression + " AS " + quoteIdentifier(alias)
def qualifyColumn(field: Field[_, _], tableAlias: String) =
tableAlias + "." + quoteIdentifier(field.name)
def on(expression: String) = "ON (" + expression + ")"
def not(expression: String) = "NOT (" + expression + ")"
def subquery(expression: String, subquery: SQLQuery[_]) =
expression + " ( " + subquery.toSql + " )"
|
Data Definition Language
|
def constraintDefinition(constraint: Constraint) =
"CONSTRAINT " + quoteIdentifier(constraint.constraintName) + " " + constraint.sqlDefinition
def alterTable(rel: Relation[_, _], action: String) =
"ALTER TABLE " + rel.qualifiedName + " " + action
def alterTableAddConstraint(constraint: Constraint) =
alterTable(constraint.relation, "ADD " + constraintDefinition(constraint));
def alterTableDropConstraint(constraint: Constraint) =
alterTable(constraint.relation, "DROP CONSTRAINT " + quoteIdentifier(constraint.constraintName));
def createSchema(schema: Schema) = "CREATE SCHEMA " + quoteIdentifier(schema.name)
def dropSchema(schema: Schema) = "DROP SCHEMA " + quoteIdentifier(schema.name) + " CASCADE"
def createTable[PK, R <: Record[PK, R]](table: Table[PK, R]) =
"CREATE TABLE " + table.qualifiedName + " (" +
table.fields.map(_.toSql).mkString(", ") +
", PRIMARY KEY (" + quoteIdentifier(table.PRIMARY_KEY.name) + "))"
def dropTable[PK, R <: Record[PK, R]](table: Table[PK, R]) =
"DROP TABLE " + table.qualifiedName
def createView[PK, R <: Record[PK, R]](view: View[PK, R]) =
"CREATE VIEW " + view.qualifiedName + " (" +
view.fields.map(f => quoteIdentifier(f.name)).mkString(", ") + ") AS " +
view.query.toInlineSql
def dropView[PK, R <: Record[PK, R]](view: View[PK, R]) =
"DROP VIEW " + quoteIdentifier(view.qualifiedName)
def createIndex(idx: Index): String = {
var result = "CREATE "
if (idx.unique_?) result += "UNIQUE "
result += "INDEX " + quoteIdentifier(idx.name) + " ON " + idx.relation.qualifiedName +
" USING " + idx.using + " (" + idx.expression + ")"
if (idx.where != EmptyPredicate)
result += " WHERE " + idx.where.toInlineSql
return result
}
def dropIndex(idx: Index) =
"DROP INDEX " + quoteIdentifier(idx.relation.schema.name) + "." + quoteIdentifier(idx.name)
def columnDefinition[R <: Record[_, R]](field: Field[_, R]): String = {
var result = field.name + " " + field.sqlType
if (field.notNull_?) result += " NOT NULL"
result += defaultExpression(field)
return result
}
def initializeRelation[R <: Record[_, R]](relation: Relation[_, R]): Unit = {}
def initializeField[R <: Record[_, R]](field: Field[_, R]): Unit = field match {
case f: AutoIncrementable[_, _]
if (f.autoIncrement_? && !field.record.relation.isInstanceOf[View[_, R]]) => {
val seqName = sequenceName(f)
val seq = new SchemaObject {
val objectName = "SEQUENCE " + seqName
val sqlDrop = "DROP SEQUENCE " + seqName
val sqlCreate = "CREATE SEQUENCE " + seqName
}
f.record.relation.addPreAux(seq)
}
case _ =>
}
def defaultExpression[R <: Record[_, R]](field: Field[_, R]): String =
field match {
case a: AutoIncrementable[_, _] if (a.autoIncrement_?) =>
" DEFAULT NEXTVAL('" + sequenceName(field) + "')"
case _ =>
field.defaultExpression.map(" DEFAULT " + _).getOrElse("")
}
def sequenceName[R <: Record[_, R]](f: Field[_, R]) =
quoteIdentifier(f.record.relation.schema.name) + "." +
quoteIdentifier(f.record.relation.relationName + "_" + f.name + "_seq")
def uniqueKeyDefinition(uniq: UniqueKey) =
"UNIQUE (" + uniq.fields.map(_.name).mkString(", ") + ")"
def foreignKeyDefinition(fk: ForeignKey) =
"FOREIGN KEY (" + fk.childFields.map(_.name).mkString(", ") +
") REFERENCES " + fk.parentRelation.qualifiedName + " (" +
fk.parentFields.map(_.name).mkString(", ") + ") " +
"ON DELETE " + fk.onDelete.toSql + " " +
"ON UPDATE " + fk.onUpdate.toSql
def checkConstraintDefinition(check: CheckConstraint) =
"CHECK (" + check.expression + ")"
|
Structured Query Language
|
def join(j: JoinNode[_, _, _, _]): String = joinInternal(j, null)
protected def joinInternal(node: RelationNode[_, _], on: String): String = {
var result = ""
node match {
case j: JoinNode[_, _, _, _] =>
result += joinInternal(j.left, on) +
" " + j.joinType.toSql + " " +
joinInternal(j.right, j.sqlOn)
case _ =>
result += node.toSql
if (on != null) result += " " + on
}
return result
}
def select(q: Select[_]): String = {
var result = "SELECT "
if (q.distinct_?)
result += "DISTINCT "
result += q.projections.map(_.toSql).mkString(", ")
if (q.from.size > 0)
result += " FROM " + q.from.map(_.toSql).mkString(", ")
if (q.where != EmptyPredicate)
result += " WHERE " + q.where.toSql
if (q.groupBy.size > 0)
result += " GROUP BY " + q.groupBy.flatMap(_.sqlAliases).mkString(", ")
if (q.having != EmptyPredicate)
result += " HAVING " + q.having.toSql
q.setOps.foreach {
case (op: SetOperation, subq: SQLQuery[_]) =>
result += " " + op.toSql + " ( " + subq.toSql + " )"
case _ =>
}
if (q.orderBy.size > 0)
result += " ORDER BY " + q.orderBy.map(_.toSql).mkString(", ")
if (q.limit > -1)
result += " LIMIT " + q.limit
if (q.offset > 0)
result += " OFFSET " + q.offset
return result
}
def identityLastIdPredicate[PK, R <: Record[PK, R]](node: RelationNode[PK, R]): Predicate =
new SimpleExpression(node.alias + "." + node.relation.PRIMARY_KEY.name + " = LASTVAL()", Nil)
def identityLastIdQuery[PK, R <: Record[PK, R]](node: RelationNode[PK, R]): SQLQuery[PK] =
new Select(expr[PK]("LASTVAL()"))
def sequenceNextValQuery[PK, R <: Record[PK, R]](node: RelationNode[PK, R]): SQLQuery[PK] =
new Select(expr[PK]("NEXTVAL('" + sequenceName(node.relation.PRIMARY_KEY) + "')"))
|
Data Manipulation Language
|
def insert[PK, R <: Record[PK, R]](dml: Insert[PK, R]): String =
"INSERT INTO " + dml.relation.qualifiedName +
" (" + dml.fields.map(f => quoteIdentifier(f.name)).mkString(", ") +
") VALUES (" + dml.fields.map(f => "?").mkString(", ") + ")"
def insertSelect[PK, R <: Record[PK, R]](dml: InsertSelect[PK, R]) =
"INSERT INTO " + dml.relation.qualifiedName + " (" +
dml.relation.fields.map(f => quoteIdentifier(f.name)).mkString(", ") + ") " + dml.query.toSql
def update[PK, R <: Record[PK, R]](dml: Update[PK, R]): String = {
var result = "UPDATE " + dml.node.toSql + " SET " +
dml.setClause.map(f => quoteIdentifier(f._1.name) + " = ?").mkString(", ")
if (dml.where != EmptyPredicate) result += " WHERE " + dml.where.toSql
return result
}
def delete[PK, R <: Record[PK, R]](dml: Delete[PK, R]): String = {
var result = "DELETE FROM " + dml.node.toSql
if (dml.where != EmptyPredicate) result += " WHERE " + dml.where.toSql
return result
}
}
|