Permalink
Browse files

Merge branch 'master' of github.com:lift/framework

  • Loading branch information...
2 parents d885916 + 7a986ee commit 7b651f0b95de126a7ab24424847f12e627a1db55 @nafg nafg committed May 29, 2011
@@ -379,7 +379,7 @@ object JsonParser {
}
}
- def near = new String(segment, (cur-20) max 0, (cur+20) min offset)
+ def near = new String(segment, (cur-20) max 0, 40 min (offset - cur + 20))
def release = segments.foreach(Segments.release)
@@ -111,7 +111,7 @@ private[json] object Meta {
val typeArgs = x.getActualTypeArguments.toList.zipWithIndex
.map { case (t, idx) =>
if (t == classOf[java.lang.Object])
- ScalaSigReader.readConstructor(context.argName, context.containingClass, context.allArgs.map(_._1))
+ ScalaSigReader.readConstructor(context.argName, context.containingClass, idx, context.allArgs.map(_._1))
else t
}
Some(mkParameterizedType(x.getRawType, typeArgs))
@@ -231,13 +231,13 @@ private[json] object Meta {
}
try {
val names = nameReader.lookupParameterNames(c).map(clean)
- val types = c.getGenericParameterTypes.toList map {
- case v: TypeVariable[_] =>
+ val types = c.getGenericParameterTypes.toList.zipWithIndex map {
+ case (v: TypeVariable[_], idx) =>
val arg = typeArgs.getOrElse(v, v)
if (arg == classOf[java.lang.Object])
- context.map(ctx => ScalaSigReader.readConstructor(ctx.argName, ctx.containingClass, ctx.allArgs.map(_._1))).getOrElse(arg)
+ context.map(ctx => ScalaSigReader.readConstructor(ctx.argName, ctx.containingClass, idx, ctx.allArgs.map(_._1))).getOrElse(arg)
else arg
- case x => x
+ case (x, _) => x
}
names.toList.zip(types)
} catch {
@@ -266,7 +266,7 @@ private[json] object Meta {
case ptype: ParameterizedType => ptype.getActualTypeArguments()(i) match {
case c: Class[_] =>
if (c == classOf[java.lang.Object])
- ScalaSigReader.readConstructor(context.argName, context.containingClass, context.allArgs.map(_._1))
+ ScalaSigReader.readConstructor(context.argName, context.containingClass, i, context.allArgs.map(_._1))
else c
case p: ParameterizedType => p.getRawType.asInstanceOf[Class[_]]
case x => fail("do not know how to get type parameter from " + x)
@@ -20,10 +20,10 @@ package json
import scala.tools.scalap.scalax.rules.scalasig._
private[json] object ScalaSigReader {
- def readConstructor(argName: String, clazz: Class[_], argNames: List[String]): Class[_] = {
+ def readConstructor(argName: String, clazz: Class[_], typeArgIndex: Int, argNames: List[String]): Class[_] = {
val cl = findClass(clazz)
val cstr = findConstructor(cl, argNames).getOrElse(Meta.fail("Can't find constructor " + clazz))
- findArgType(cstr, argNames.indexOf(argName))
+ findArgType(cstr, argNames.indexOf(argName), typeArgIndex)
}
def readField(name: String, clazz: Class[_], typeArgIndex: Int): Class[_] = {
@@ -60,17 +60,22 @@ private[json] object ScalaSigReader {
private def findField(c: ClassSymbol, name: String): Option[MethodSymbol] =
(c.children collect { case m: MethodSymbol if m.name == name => m }).headOption
- private def findArgType(s: MethodSymbol, argIdx: Int): Class[_] = {
+ private def findArgType(s: MethodSymbol, argIdx: Int, typeArgIndex: Int): Class[_] = {
def findPrimitive(t: Type): Symbol = t match {
+ case TypeRefType(ThisType(_), symbol, _) if isPrimitive(symbol) => symbol
case TypeRefType(_, _, TypeRefType(ThisType(_), symbol, _) :: xs) => symbol
- case TypeRefType(_, _, (ref @ TypeRefType(_, _, _)) :: xs) => findPrimitive(ref)
+ case TypeRefType(_, _, args) =>
+ args(typeArgIndex) match {
+ case ref @ TypeRefType(_, _, _) => findPrimitive(ref)
+ case x => Meta.fail("Unexpected type info " + x)
+ }
case x => Meta.fail("Unexpected type info " + x)
}
toClass(findPrimitive(s.children(argIdx).asInstanceOf[SymbolInfoSymbol].infoType))
}
private def findArgTypeF(s: MethodSymbol, typeArgIdx: Int): Class[_] = {
- // FIXME can be removed when 2.8 no loner needs to supported.
+ // FIXME can be removed when 2.8 no longer needs to be supported.
// 2.8 does not have NullaryMethodType, work around that.
/*
val t = s.infoType match {
@@ -99,6 +104,8 @@ private[json] object ScalaSigReader {
case _ => classOf[AnyRef]
}
+ private def isPrimitive(s: Symbol) = toClass(s) != classOf[AnyRef]
+
private def findScalaSig(clazz: Class[_]): Option[ScalaSig] =
ScalaSigParser.parse(clazz).orElse(findScalaSig(clazz.getDeclaringClass))
}
@@ -117,8 +117,17 @@ object SerializationBugs extends Specification {
val ser = Serialization.write(o)
ser mustEqual """{"x":{"some":"data"}}"""
}
+
+ "Map with Map value" in {
+ val a = Map("a" -> Map("a" -> 5))
+ val b = Map("b" -> 1)
+ val str = Serialization.write(MapWithMap(a, b))
+ read[MapWithMap](str) mustEqual MapWithMap(a, b)
+ }
}
+case class MapWithMap(a: Map[String, Map[String, Int]], b: Map[String, Int])
+
case class LongList(xs: List[Num])
case class Num(x: Int)
@@ -161,13 +161,38 @@ object DriverType {
case (SybaseASEDriver.name,_,_) => SybaseASEDriver
case (OracleDriver.name,_,_) => OracleDriver
case (MaxDbDriver.name,_,_) => MaxDbDriver
+ case (other, _, _) if other.startsWith(DB2Driver.name) => DB2Driver
case x => throw new Exception(
"Lift mapper does not support JDBC driver %s.\n".format(x) +
"See http://wiki.liftweb.net/index.php/Category:Database for a list of supported databases.")
}
}
}
+object DB2Driver extends DriverType("DB2") {
+ def binaryColumnType = "LONG VARCHAR FOR BIT DATA"
+ def booleanColumnType = "SMALLINT"
+ def clobColumnType = "LONG VARCHAR"
+ def dateTimeColumnType = "TIMESTAMP"
+ def dateColumnType = "DATE"
+ def timeColumnType = "TIME"
+ def integerColumnType = "INT"
+ def integerIndexColumnType = "INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY"
+ def enumColumnType = "INT"
+ def longForeignKeyColumnType = "INT"
+ def longIndexColumnType = "BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY"
+ def enumListColumnType = "INT"
+ def longColumnType = "INT"
+ def doubleColumnType = "DOUBLE"
+
+ override def brokenLimit_? : Boolean = true
+
+ override def pkDefinedByIndexColumn_? = true
+
+ // This will let DB2 handle the schema name without case issues
+ override def defaultSchemaName = Full(null)
+}
+
object DerbyDriver extends DriverType("Apache Derby") {
def binaryColumnType = "LONG VARCHAR FOR BIT DATA"
def booleanColumnType = "SMALLINT"
@@ -495,30 +495,19 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
by match {
case Nil => curPos
case Cmp(field, _, Full(value), _, _) :: xs =>
- if (field.dbIgnoreSQLType_?)
- st.setObject(curPos, field.convertToJDBCFriendly(value))
- else
- st.setObject(curPos, field.convertToJDBCFriendly(value), conn.driverType.columnTypeMap(field.targetSQLType))
-
+ setPreparedStatementValue(conn, st, curPos, field, field.targetSQLType, value.asInstanceOf[AnyRef], objectSetterFor(field))
setStatementFields(st, xs, curPos + 1, conn)
case ByList(field, orgVals) :: xs => {
- val vals = Set(orgVals :_*).toList
- var newPos = curPos
- vals.foreach(v => {
- if (field.dbIgnoreSQLType_?)
- st.setObject(newPos,
- field.convertToJDBCFriendly(v))
- else
- st.setObject(newPos,
- field.convertToJDBCFriendly(v),
- conn.driverType.columnTypeMap(field.targetSQLType))
-
- newPos = newPos + 1
- })
-
- setStatementFields(st, xs, newPos, conn)
- }
+ val vals = Set(orgVals :_*).toList
+ var newPos = curPos
+ vals.foreach(v => {
+ setPreparedStatementValue(conn, st, newPos, field, field.targetSQLType, v.asInstanceOf[AnyRef], objectSetterFor(field))
+ newPos = newPos + 1
+ })
+
+ setStatementFields(st, xs, newPos, conn)
+ }
case (in: InThing[A]) :: xs =>
val newPos = in.innerMeta.setStatementFields(st, in.queryParams,
@@ -552,13 +541,8 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
st.setTimestamp(curPos, new java.sql.Timestamp(d.getTime))
setStatementFields(st, xs, curPos + 1, conn)
case List(field: BaseMappedField) =>
- if (field.dbIgnoreSQLType_?)
- st.setObject(curPos, field.jdbcFriendly)
- else
- st.setObject(curPos, field.jdbcFriendly, conn.driverType.columnTypeMap(field.targetSQLType))
-
+ setPreparedStatementValue(conn, st, curPos, field, field.targetSQLType, field.jdbcFriendly, objectSetterFor(field))
setStatementFields(st, xs, curPos + 1, conn)
-
case p :: ps =>
setStatementFields(st, BySql[A](query, who, p) :: BySql[A](query, who, ps: _*) :: xs, curPos, conn)
}
@@ -611,10 +595,7 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
st =>
val indVal = indexedField(toDelete)
indVal.map{indVal =>
- if (indVal.dbIgnoreSQLType_?)
- st.setObject(1, indVal.jdbcFriendly(im))
- else
- st.setObject(1, indVal.jdbcFriendly(im), conn.driverType.columnTypeMap(indVal.targetSQLType(im)))
+ setPreparedStatementValue(conn, st, 1, indVal, im, objectSetterFor(indVal))
st.executeUpdate == 1
} openOr false
@@ -758,6 +739,87 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
*/
def clean_?(toCheck: A): Boolean = mappedColumns.foldLeft(true)((bool, ptr) => bool && !(??(ptr._2, toCheck).dirty_?))
+ /**
+ * Sets a prepared statement value based on the given MappedField's value
+ * and column name. This delegates to the BaseMappedField overload of
+ * setPreparedStatementValue by retrieving the necessary values.
+ *
+ * @param conn The connection for this prepared statement
+ * @param st The prepared statement
+ * @param index The index for this prepared statement value
+ * @param field The field corresponding to this prepared statement value
+ * @param columnName The column name to use to retrieve the type and value
+ * @param setObj A function that we can delegate to for setObject calls
+ */
+ private def setPreparedStatementValue(conn: SuperConnection,
+ st: PreparedStatement,
+ index: Int,
+ field: MappedField[_, A],
+ columnName : String,
+ setObj : (PreparedStatement, Int, AnyRef, Int) => Unit) {
+ setPreparedStatementValue(conn, st, index, field,
+ field.targetSQLType(columnName),
+ field.jdbcFriendly(columnName),
+ setObj)
+ }
+
+ /**
+ * Sets a prepared statement value based on the given BaseMappedField's type and value. This
+ * allows us to do special handling based on the type in a central location.
+ *
+ * @param conn The connection for this prepared statement
+ * @param st The prepared statement
+ * @param index The index for this prepared statement value
+ * @param field The field corresponding to this prepared statement value
+ * @param columnType The JDBC SQL Type for this value
+ * @param value The value itself
+ * @param setObj A function that we can delegate to for setObject calls
+ */
+ private def setPreparedStatementValue(conn: SuperConnection,
+ st: PreparedStatement,
+ index: Int,
+ field: BaseMappedField,
+ columnType : Int,
+ value : Object,
+ setObj : (PreparedStatement, Int, AnyRef, Int) => Unit) {
+ // Remap the type if the driver wants
+ val mappedColumnType = conn.driverType.columnTypeMap(columnType)
+
+ // We generally use setObject for everything, but we've found some broken JDBC drivers
+ // which has prompted us to use type-specific handling for certain types
+ mappedColumnType match {
+ case Types.VARCHAR =>
+ // Set a string with a simple guard for null values
+ st.setString(index, if (value ne null) value.toString else value.asInstanceOf[String])
+
+ // Sybase SQL Anywhere and DB2 choke on using setObject for boolean data
+ case Types.BOOLEAN => value match {
+ case intData : java.lang.Integer => st.setBoolean(index, intData.intValue != 0)
+ case b : java.lang.Boolean => st.setBoolean(index, b.booleanValue)
+ // If we can't figure it out, maybe the driver can
+ case other => setObj(st, index, other, mappedColumnType)
+ }
+
+ // In all other cases, delegate to the driver
+ case _ => setObj(st, index, value, mappedColumnType)
+ }
+ }
+
+ /**
+ * This is a utility method to simplify using setObject. It's intended use is to
+ * generate a setObject proxy so that the intermediate code doesn't need to be aware
+ * of drivers that ignore column types.
+ */
+ private def objectSetterFor(field : BaseMappedField) = {
+ (st : PreparedStatement, index : Int, value : AnyRef, columnType : Int) => {
+ if (field.dbIgnoreSQLType_?) {
+ st.setObject(index, value)
+ } else {
+ st.setObject(index, value, columnType)
+ }
+ }
+ }
+
def save(toSave: A): Boolean = {
toSave match {
case x: MetaMapper[_] => throw new MapperException("Cannot save the MetaMapper singleton")
@@ -812,21 +874,11 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
st =>
var colNum = 1
+ // Here we apply each column's value to the prepared statement
for (col <- mappedColumns) {
val colVal = ??(col._2, toSave)
if (!columnPrimaryKey_?(col._1) && colVal.dirty_?) {
- colVal.targetSQLType(col._1) match {
- case Types.VARCHAR => st.setString(colNum, colVal.jdbcFriendly(col._1).asInstanceOf[String])
-
- case _ =>
- if (colVal.dbIgnoreSQLType_?)
- st.setObject(colNum, colVal.jdbcFriendly(col._1))
- else
- st.setObject(colNum, colVal.jdbcFriendly(col._1),
- conn.driverType.
- columnTypeMap(colVal.
- targetSQLType(col._1)))
- }
+ setPreparedStatementValue(conn, st, colNum, colVal, col._1, objectSetterFor(colVal))
colNum = colNum + 1
}
}
@@ -835,13 +887,7 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
indVal <- indexedField(toSave)
indexColumnName <- thePrimaryKeyField
} {
- if (indVal.dbIgnoreSQLType_?)
- st.setObject(colNum, indVal.jdbcFriendly(indexColumnName))
- else
- st.setObject(colNum, indVal.jdbcFriendly(indexColumnName),
- conn.driverType.
- columnTypeMap(indVal.
- targetSQLType(indexColumnName)))
+ setPreparedStatementValue(conn, st, colNum, indVal, indexColumnName, objectSetterFor(indVal))
}
st.executeUpdate
@@ -862,24 +908,7 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
for (col <- mappedColumns) {
if (!columnPrimaryKey_?(col._1)) {
val colVal = col._2.invoke(toSave).asInstanceOf[MappedField[AnyRef, A]]
- colVal.targetSQLType(col._1) match {
- case Types.VARCHAR =>
- st.setString(colNum, colVal.jdbcFriendly(col._1).asInstanceOf[String])
-
- // Sybase SQL Anywhere chokes on using setObject for boolean data
- case Types.BOOLEAN =>
- // For some reason MappedBoolean uses Ints to represent data
- st.setBoolean(colNum, colVal.jdbcFriendly(col._1) == 1)
-
- case _ =>
- if (colVal.dbIgnoreSQLType_?)
- st.setObject(colNum, colVal.jdbcFriendly(col._1))
- else
- st.setObject(colNum, colVal.jdbcFriendly(col._1),
- conn.driverType.
- columnTypeMap(colVal.
- targetSQLType(col._1)))
- }
+ setPreparedStatementValue(conn, st, colNum, colVal, col._1, objectSetterFor(colVal))
colNum = colNum + 1
}
}

0 comments on commit 7b651f0

Please sign in to comment.