diff options
author | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2006-03-20 15:58:47 +0000 |
---|---|---|
committer | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2006-03-20 15:58:47 +0000 |
commit | 0a5eb2c599eead1eeba7559ed0dcc5706079bf4d (patch) | |
tree | 73c710573e7146cb6958e90f679ed35f51aaa740 /src/dbc | |
parent | b656cd6c838b424ed2aa0cff746e3f49b9871dd0 (diff) | |
download | scala-0a5eb2c599eead1eeba7559ed0dcc5706079bf4d.tar.gz scala-0a5eb2c599eead1eeba7559ed0dcc5706079bf4d.tar.bz2 scala-0a5eb2c599eead1eeba7559ed0dcc5706079bf4d.zip |
1.
2. ScalaTool Ant task updated to be more flexible.
3. Build now generates a separate archive for DBC (also changed in source layout).
Diffstat (limited to 'src/dbc')
65 files changed, 3439 insertions, 0 deletions
diff --git a/src/dbc/scala/dbc/DataType.scala b/src/dbc/scala/dbc/DataType.scala new file mode 100644 index 0000000000..f9b12bc431 --- /dev/null +++ b/src/dbc/scala/dbc/DataType.scala @@ -0,0 +1,70 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc; + + +/** An ISO-9075:2003 (SQL) data type. Mappings between SQL types and + * database specific types should be provided by the database driver. + */ +abstract class DataType { + + /** Tests whether this datatype is equivalent to another. Usually, two + * types are defined as equivalent if they are equal. Two types can be + * equivalent without being equal if values of those types will be + * encoded in the same native Scala type. + */ + def isEquivalent(datatype: DataType): Boolean; + + /** Tests whether this datatype is equivalent or a subtype of another + * datatype. Type <code>A</code> is said to be subtype of type + * <code>B</code> if any value of type <code>A</code> can be + * represented as a value of type <code>B</code>. + */ + def isSubtypeOf(datatype: DataType): Boolean; + + /** The native Scala type in which values of this SQL type will be + * encoded. + */ + type NativeType <: Any; + + /** The native Scala type in which values of this SQL type will be + * encoded. This must point to the same type as <code>NativeType</code>. + */ + def nativeTypeId: DataType.Id; + + /** Whether the value can take the null value, None when this property is + * unknown. + */ + def nullable: Option[Boolean] = None; + + /** The SQL name of the type */ + def sqlString: String = "UNDEFINED DATA TYPE" + +} + +object DataType { + + type Id = Int; + + val OBJECT : Id = 10; + val BOOLEAN : Id = 20; + val BYTE : Id = 30; + val SHORT : Id = 31; + val INT : Id = 32; + val LONG : Id = 33; + val BIG_INTEGER: Id = 34; + val BIG_DECIMAL: Id = 35; + val FLOAT : Id = 40; + val DOUBLE : Id = 41; + val STRING : Id = 50; + +} diff --git a/src/dbc/scala/dbc/Database.scala b/src/dbc/scala/dbc/Database.scala new file mode 100644 index 0000000000..3dd27c8481 --- /dev/null +++ b/src/dbc/scala/dbc/Database.scala @@ -0,0 +1,189 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc; + + +import java.sql._; + +/** A link to a database. The <code>Database</code> abstract class must + * be specialised for every different DBMS. + * @author Gilles Dubochet + **/ +case class Database(dbms: Vendor) { + + class Closed extends Exception {} + + /** A lock used for operations that need to be atomic for this database + * instance. */ + private val lock: scala.concurrent.Lock = new scala.concurrent.Lock(); + + /** The vendor of the DBMS that contains this database. */ + private val vendor: Vendor = dbms; + + /** The Database connections available to use. */ + private var availableConnections: List[Connection] = Nil; + + /** The connections that are currently in use. */ + private var usedConnections: List[Connection] = Nil; + + /** Whether the database no longer accepts new connections. */ + private var closing: Boolean = false; + + /** Retrieves a connection from the available connection pool or creates + * a new one. + * + * @returns A connection that can be used to access the database. + */ + private def getConnection: Connection = { + if (closing) { + throw new Closed; + } else { + availableConnections match { + case Nil => { + lock.acquire; + val connection = vendor.getConnection; + usedConnections = connection :: usedConnections; + lock.release; + connection + } + case connection :: cs => { + lock.acquire; + availableConnections = cs; + usedConnections = connection :: usedConnections; + lock.release; + connection; + } + } + } + } + + /** Closes a connection to this database. A closed connection might + * also return to the available connection pool if the latter is depleted. + * + * @param connection The connection that should be closed. + */ + private def closeConnection(connection: Connection): Unit = { + if (closing) { + connection.close(); + } else { + lock.acquire; + usedConnections = usedConnections.remove(e => (e.equals(connection))); + if (availableConnections.length < vendor.retainedConnections) + availableConnections = connection :: availableConnections + else + connection.close(); + lock.release; + } + } + + /** .. + */ + def close: Unit = { + closing = true; + for (val conn <- availableConnections) conn.close(); + } + + /** Executes a statement that returns a relation on this database. + * + * @param relationStatement The statement to execute. + * @return The relation returned by the database for this statement. + */ + def executeStatement(relationStatement: statement.Relation): result.Relation = + executeStatement(relationStatement, false); + + /** Executes a statement that returns a relation on this database. + * + * @param relationStatement The statement to execute. + * @param debug Whether debugging information should be printed on the console. + * @return The relation returned by the database for this statement. + */ + def executeStatement(relationStatement: statement.Relation, + debug: Boolean): result.Relation = + new scala.dbc.result.Relation { + val statement = relationStatement; + if (debug) Console.println("## " + statement.sqlString); + private val connection = getConnection; + val sqlResult = connection.createStatement().executeQuery(statement.sqlString); + closeConnection(connection); + statement.typeCheck(this); + } + + /** Executes a statement that updates the state of the database. + * + * @param statusStatement The statement to execute. + * @return The status of the database after the statement has been executed. + */ + def executeStatement(statusStatement: statement.Status): result.Status[Unit] = + executeStatement(statusStatement, false); + + /** Executes a statement that updates the state of the database. + * + * @param statusStatement The statement to execute. + * @param debug Whether debugging information should be printed on the console. + * @return The status of the database after the statement has been executed. + */ + def executeStatement(statusStatement: statement.Status, + debug: Boolean): result.Status[Unit] = + new scala.dbc.result.Status[Unit] { + val statement = statusStatement; + if (debug) Console.println("## " + statement.sqlString); + def result = (); + private val connection = getConnection; + val jdbcStatement: java.sql.Statement = connection.createStatement(); + jdbcStatement.execute(statement.sqlString); + val touchedCount = Some(jdbcStatement.getUpdateCount()); + closeConnection(connection); + } + + /** Executes a list of statements or other operations inside a transaction. + * Only statements are protected in a transaction, other Scala code is not. + * + * @param transactionStatement The transaction to execute as a closure. + * @return The status of the database after the transaction has been executed. + */ + def executeStatement[ResultType](transactionStatement: statement.Transaction[ResultType]): result.Status[ResultType] = + executeStatement(transactionStatement, false); + + /** Executes a list of statements or other operations inside a transaction. + * Only statements are protected in a transaction, other Scala code is not. + * + * @param transactionStatement The transaction to execute as a closure. + * @param debug Whether debugging information should be printed on the console. + * @return The status of the database after the transaction has been executed. + */ + def executeStatement[ResultType](transactionStatement: statement.Transaction[ResultType], debug: Boolean): result.Status[ResultType] = { + new scala.dbc.result.Status[ResultType] { + val touchedCount = None; + val statement = transactionStatement; + private val connection = getConnection; + connection.setAutoCommit(false); + val jdbcStatement: java.sql.Statement = connection.createStatement(); + if (debug) Console.println("## " + transactionStatement.sqlStartString); + jdbcStatement.execute(transactionStatement.sqlStartString); + val result: ResultType = try { + val buffer = transactionStatement.transactionBody(Database.this); + if (debug) Console.println("## " + transactionStatement.sqlCommitString); + jdbcStatement.execute(transactionStatement.sqlCommitString); + buffer + } catch { + case e: Throwable => { + if (debug) Console.println("## " + transactionStatement.sqlAbortString); + jdbcStatement.execute(transactionStatement.sqlAbortString); + throw e; + } + } + connection.setAutoCommit(true); + closeConnection(connection); + } + } + +} diff --git a/src/dbc/scala/dbc/Syntax.scala b/src/dbc/scala/dbc/Syntax.scala new file mode 100644 index 0000000000..d8ed87740a --- /dev/null +++ b/src/dbc/scala/dbc/Syntax.scala @@ -0,0 +1,48 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc; + + +import java.math.{BigDecimal, BigInteger}; + + +/** This class .. + * + */ +object Syntax { + + import syntax.DataTypeUtil; + + /* Data types */ + def boolean = DataTypeUtil.boolean; + def tinyint = DataTypeUtil.tinyint; + def smallint = DataTypeUtil.smallint; + def integer = DataTypeUtil.integer; + def bigint = DataTypeUtil.bigint; + def real = DataTypeUtil.real; + + def numeric(precision: Int) = DataTypeUtil.numeric(precision); + def numeric(precision: Int, scale: Int) = DataTypeUtil.numeric(precision, scale); + + def doublePrecision = DataTypeUtil.doublePrecision; + def character(length: Int) = DataTypeUtil.character(length); + def characterVarying(length: Int) = DataTypeUtil.characterVarying(length); + def characterLargeObject = DataTypeUtil.characterLargeObject; + + /* Statements */ + //def select + + /* Other stuff */ + def database (server: String, username: String, password: String): dbc.Database = + syntax.Database.database(server, username, password); + +} diff --git a/src/dbc/scala/dbc/Utilities.scala b/src/dbc/scala/dbc/Utilities.scala new file mode 100644 index 0000000000..c2691143f3 --- /dev/null +++ b/src/dbc/scala/dbc/Utilities.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc; + + +/** An object offering transformation methods (views) on various values. + * This object's members must be visible in an expression to use value + * auto-conversion. + */ +object Utilities { + + implicit def constantToValue (obj: statement.expression.Constant): Value = + obj.constantValue; + + implicit def valueToConstant (obj: Value): statement.expression.Constant = + new statement.expression.Constant { + val constantValue = obj; + } + +} diff --git a/src/dbc/scala/dbc/Value.scala b/src/dbc/scala/dbc/Value.scala new file mode 100644 index 0000000000..13a7678928 --- /dev/null +++ b/src/dbc/scala/dbc/Value.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc; + + +/** A SQL-99 value of any type. */ +abstract class Value { + + /** The SQL-99 type of the value. */ + val dataType: DataType; + + type NativeType = dataType.type#NativeType; + + val nativeValue: NativeType; + + /** A SQL-99 compliant string representation of the value. */ + def sqlString: String; + +} diff --git a/src/dbc/scala/dbc/Vendor.scala b/src/dbc/scala/dbc/Vendor.scala new file mode 100644 index 0000000000..b9b3595d95 --- /dev/null +++ b/src/dbc/scala/dbc/Vendor.scala @@ -0,0 +1,42 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc; + + +import java.sql.{Connection, Driver}; + + +/** This class .. + */ +abstract class Vendor { + + def nativeDriverClass: Class; + def uri: java.net.URI; + def user: String; + def pass: String; + def nativeProperties: java.util.Properties = { + val properties = new java.util.Properties(); + properties.setProperty("user", user); + properties.setProperty("password", pass); + properties + } + + def retainedConnections: Int; + + def getConnection: Connection = { + val driver = nativeDriverClass.newInstance().asInstanceOf[Driver]; + driver.connect(uri.toString(),nativeProperties) + } + + def urlProtocolString: String; + +} diff --git a/src/dbc/scala/dbc/datatype/ApproximateNumeric.scala b/src/dbc/scala/dbc/datatype/ApproximateNumeric.scala new file mode 100644 index 0000000000..8943f46364 --- /dev/null +++ b/src/dbc/scala/dbc/datatype/ApproximateNumeric.scala @@ -0,0 +1,57 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** A type category for all SQL types that store varying-precision + * numbers. + */ +abstract class ApproximateNumeric[Type] ( + override val nativeTypeId: DataType.Id +) extends datatype.Numeric[Type](nativeTypeId) { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: ApproximateNumeric[Type] => + (nativeTypeId == dt.nativeTypeId && + precisionRadix == dt.precisionRadix && + precision == dt.precision && + signed == dt.signed) + case _ => + false + } + + def isSubtypeOf (datatype:DataType) = datatype match { + case dt:ApproximateNumeric[Type] => + (nativeTypeId == dt.nativeTypeId && + precisionRadix == dt.precisionRadix && + precision <= dt.precision && + signed == dt.signed) + case _ => + false + } + + /** A SQL-99 compliant string representation of the type. + * <h3>Compatibility notice</h3> This method assumes that a real + * uses 32 bits and a double 64. This is not defined in the + * standard but is usually the case. + */ + override def sqlString: java.lang.String = Tuple2(precisionRadix,precision) match { + case Tuple2(2,64) => "REAL" + case Tuple2(2,128) => "DOUBLE PRECISION" + case Tuple2(2,p) => + throw exception.UnsupportedFeature("SQL-99 does not support an approximate numeric type with a binary defined precision other than 16, 32 and 64 bits"); + case Tuple2(10,p) => "FLOAT (" + p.toString() + ")" + case Tuple2(pr,_) => + throw exception.UnsupportedFeature("SQL-99 does not support the precision of an approximate numeric type to be defined in a radix other than 2 or 10"); + } + +} diff --git a/src/dbc/scala/dbc/datatype/Boolean.scala b/src/dbc/scala/dbc/datatype/Boolean.scala new file mode 100644 index 0000000000..c4a880885e --- /dev/null +++ b/src/dbc/scala/dbc/datatype/Boolean.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** The SQL type for a truth value. */ +class Boolean extends DataType { + + def isEquivalent (datatype:DataType) = datatype match { + case dt:Boolean => true + case _ => false + } + + def isSubtypeOf (datatype:DataType) = isEquivalent(datatype); + + type NativeType = scala.Boolean; + val nativeTypeId = DataType.BOOLEAN; + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = "BOOLEAN"; + +} diff --git a/src/dbc/scala/dbc/datatype/Character.scala b/src/dbc/scala/dbc/datatype/Character.scala new file mode 100644 index 0000000000..02b4b182e0 --- /dev/null +++ b/src/dbc/scala/dbc/datatype/Character.scala @@ -0,0 +1,40 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** A SQL type for a string of characters of arbitrary length with + * arbitrary character set. + */ +abstract class Character extends CharacterString { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: Character => + length == dt.length && encoding == dt.encoding + case _ => + false + } + + def isSubtypeOf(datatype: DataType) = datatype match { + case dt: Character => + length >= dt.length && encoding == dt.encoding + case _ => + false + } + + /** The length of the string defined in characters. */ + def length: Int; + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = "CHARACTER (" + length.toString() + ")"; + +} diff --git a/src/dbc/scala/dbc/datatype/CharacterLargeObject.scala b/src/dbc/scala/dbc/datatype/CharacterLargeObject.scala new file mode 100644 index 0000000000..c84bb4bae9 --- /dev/null +++ b/src/dbc/scala/dbc/datatype/CharacterLargeObject.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** A SQL type for an unbounded length string of characters with arbitrary + * character set. */ +class CharacterLargeObject extends CharacterString { + + def isEquivalent (datatype:DataType) = datatype match { + case dt:CharacterLargeObject => { + encoding == dt.encoding + } + case _ => false + } + + def isSubtypeOf (datatype:DataType) = isEquivalent(datatype); + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = "CHARACTER LARGE OBJECT"; + +} diff --git a/src/dbc/scala/dbc/datatype/CharacterString.scala b/src/dbc/scala/dbc/datatype/CharacterString.scala new file mode 100644 index 0000000000..1f250475b3 --- /dev/null +++ b/src/dbc/scala/dbc/datatype/CharacterString.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** A type category for all SQL types that store strings of characters. */ +abstract class CharacterString extends String { + + type NativeType = java.lang.String; + val nativeTypeId = DataType.STRING; + + /** The name of the character set in which the string is encoded. */ + def encoding: Option[java.lang.String] = None; + +} diff --git a/src/dbc/scala/dbc/datatype/CharacterVarying.scala b/src/dbc/scala/dbc/datatype/CharacterVarying.scala new file mode 100644 index 0000000000..e0cdbc819b --- /dev/null +++ b/src/dbc/scala/dbc/datatype/CharacterVarying.scala @@ -0,0 +1,41 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** A SQL type for a varying length string of characters with arbitrary + * maximal length and arbitrary character set. + */ +abstract class CharacterVarying extends CharacterString { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: CharacterVarying => + length == dt.length && encoding == dt.encoding + case _ => + false + } + + def isSubtypeOf(datatype: DataType) = datatype match { + case dt: CharacterVarying => + length >= dt.length && encoding == dt.encoding + case _ => + false + } + + /** The maximal length of the string defined in characters. */ + def length: Int; + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = + "CHARACTER VARYING (" + length.toString() + ")"; + +} diff --git a/src/dbc/scala/dbc/datatype/ExactNumeric.scala b/src/dbc/scala/dbc/datatype/ExactNumeric.scala new file mode 100644 index 0000000000..331cf866e1 --- /dev/null +++ b/src/dbc/scala/dbc/datatype/ExactNumeric.scala @@ -0,0 +1,65 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** A type category for all SQL types that store constant-precision + * numbers. + */ +abstract class ExactNumeric[Type]( + override val nativeTypeId: DataType.Id +) extends datatype.Numeric[Type](nativeTypeId) { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: ExactNumeric[Type] => + (nativeTypeId == dt.nativeTypeId && + precisionRadix == dt.precisionRadix && + precision == dt.precision && + scale == dt.scale && + signed == dt.signed) + case _ => + false + } + + def isSubtypeOf(datatype: DataType) = datatype match { + case dt: ExactNumeric[Type] => + (nativeTypeId == dt.nativeTypeId && + precisionRadix == dt.precisionRadix && + precision <= dt.precision && + scale <= dt.scale && + signed == dt.signed) + case _ => + false + } + + /** The number of digits used after the decimal point. */ + def scale: Int; + + /** A SQL-99 compliant string representation of the type. + * <h3>Compatibility notice</h3> This method assumes that an integer + * uses 32 bits, a small 16 and a big 64. This is not defined in the + * standard but is usually the case. + */ + override def sqlString: java.lang.String = Tuple3(precisionRadix,precision,scale) match { + case Tuple3(2,16,0) => "SMALLINT" + case Tuple3(2,32,0) => "INTEGER" + case Tuple3(2,64,0) => "BIGINT" + case Tuple3(2,java.lang.Integer.MAX_VALUE,0) => "BIGINT" + case Tuple3(2,p,s) => + throw exception.UnsupportedFeature("SQL-99 does not support an exact numeric type with a binary defined precision other than 16, 32 and 64 bits"); + case Tuple3(10,p,0) => "NUMERIC (" + p.toString() + ")" + case Tuple3(10,p,s) => "NUMERIC (" + p.toString() + ", " + s.toString() + ")" + case Tuple3(pr,_,_) => + throw exception.UnsupportedFeature("SQL-99 does not support the precision of an exact numeric type to be defined in a radix other than 2 or 10"); + } + +} diff --git a/src/dbc/scala/dbc/datatype/Factory.scala b/src/dbc/scala/dbc/datatype/Factory.scala new file mode 100644 index 0000000000..a4a9672184 --- /dev/null +++ b/src/dbc/scala/dbc/datatype/Factory.scala @@ -0,0 +1,250 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +import java.sql.Types._; +import java.math.BigInteger; +import java.math.BigDecimal; + +object Factory { + + final val java_lang_Integer_SIZE = 32; + final val java_lang_Long_SIZE = 64; + + /** Returns a mullable property formated as a boolean option */ + def isNullable (metadata:java.sql.ResultSetMetaData, index:Int): Option[scala.Boolean] = + metadata.isNullable(index) match { + case java.sql.ResultSetMetaData.columnNoNulls => Some(false); + case java.sql.ResultSetMetaData.columnNullable => Some(true); + case java.sql.ResultSetMetaData.columnNoNulls => None; + } + + /** Returns the binary precision for an integer field. This should only be + * used to find precision for integer numbers. It assumes that + * bytes cannot be used partially (result % 8 = 0). */ + def bytePrecision (precision:Int, signed:scala.Boolean, safe:scala.Boolean): Int = { + val decimalPrecision = precision + (if (safe) 1 else 0); + Pair(signed,decimalPrecision) match { + case Pair(_,0) => java.lang.Integer.MAX_VALUE // That's a bit of a hack. + case Pair(_,dp) if (dp <= 3) => 8 + case Pair(_,dp) if (dp <= 5) => 16 + case Pair(true,dp) if (dp <= 7) => 24 + case Pair(false,dp) if (dp <= 8) => 24 + case Pair(_,dp) if (dp <= 10) => 32 + case Pair(true,dp) if (dp <= 12) => 40 + case Pair(false,dp) if (dp <= 13) => 40 + case Pair(_,dp) if (dp <= 15) => 48 + case Pair(_,dp) if (dp <= 17) => 56 + case Pair(true,dp) if (dp <= 19) => 64 + case Pair(false,dp) if (dp <= 20) => 64 + case Pair(_,dp) if (dp <= 22) => 72 + case Pair(true,dp) if (dp <= 24) => 80 + case Pair(false,dp) if (dp <= 25) => 80 + case Pair(_,dp) if (dp <= 27) => 88 + case Pair(_,dp) if (dp <= 29) => 96 + case Pair(_,dp) if (dp <= 32) => 104 + case Pair(_,dp) if (dp <= 34) => 112 + case Pair(true,dp) if (dp <= 36) => 120 + case Pair(false,dp) if (dp <= 37) => 120 + case Pair(_,dp) if (dp <= 39) => 128 + case _ => java.lang.Integer.MAX_VALUE + } + } + + def create (metadata:java.sql.ResultSetMetaData, index:Int): DataType = { + metadata.getColumnType(index) match { + /* Boolean data types. */ + case BOOLEAN => new datatype.Boolean { + override val nullable = isNullable(metadata,index); + } + case BIT => new datatype.Boolean { + override val nullable = isNullable(metadata,index); + } + /* Fixed precision numeric data types. */ + case DECIMAL => { + Pair(bytePrecision(metadata.getPrecision(index),metadata.isSigned(index),true),metadata.getScale(index) == 0) match { + case Pair(bp,true) if (bp <= java_lang_Integer_SIZE) => + new datatype.ExactNumeric[Int](DataType.INT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(bp,true) if (bp <= java_lang_Long_SIZE) => + new datatype.ExactNumeric[Long](DataType.LONG) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(_,true) => + new datatype.ExactNumeric[BigInteger](DataType.BIG_INTEGER) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(_,false) => + new datatype.ExactNumeric[BigDecimal](DataType.BIG_DECIMAL) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + } + } + case NUMERIC => { + Pair(bytePrecision(metadata.getPrecision(index),metadata.isSigned(index),true),metadata.getScale(index) == 0) match { + case Pair(bp,true) if (bp <= java_lang_Integer_SIZE) => + new datatype.ExactNumeric[Int](DataType.INT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(bp,true) if (bp <= java_lang_Long_SIZE) => + new datatype.ExactNumeric[Long](DataType.LONG) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(_,true) => + new datatype.ExactNumeric[BigInteger](DataType.BIG_INTEGER) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(_,false) => + new datatype.ExactNumeric[BigDecimal](DataType.BIG_DECIMAL) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + } + } + /* Fixed precision integer data types. */ + case BIGINT => + new datatype.ExactNumeric[Long](DataType.LONG) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 64; + val signed = metadata.isSigned(index); + val scale = 0; + } + case INTEGER => + new datatype.ExactNumeric[Int](DataType.INT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 32; + val signed = metadata.isSigned(index); + val scale = 0; + } + case SMALLINT => + new datatype.ExactNumeric[Short](DataType.SHORT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 16; + val signed = metadata.isSigned(index); + val scale = 0; + } + case TINYINT => + new datatype.ExactNumeric[Byte](DataType.BYTE) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 8; + val signed = metadata.isSigned(index); + val scale = 0; + } + /* Floating point numeric data types. */ + case REAL => + new datatype.ApproximateNumeric[Float](DataType.FLOAT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 64; + val signed = metadata.isSigned(index); + } + case DOUBLE => + new datatype.ApproximateNumeric[Double](DataType.DOUBLE) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 128; + val signed = metadata.isSigned(index); + } + case FLOAT => + new datatype.ApproximateNumeric[Double](DataType.DOUBLE) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 128; + val signed = metadata.isSigned(index); + } + /* Character string data types. */ + case CHAR => new datatype.Character { + override val nullable = isNullable(metadata,index); + val length = metadata.getColumnDisplaySize(index); + } + case CLOB => new datatype.CharacterLargeObject { + override val nullable = isNullable(metadata,index); + } + case LONGVARCHAR => { + if (metadata.getColumnDisplaySize(index) >= 0) + new datatype.CharacterVarying { + override val nullable = isNullable(metadata,index); + def length = metadata.getColumnDisplaySize(index); + } + else // A PostgreSQL Hack + new datatype.CharacterLargeObject { + override val nullable = isNullable(metadata,index); + } + } + case VARCHAR => { + if (metadata.getColumnDisplaySize(index) >= 0) + new datatype.CharacterVarying { + override val nullable = isNullable(metadata,index); + def length = metadata.getColumnDisplaySize(index); + } + else // A PostgreSQL Hack + new datatype.CharacterLargeObject { + override val nullable = isNullable(metadata,index); + } + } + /* Undefined cases. */ + case OTHER => new datatype.Unknown { + override val nullable = isNullable(metadata, index); + } + /* Unsupported data types. */ + case REF | ARRAY | STRUCT => + error ("I don't support composite data types yet."); + case DATALINK | DISTINCT | JAVA_OBJECT | NULL => + error ("I won't support strange data types."); + /* Unsupported binary string data types. */ + case BINARY | BLOB | LONGVARBINARY | VARBINARY => + error ("I don't support binary string data types yet."); + /* Unsupported date and time data types. */ + case DATE | TIME | TIMESTAMP => + error ("I don't support date and time data types yet."); + /* Default case */ + case x => error ("I don't know about this ("+metadata.getColumnTypeName(index)+") JDBC type.") + } + } +} diff --git a/src/dbc/scala/dbc/datatype/Numeric.scala b/src/dbc/scala/dbc/datatype/Numeric.scala new file mode 100644 index 0000000000..d086ecdfff --- /dev/null +++ b/src/dbc/scala/dbc/datatype/Numeric.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** A type category for all SQL types that store numbers. */ +abstract class Numeric[Type](_nativeTypeId: DataType.Id) extends DataType { + + type NativeType = Type; + val nativeTypeId = _nativeTypeId; + + /** The radix in which the precision (and scale when appliable) is defined. + * ISO-9075 only allows 2 and 10 for this value. + */ + def precisionRadix: Int; + + /** The number of significant digits for that number. */ + def precision: Int; + + /** Whether the number is signed or not. */ + def signed: scala.Boolean; + +} diff --git a/src/dbc/scala/dbc/datatype/String.scala b/src/dbc/scala/dbc/datatype/String.scala new file mode 100644 index 0000000000..878bea7061 --- /dev/null +++ b/src/dbc/scala/dbc/datatype/String.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** A type category for all SQL types that store strings of elements. + */ +abstract class String extends DataType { + + /** The maximal possible length of the string defined in characters. + * This is an implementation-specific value. + */ + def maxLength: Option[Int] = None; + +} diff --git a/src/dbc/scala/dbc/datatype/Unknown.scala b/src/dbc/scala/dbc/datatype/Unknown.scala new file mode 100644 index 0000000000..4ab1db59f8 --- /dev/null +++ b/src/dbc/scala/dbc/datatype/Unknown.scala @@ -0,0 +1,34 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.datatype; + + +/** The SQL type for a truth value. */ +class Unknown extends DataType { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: Unknown => + nativeTypeId == dt.nativeTypeId + case _ => + false + } + + def isSubtypeOf(datatype: DataType) = true; + + type NativeType = Object; + val nativeTypeId = DataType.OBJECT; + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = + error("The 'UNKNOWN' data type cannot be represented."); + +} diff --git a/src/dbc/scala/dbc/exception/IncompatibleSchema.scala b/src/dbc/scala/dbc/exception/IncompatibleSchema.scala new file mode 100644 index 0000000000..bb566ff6f2 --- /dev/null +++ b/src/dbc/scala/dbc/exception/IncompatibleSchema.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.exception; + + +/** A type category for all SQL types that store constant-precision numbers. */ +case class IncompatibleSchema ( + expectedSchema: List[DataType], + foundSchema: List[DataType] +) extends Exception; diff --git a/src/dbc/scala/dbc/exception/UnsupportedFeature.scala b/src/dbc/scala/dbc/exception/UnsupportedFeature.scala new file mode 100644 index 0000000000..073bd8ab32 --- /dev/null +++ b/src/dbc/scala/dbc/exception/UnsupportedFeature.scala @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.exception; + + +/** A type category for all SQL types that store constant-precision numbers. */ +case class UnsupportedFeature (msg: String) extends Exception; diff --git a/src/dbc/scala/dbc/result/Field.scala b/src/dbc/scala/dbc/result/Field.scala new file mode 100644 index 0000000000..75c9898076 --- /dev/null +++ b/src/dbc/scala/dbc/result/Field.scala @@ -0,0 +1,63 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.result; + + +import scala.dbc.datatype._; +import scala.dbc.value._; + +/** An ISO-9075:2003 (SQL) table field. */ +abstract class Field { + + /** The content (value) of the field. The type of this value is undefined, + * transformation into a useful type will be done by an automatic view + * function defined in the field object. */ + def content: Value; + + final def value[Type <: Value]: Type = + content.asInstanceOf[Type]; + + final def exactNumericValue[NativeType] = + content.asInstanceOf[dbc.value.ExactNumeric[NativeType]]; + + final def approximateNumericValue[NativeType] = + content.asInstanceOf[dbc.value.ApproximateNumeric[NativeType]]; + + final def booleanValue = + content.asInstanceOf[dbc.value.Boolean]; + + final def characterValue = + content.asInstanceOf[dbc.value.Character]; + + final def characterLargeObjectValue = + content.asInstanceOf[dbc.value.CharacterLargeObject]; + + final def characterVaryingValue = + content.asInstanceOf[dbc.value.CharacterVarying]; + + final def unknownValue = + content.asInstanceOf[dbc.value.Unknown]; + + /** The tuple that contains this field. */ + def originatingTuple: Tuple; + + /** The field metadata attached to this field. */ + def metadata: FieldMetadata; + +} + +object Field { + + implicit def fieldToValue (field:Field): Value = field.content; + + +} diff --git a/src/dbc/scala/dbc/result/FieldMetadata.scala b/src/dbc/scala/dbc/result/FieldMetadata.scala new file mode 100644 index 0000000000..a3117a262c --- /dev/null +++ b/src/dbc/scala/dbc/result/FieldMetadata.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.result; + + +/** Informations attached to a field about its content and its relationship to the originating database. */ +abstract class FieldMetadata { + + /** The name of the field. */ + def name: String; + + /** The index of the field in the tuple. */ + def index: Int; + + /** The expected type of the field. This information is used for automatic transformation of the field value into a usable type. */ + def datatype: DataType; + + /** The name of the catalog in the database from which the field originates */ + def catalog: String; + + /** The name of the schema in the database from which the field originates */ + def schema: String; + + /** The name of the table in the database from which the field originates */ + def table: String; + +} diff --git a/src/dbc/scala/dbc/result/Relation.scala b/src/dbc/scala/dbc/result/Relation.scala new file mode 100644 index 0000000000..218f8ff825 --- /dev/null +++ b/src/dbc/scala/dbc/result/Relation.scala @@ -0,0 +1,72 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.result; + + +/** An ISO-9075:2003 (SQL) table. This is equivalent to a relation in the + * relational model. */ +abstract class Relation extends Object with Iterable[Tuple] { + + /** The statement that generated this relation. */ + def statement: scala.dbc.statement.Relation; + + /** A JDBC result containing this relation. */ + protected def sqlResult: java.sql.ResultSet; + + /** A JDBC metadata object attached to the relation. */ + protected def sqlMetadata: java.sql.ResultSetMetaData = sqlResult.getMetaData(); + + /** Metadata about all fields in a tuple of the relation. */ + def metadata: List[FieldMetadata] = + for (val count <- List.range(1, sqlMetadata.getColumnCount()+1)) yield + new FieldMetadata { + val name: String = sqlMetadata.getColumnName(count); + val index: Int = count; + val datatype: DataType = dbc.datatype.Factory.create(sqlMetadata,count); + val catalog: String = sqlMetadata.getCatalogName(count); + val schema: String = sqlMetadata.getSchemaName(count); + val table: String = sqlMetadata.getTableName(count); + } + + /** Metadata about the field at the given index. If there is no such + * field <code>None</code> is returned instead. */ + def metadataFor (index:Int): Option[FieldMetadata] = + try {Some(metadata(index))} catch {case e => None} + + /** Metadata about the field with the given column name. If there is no + * such field, <code>None</code> is returned instead. */ + def metadataFor (name:String): Option[FieldMetadata] = + metadata.find(f=>(f.name==name)); + + /** An iterator on the tuples of the relation. + * <h3>Caution</h3> A Relation only has one single iterator, due to limitations + * in DBMS. This means that if this method is called multiple times, all returned + * iterators will share the same state. */ + def elements: Iterator[Tuple] = new Iterator[Tuple] { + protected val result: java.sql.ResultSet = Relation.this.sqlResult; + def hasNext: Boolean = !(result.isLast()); + def next: Tuple = { + if (result.next()) { + new Tuple { + val me = this; + val originatingRelation = Relation.this; + val fields: List[Field] = for (val fieldMetadata <- metadata) yield + new Field { + val metadata = fieldMetadata; + val content = dbc.value.Factory.create(result,metadata.index,metadata.datatype); + val originatingTuple = me; + } + } + } else error("next on empty iterator") + } + } +} diff --git a/src/dbc/scala/dbc/result/Status.scala b/src/dbc/scala/dbc/result/Status.scala new file mode 100644 index 0000000000..c8a2370819 --- /dev/null +++ b/src/dbc/scala/dbc/result/Status.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.result; + + +import scala.dbc.datatype._; + +/** An object containing the status of a query */ +abstract class Status[ResultType] { + + /** The statement that generated this status result. */ + def statement: scala.dbc.statement.Statement; + + /** The number of elements modified or added by this statement. */ + def touchedCount: Option[Int]; + + def result: ResultType; + +} diff --git a/src/dbc/scala/dbc/result/Tuple.scala b/src/dbc/scala/dbc/result/Tuple.scala new file mode 100644 index 0000000000..6ed8955951 --- /dev/null +++ b/src/dbc/scala/dbc/result/Tuple.scala @@ -0,0 +1,42 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.result; + + +/** An ISO-9075:2003 (SQL) table row. This is equivalent to a tuple in the relational model. */ +abstract class Tuple { + + /** All the fields contained in the tuple. */ + def fields: List[Field]; + + /** The relation that contains the tuple. */ + def originatingRelation: Relation; + + /** The field at the given index. If there is no such field (that is the index is out of bounds), <code>None</code> is returned instead. */ + def apply (index:Int): Field = + try { + fields(index) + } catch { + case e => + throw new java.lang.IndexOutOfBoundsException("Field at index "+index+" does not exist in relation"); + } + + /** The field with the given column name. If there is no such field, <code>None</code> is returned instead. */ + def apply (name:String): Field = { + def findField (fields: List[Field], name:String): Field = fields match { + case Nil => throw new java.lang.IndexOutOfBoundsException("Field '"+name+"' does not exist in relation") + case field :: _ if (field.metadata.name == name) => field + case field :: fields => findField (fields, name) + } + findField (fields, name); + } +} diff --git a/src/dbc/scala/dbc/statement/AccessMode.scala b/src/dbc/scala/dbc/statement/AccessMode.scala new file mode 100644 index 0000000000..16f778b35e --- /dev/null +++ b/src/dbc/scala/dbc/statement/AccessMode.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +abstract class AccessMode { + def sqlString: String; +} + +object AccessMode { + case object ReadOnly extends AccessMode { + def sqlString = "READ ONLY"; + } + case object ReadWrite extends AccessMode { + def sqlString = "READ WRITE" + } +} diff --git a/src/dbc/scala/dbc/statement/DerivedColumn.scala b/src/dbc/scala/dbc/statement/DerivedColumn.scala new file mode 100644 index 0000000000..653ce75698 --- /dev/null +++ b/src/dbc/scala/dbc/statement/DerivedColumn.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +abstract class DerivedColumn { + + /** The value for the column. This value can be of any type but must be + * calculated from fields that appear in a relation that takes part + * in the query. */ + def valueExpression: Expression; + + /** A new name for this field. This name must be unique for the query in + * which the column takes part. */ + def asClause: Option[String]; + + /** A SQL-99 compliant string representation of the derived column + * sub-statement. This only has a meaning inside a select statement. */ + def sqlString: String = ( + valueExpression.sqlInnerString + + (asClause match { + case None => "" + case Some(ac) => " AS " + ac + }) + ); + +} diff --git a/src/dbc/scala/dbc/statement/Expression.scala b/src/dbc/scala/dbc/statement/Expression.scala new file mode 100644 index 0000000000..6243564311 --- /dev/null +++ b/src/dbc/scala/dbc/statement/Expression.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** An expression that calculates some value from fields. */ +abstract class Expression extends Relation { + + def fieldTypes: List[DataType] = Nil; + + /** A SQL-99 compliant string representation of the expression. */ + def sqlString: String = { + "SELECT " + sqlInnerString + } + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String; + +} diff --git a/src/dbc/scala/dbc/statement/Insert.scala b/src/dbc/scala/dbc/statement/Insert.scala new file mode 100644 index 0000000000..19d236d2fb --- /dev/null +++ b/src/dbc/scala/dbc/statement/Insert.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +import scala.dbc.statement.expression._; + +/** An insertion of values into a table. */ +case class Insert ( + insertionTarget: String, + insertionData: InsertionData +) extends Status { + + /** A SQL-99 compliant string representation of the select statement. */ + def sqlString: String = ( + "INSERT INTO " + + insertionTarget + + " " + insertionData.sqlString + ); + + /** The name of the table where the data should be added. */ + //def insertionTarget: String; + + /** The data that will be added tot he table. */ + //def insertionData: InsertionData; + +} diff --git a/src/dbc/scala/dbc/statement/InsertionData.scala b/src/dbc/scala/dbc/statement/InsertionData.scala new file mode 100644 index 0000000000..c36dde0fb7 --- /dev/null +++ b/src/dbc/scala/dbc/statement/InsertionData.scala @@ -0,0 +1,41 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +import scala.dbc.statement.expression._; + +/** Data to be inserted into a table in an <code>Insert</code>. */ +abstract class InsertionData { + def sqlString: String; +} + +object InsertionData { + /** Insertion of data resulting from a query on the database. */ + case class Subquery (query:Relation) extends InsertionData { + def sqlString = query.sqlString; + } + /** Insertion of data as explicitly defined values. */ + case class Constructor ( + columnNames:Option[List[String]], + columnValues:List[Expression] + ) extends InsertionData { + def sqlString = ( + (columnNames match { + case None => "" + case Some(cn) => cn.mkString(" (",", ",")") + }) + + " VALUES" + + columnValues.map(e=>e.sqlInnerString).mkString(" (",", ",")") + ) + } +} diff --git a/src/dbc/scala/dbc/statement/IsolationLevel.scala b/src/dbc/scala/dbc/statement/IsolationLevel.scala new file mode 100644 index 0000000000..4d1d2ef2de --- /dev/null +++ b/src/dbc/scala/dbc/statement/IsolationLevel.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +abstract class IsolationLevel { + def sqlString: String; +} + +object IsolationLevel { + case object ReadUncommitted extends IsolationLevel { + def sqlString = "ISOLATION LEVEL READ UNCOMMITTED" + } + case object ReadCommitted extends IsolationLevel { + def sqlString = "ISOLATION LEVEL READ COMMITTED" + } + case object RepeatableRead extends IsolationLevel { + def sqlString = "ISOLATION LEVEL REPEATABLE READ" + } + case object Serializable extends IsolationLevel { + def sqlString = "ISOLATION LEVEL SERIALIZABLE" + } +} diff --git a/src/dbc/scala/dbc/statement/JoinType.scala b/src/dbc/scala/dbc/statement/JoinType.scala new file mode 100644 index 0000000000..77befa5607 --- /dev/null +++ b/src/dbc/scala/dbc/statement/JoinType.scala @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** A join behaviour in a <code>Jointure</code>. */ +abstract class JoinType { + /** A SQL-99 string representation of the join behaviour. */ + def sqlString: String; +} + +object JoinType { + /** A join behaviour where a joined tuple is created only when a + * corresponding tuple exists in both original relations. */ + case object Inner extends JoinType { + val sqlString = "INNER JOIN" + } + /** A join behaviour family where a joined tuple is created even when a + * tuple has no corresponding tuple in the other relation. The fields + * populated by values of the other tuple will receive the NULL value. + */ + abstract class Outer extends JoinType; + object Outer { + /** An outer join behaviour where there will be at least on tuple for + * every tuple in the left relation. */ + case object Left extends Outer { + val sqlString = "LEFT OUTER JOIN" + } + /** An outer join behaviour where there will be at least on tuple for + * every tuple in the right relation. */ + case object Right extends Outer { + val sqlString = "RIGHT OUTER JOIN" + } + /** An outer join behaviour where there will be at least on tuple for + * every tuple in both right and left relations. */ + case object Full extends Outer { + val sqlString = "FULL OUTER JOIN" + } + } +} diff --git a/src/dbc/scala/dbc/statement/Jointure.scala b/src/dbc/scala/dbc/statement/Jointure.scala new file mode 100644 index 0000000000..471626ab05 --- /dev/null +++ b/src/dbc/scala/dbc/statement/Jointure.scala @@ -0,0 +1,47 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** A jointure between two relations. */ +abstract class Jointure extends Relation { + + /** The relation on the left part of the join. */ + def leftRelation: Relation; + + /** The relation on the right part of the join. */ + def rightRelation: Relation; + + /** The type of the jointure. */ + def joinType: JoinType; + + /** The condition on which the jointure needs be done. */ + def joinCondition: Option[Expression]; + + /** A SQL-99 compliant string representation of the relation statement. */ + def sqlString: String = { + "SELECT * FROM " + sqlInnerString + } + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside a query. */ + def sqlInnerString: String = ( + leftRelation.sqlInnerString + " " + + joinType.sqlString + " " + + rightRelation.sqlInnerString + + (joinCondition match { + case Some(jc) => jc.sqlString + case None => "" + }) + ) + +} diff --git a/src/dbc/scala/dbc/statement/Relation.scala b/src/dbc/scala/dbc/statement/Relation.scala new file mode 100644 index 0000000000..d6e86b39a5 --- /dev/null +++ b/src/dbc/scala/dbc/statement/Relation.scala @@ -0,0 +1,55 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** A statement that returns a relation. */ +abstract class Relation extends Statement { + + def isCompatibleType: (DataType,DataType)=>Boolean = + ((dt,wdt)=>dt.isSubtypeOf(wdt)); + + def typeCheck (relation: result.Relation): Unit = { + val sameType: Boolean = ( + relation.metadata.length == fieldTypes.length && + (relation.metadata.zip(fieldTypes).forall({case Pair(field,expectedType) => + isCompatibleType(field.datatype, expectedType)})) + ); + if (!sameType) + throw new exception.IncompatibleSchema(fieldTypes,relation.metadata.map(field=>field.datatype)); + } + + def fieldTypes: List[DataType]; + + def sqlTypeString: String = + if (fieldTypes.isEmpty) + "UNTYPED" + else + fieldTypes.map(dt=>dt.sqlString).mkString("RELATION (",", ",")"); + + /** A SQL-99 compliant string representation of the statement. */ + def sqlString: String; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String; + + /** Executes the statement on the given database. */ + def execute (database: scala.dbc.Database): scala.dbc.result.Relation = { + database.executeStatement(this); + } + + def execute (database:scala.dbc.Database, debug:Boolean): scala.dbc.result.Relation = { + database.executeStatement(this,debug); + } + +} diff --git a/src/dbc/scala/dbc/statement/Select.scala b/src/dbc/scala/dbc/statement/Select.scala new file mode 100644 index 0000000000..4da6b9e571 --- /dev/null +++ b/src/dbc/scala/dbc/statement/Select.scala @@ -0,0 +1,95 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** A statement that when executed on a database will return a relation. + * The returned relation will be a subset of a table in the database or + * a jointure between such subsets. */ +abstract class Select extends Relation { + + /** Defines if duplicated tuples should be removed from the returned + * relation. <h3>Compatibility notice</h3> Some DBMS (PostgreSQL) allow + * uniqueness constrains on an arbitrary field instead of the entire + * tuple. */ + def setQuantifier: Option[SetQuantifier]; + + /** Defines the output fields that a tuple in the returned relation will + * contain, and their content with respect to the tables in the + * database. If the fields are not specified (that is the list is + * empty), all possible input fields will be returned. <h3>Compatibility + * notice</h3> SQL's qualified asterisk select sublist is not + * available. */ + def selectList: List[DerivedColumn]; + + /** Defines the relations from which the query will obtain its data.*/ + def fromClause: List[Relation]; + + /** Defines condition that must be true in the returned relation's tuples. + * This value expression must return a boolean or boolean-compatible + * value. This condition is applied before any GROUP BY clause. */ + def whereClause: Option[Expression]; + + /** Defines the grouping of the returned relation's tuples. One tuple is + * returned for every group. The value of <code>selectList</code> must + * use aggregate functions for calculation. */ + def groupByClause: Option[List[Expression]]; + + /** Defines conditions that must be true in the returned relation's tuples. + * The value expression must return a boolean can only refer to fields + * that are grouped or to any field from inside an aggregate function. */ + def havingClause: Option[Expression]; + + /* def windowClause: Option[_]; */ + + /** A SQL-99 compliant string representation of the select statement. */ + def sqlString: String = ( + "SELECT" + + (setQuantifier match { + case None => "" + case Some(sq) => " " + sq.sqlString + }) + + (selectList match { + case Nil => " *" + case _ => (" " + selectList.tail.foldLeft(selectList.head.sqlString) + ((name:String, dc:DerivedColumn) => name + ", " + dc.sqlString)) + }) + + (fromClause match { + case Nil => error("Empty from clause is not allowed") + case _ => (" FROM " + fromClause.tail.foldLeft(fromClause.head.sqlInnerString) + ((name:String, rel:Relation) => name + ", " + rel.sqlInnerString)) + }) + + (whereClause match { + case None => "" + case Some(expr) => " WHERE " + expr.sqlInnerString + }) + + (groupByClause match { + case None => "" + case Some(gbl) => gbl match { + case Nil => error("Empty group by clause is not allowed") + case _ => + (" GROUP BY " + + gbl.tail.foldLeft(gbl.head.sqlInnerString) + ((name:String, gb) => name + ", " + gb.sqlInnerString)) + } + }) + + (havingClause match { + case None => "" + case Some(expr) => " HAVING " + expr.sqlString + }) + ); + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside a query. */ + def sqlInnerString: String = "("+sqlString+")"; + +} diff --git a/src/dbc/scala/dbc/statement/SetClause.scala b/src/dbc/scala/dbc/statement/SetClause.scala new file mode 100644 index 0000000000..e6a55ae846 --- /dev/null +++ b/src/dbc/scala/dbc/statement/SetClause.scala @@ -0,0 +1,22 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +import scala.dbc.statement.expression._; + +/** Data to be inserted into a table in an <code>Insert</code>. */ +case class SetClause (name:String, expr:Expression) { + val value: Pair[String,Expression] = Pair(name,expr); + def sqlString: String = + value._1 + " = " + value._2.sqlInnerString; +} diff --git a/src/dbc/scala/dbc/statement/SetQuantifier.scala b/src/dbc/scala/dbc/statement/SetQuantifier.scala new file mode 100644 index 0000000000..ad7c2238c8 --- /dev/null +++ b/src/dbc/scala/dbc/statement/SetQuantifier.scala @@ -0,0 +1,34 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** A set quantifier that defines the collection type of a relation. */ +abstract class SetQuantifier { + /** A SQL-99 compliant string representation of the set quantifier. */ + def sqlString: String; +} + +object SetQuantifier { + /** A set quantifier that defines a relation as being a bag. That means + * that duplicates are allowed. */ + case object AllTuples extends SetQuantifier { + /** A SQL-99 compliant string representation of the set quantifier. */ + def sqlString: String = "ALL"; + } + /** A set quantifier that defines a relation as being a set. That means + * that duplicates are not allowed and will be pruned. */ + case object DistinctTuples extends SetQuantifier { + /** A SQL-99 compliant string representation of the set quantifier. */ + def sqlString: String = "DISTINCT"; + } +} diff --git a/src/dbc/scala/dbc/statement/Statement.scala b/src/dbc/scala/dbc/statement/Statement.scala new file mode 100644 index 0000000000..5c78b075b7 --- /dev/null +++ b/src/dbc/scala/dbc/statement/Statement.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** An ISO-9075:2003 (SQL) statement. */ +abstract class Statement { + +} diff --git a/src/dbc/scala/dbc/statement/Status.scala b/src/dbc/scala/dbc/statement/Status.scala new file mode 100644 index 0000000000..3065ec5f7a --- /dev/null +++ b/src/dbc/scala/dbc/statement/Status.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** A statement that changes the status of the database. */ +abstract class Status extends Statement { + + /** A SQL-99 compliant string representation of the statement. */ + def sqlString: String; + + /** Executes the statement on the given database. */ + def execute (database: scala.dbc.Database): scala.dbc.result.Status[Unit] = { + database.executeStatement(this); + } + + def execute (database: scala.dbc.Database, debug: Boolean): scala.dbc.result.Status[Unit] = { + database.executeStatement(this,debug); + } + +} diff --git a/src/dbc/scala/dbc/statement/Table.scala b/src/dbc/scala/dbc/statement/Table.scala new file mode 100644 index 0000000000..17015a6c73 --- /dev/null +++ b/src/dbc/scala/dbc/statement/Table.scala @@ -0,0 +1,40 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** A reference to a table in the database. + * @author Gilles Dubochet + * @version 1.0 */ +abstract class Table extends Relation { + + /** The name of the table in the database. */ + def tableName: String; + + /** The name that the table will be called in the enclosing statement. */ + def tableRename: Option[String]; + + /** A SQL-99 compliant string representation of the relation statement. */ + def sqlString: String = { + "SELECT * FROM " + tableName + } + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside a query. */ + def sqlInnerString: String = ( + tableName + + (tableRename match { + case None => "" + case Some(rename) => " AS " + rename + }) + ) +} diff --git a/src/dbc/scala/dbc/statement/Transaction.scala b/src/dbc/scala/dbc/statement/Transaction.scala new file mode 100644 index 0000000000..a802aa82fa --- /dev/null +++ b/src/dbc/scala/dbc/statement/Transaction.scala @@ -0,0 +1,55 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +/** A statement that changes the status of the database. */ +case class Transaction [ResultType] ( + transactionBody: (scala.dbc.Database=>ResultType), + accessMode: Option[AccessMode], + isolationLevel: Option[IsolationLevel] +) extends Statement { + + /** A SQL-99 compliant string representation of the statement. */ + def sqlStartString: String = ( + "START TRANSACTION" + + (Pair(accessMode,isolationLevel) match { + case Pair(None,None) => "" + case Pair(Some(am),None) => " " + am.sqlString + case Pair(None,Some(il)) => " " + il.sqlString + case Pair(Some(am),Some(il)) => " " + am.sqlString + ", " + il.sqlString + }) + ); + + def sqlCommitString: String = { + "COMMIT" + } + + def sqlAbortString: String = { + "ROLLBACK" + } + + //def transactionBody: (()=>Unit); + + //def accessMode: Option[AccessMode]; + + //def isolationLevel: Option[IsolationLevel]; + + def execute (database: scala.dbc.Database): scala.dbc.result.Status[ResultType] = { + database.executeStatement(this); + } + + def execute (database: scala.dbc.Database, debug: Boolean): scala.dbc.result.Status[ResultType] = { + database.executeStatement(this,debug); + } + +} diff --git a/src/dbc/scala/dbc/statement/Update.scala b/src/dbc/scala/dbc/statement/Update.scala new file mode 100644 index 0000000000..cf8a3c9b6f --- /dev/null +++ b/src/dbc/scala/dbc/statement/Update.scala @@ -0,0 +1,47 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement; + + +import scala.dbc.statement.expression._; + +/** An update of the state of a table. */ +case class Update ( + updateTarget: String, + setClauses: List[SetClause], + whereClause: Option[Expression] +) extends Status { + + + /** A SQL-99 compliant string representation of the select statement. */ + def sqlString: String = ( + "UPDATE " + + updateTarget + + " SET " + setClauses.map(sc=>sc.sqlString).mkString("",", ","") + + (whereClause match { + case None => "" + case Some(expr) => " WHERE " + expr.sqlString + }) + ); + + /** The name of the table that should be updated. */ + //def updateTarget: String; + + /** The data that will be added tot he table. */ + //def setClauses: List[SetClause]; + + /** Defines condition that must be true in the tuples that will be updated. + * This value expression must return a boolean or boolean-compatible + * value. */ + //def whereClause: Option[scala.dbc.statement.expression.Expression]; + +} diff --git a/src/dbc/scala/dbc/statement/expression/Aggregate.scala b/src/dbc/scala/dbc/statement/expression/Aggregate.scala new file mode 100644 index 0000000000..3a93864475 --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/Aggregate.scala @@ -0,0 +1,34 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +abstract class Aggregate extends Expression { + + def aggregateName: String; + + def setFunction: SetFunction; + + def filterClause: Option[Expression]; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = ( + aggregateName + + "(" + setFunction.sqlString + ")" + + (filterClause match { + case None => "" + case Some(fc) => " FILTER (WHERE " + fc.sqlString + ")" + }) + ) + +} diff --git a/src/dbc/scala/dbc/statement/expression/BinaryOperator.scala b/src/dbc/scala/dbc/statement/expression/BinaryOperator.scala new file mode 100644 index 0000000000..3b3ac6f0fe --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/BinaryOperator.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +abstract class BinaryOperator extends Expression { + + /** The name of the operator. */ + def operator: String; + + /** The expression applied on the left of the operator. */ + def leftOperand: Expression; + + /** The expression applied on the right of the operator. */ + def rightOperand: Expression; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = { + leftOperand.sqlInnerString + " " + operator + " " + rightOperand.sqlInnerString + } + +} diff --git a/src/dbc/scala/dbc/statement/expression/Constant.scala b/src/dbc/scala/dbc/statement/expression/Constant.scala new file mode 100644 index 0000000000..6ee665c97d --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/Constant.scala @@ -0,0 +1,22 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +abstract class Constant extends Expression { + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = constantValue.sqlString; + + /** The value of the constant. */ + def constantValue: Value; +} diff --git a/src/dbc/scala/dbc/statement/expression/Default.scala b/src/dbc/scala/dbc/statement/expression/Default.scala new file mode 100644 index 0000000000..7d03872ab5 --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/Default.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +case object Default extends Expression { + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = "DEFAULT"; + +} diff --git a/src/dbc/scala/dbc/statement/expression/Field.scala b/src/dbc/scala/dbc/statement/expression/Field.scala new file mode 100644 index 0000000000..b005f024bd --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/Field.scala @@ -0,0 +1,39 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +abstract class Field extends Expression { + + /** The name of the schema in the database where the field is located. */ + def schemaName: Option[String] = None; + + /** The name of the table in the database where the field is located. */ + def tableName: Option[String]; + + /** The name of the field in the database. */ + def fieldName: String; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = ( + (schemaName match { + case None => "" + case Some(sn) => sn + "." + }) + + (tableName match { + case None => "" + case Some(tn) => tn + "." + }) + fieldName + ) + +} diff --git a/src/dbc/scala/dbc/statement/expression/FunctionCall.scala b/src/dbc/scala/dbc/statement/expression/FunctionCall.scala new file mode 100644 index 0000000000..bfee8701dd --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/FunctionCall.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +case class FunctionCall ( + functionName: String, + arguments: List[Expression] +) extends Expression { + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = { + functionName + "(" + arguments.mkString("",", ","") + ")" + } + + /** The name of the function to call. */ + //def functionName: String; + + /** A list of all argument expressions to pass to the function, in order. */ + //def arguments: List[Expression]; + +} diff --git a/src/dbc/scala/dbc/statement/expression/Select.scala b/src/dbc/scala/dbc/statement/expression/Select.scala new file mode 100644 index 0000000000..44c14e8474 --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/Select.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +abstract class Select extends Expression { + + /** The actual select statement */ + def selectStatement: statement.Select; + + /** A SQL-99 compliant string representation of the expression. */ + override def sqlString: String = selectStatement.sqlString; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = "("+selectStatement.sqlString+")"; + +} diff --git a/src/dbc/scala/dbc/statement/expression/SetFunction.scala b/src/dbc/scala/dbc/statement/expression/SetFunction.scala new file mode 100644 index 0000000000..b58850df12 --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/SetFunction.scala @@ -0,0 +1,39 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +abstract class SetFunction { + /** A SQL-99 compliant string representation of the set quantifier. */ + def sqlString: String; +} + +object SetFunction { + abstract class Asterisk extends SetFunction { + def sqlString = "(*)"; + } + abstract class General extends SetFunction { + def setQuantifier: Option[SetQuantifier]; + def valueExpression: Expression; + def sqlString = ( + "(" + + (setQuantifier match { + case None => "" + case Some(sq) => sq.sqlString + " " + }) + + valueExpression.sqlString + ")" + ); + } + abstract class Binary extends SetFunction { + def sqlString = error("Binary set function is not supported yet."); + } +} diff --git a/src/dbc/scala/dbc/statement/expression/TypeCast.scala b/src/dbc/scala/dbc/statement/expression/TypeCast.scala new file mode 100644 index 0000000000..5c9cbddebe --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/TypeCast.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +case class TypeCast ( + expression: Expression, + castType: DataType +) extends Expression { + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = { + "CAST (" + expression.sqlInnerString + " AS " + castType.sqlString + ")"; + } + + /** The expression that will be casted. */ + //def expression: Expression; + + /** The type to which to cast. */ + //def castType: scala.dbc.datatype.DataType; +} diff --git a/src/dbc/scala/dbc/statement/expression/UnaryOperator.scala b/src/dbc/scala/dbc/statement/expression/UnaryOperator.scala new file mode 100644 index 0000000000..9068595f77 --- /dev/null +++ b/src/dbc/scala/dbc/statement/expression/UnaryOperator.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.statement.expression; + + +abstract class UnaryOperator extends Expression { + + /** The name of the operator */ + def operator: String; + + /** Whether the operator comes before the operand or not. */ + def operatorIsLeft: Boolean; + + /** The operand applied to the operator. */ + def operand: Expression; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = operatorIsLeft match { + case true => operator + " " + operand.sqlInnerString; + case false => operand.sqlInnerString + " " + operator; + } +} diff --git a/src/dbc/scala/dbc/syntax/DataTypeUtil.scala b/src/dbc/scala/dbc/syntax/DataTypeUtil.scala new file mode 100644 index 0000000000..c42efb082f --- /dev/null +++ b/src/dbc/scala/dbc/syntax/DataTypeUtil.scala @@ -0,0 +1,98 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.syntax; + + +import java.math.BigDecimal; +import java.math.BigInteger; + +object DataTypeUtil { + + final val java_lang_Integer_SIZE = 32; + final val java_lang_Long_SIZE = 64; + + def boolean = new datatype.Boolean; + def tinyint = new datatype.ExactNumeric[Byte](dbc.DataType.BYTE) { + val precisionRadix = 2; + val precision = 8; + val signed = true; + val scale = 0; + } + def smallint = new datatype.ExactNumeric[Short](dbc.DataType.SHORT) { + val precisionRadix = 2; + val precision = 16; + val signed = true; + val scale = 0; + } + def integer = new datatype.ExactNumeric[Int](dbc.DataType.INT) { + val precisionRadix = 2; + val precision = 32; + val signed = true; + val scale = 0; + } + def bigint = new datatype.ExactNumeric[Long](dbc.DataType.LONG) { + val precisionRadix = 2; + val precision = 64; + val signed = true; + val scale = 0; + } + def numeric (_precision:Int): DataType = numeric(_precision,0); + def numeric (_precision:Int, _scale:Int): DataType = + Pair(datatype.Factory.bytePrecision(_precision,true,true),_scale == 0) match { + case Pair(bp,true) if (bp <= java_lang_Integer_SIZE) => + new datatype.ExactNumeric[Int](DataType.INT) { + val precisionRadix = 10; + val precision = _precision; + val signed = true; + val scale = 0; + } + case Pair(bp,true) if (bp <= java_lang_Long_SIZE) => + new datatype.ExactNumeric[Long](DataType.LONG) { + val precisionRadix = 10; + val precision = _precision; + val signed = true; + val scale = 0; + } + case Pair(_,true) => + new datatype.ExactNumeric[BigInteger](DataType.BIG_INTEGER) { + val precisionRadix = 10; + val precision = _precision; + val signed = true; + val scale = 0; + } + case Pair(_,false) => + new datatype.ExactNumeric[BigDecimal](DataType.BIG_DECIMAL) { + val precisionRadix = 10; + val precision = _precision; + val signed = true; + val scale = _scale; + } + } + def real = new datatype.ApproximateNumeric[Float](DataType.FLOAT) { + val precisionRadix = 2; + val precision = 64; + val signed = true; + } + def doublePrecision = new datatype.ApproximateNumeric[Double](DataType.DOUBLE) { + val precisionRadix = 2; + val precision = 128; + val signed = true; + } + def character (_length: Int) = new datatype.Character { + val length = _length; + } + def characterVarying (_length: Int) = new datatype.CharacterVarying { + def length = _length; + } + def characterLargeObject = new datatype.CharacterLargeObject; + +} diff --git a/src/dbc/scala/dbc/syntax/Database.scala b/src/dbc/scala/dbc/syntax/Database.scala new file mode 100644 index 0000000000..31a04b1129 --- /dev/null +++ b/src/dbc/scala/dbc/syntax/Database.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.syntax; + + +import java.net.URI; + +object Database { + + def database (server:String, username:String, password:String): dbc.Database = { + val uri = new URI(server); + // Java 1.5 if (uri.toString().contains("postgres")) { + if (uri.toString().indexOf("postgres") != -1) { + new dbc.Database(new vendor.PostgreSQL { + val uri = new URI(server); + val user = username; + val pass = password; + }) + } else { + throw new Exception("No DBMS vendor support could be found for the given URI"); + } + } + +} diff --git a/src/dbc/scala/dbc/syntax/Statement.scala b/src/dbc/scala/dbc/syntax/Statement.scala new file mode 100644 index 0000000000..027a05b8fc --- /dev/null +++ b/src/dbc/scala/dbc/syntax/Statement.scala @@ -0,0 +1,274 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.syntax; + + +import java.math.BigDecimal; +import java.math.BigInteger; + +import StatementExpression._; + +/* + +ASSUMPTIONS: + +IMPROVABLE: +For type safety, all types must be defined. If one is missing, none is taken into account. +It is possible to redefine many types or renamings for a field, in that case, + only the last one is taken into account ("a" as "b" as "c" of boolean as "e" of integer + is equivalent to "a" as "e" of integer). + +FIXED: + +*/ + +object Statement { + + // SELECT ZYGOTE ... + + def select: SelectZygote = new SelectZygote { + val setQuantifier = None; + } + def selectBag: SelectZygote = new SelectZygote { + val setQuantifier = Some(statement.SetQuantifier.AllTuples); + } + def selectSet: SelectZygote = new SelectZygote { + val setQuantifier = Some(statement.SetQuantifier.DistinctTuples); + } + + abstract class SelectZygote { + def setQuantifier: Option[statement.SetQuantifier]; + def fields (sdc:SelectDerivedColumns): SelectOf = new SelectOf { + val setQuantifier = SelectZygote.this.setQuantifier; + val selectList = sdc.selectList; + val selectTypes = sdc.selectTypes; + } + } + + abstract class SelectDerivedField { + def fieldValue: StatementField; + def fieldRename: Option[String] = {val x = None; x} + def fieldType: Option[dbc.DataType] = {val x = None; x} + def as (rename:String): SelectDerivedField = new SelectDerivedField { + val fieldValue = SelectDerivedField.this.fieldValue; + override val fieldRename = Some(rename); + override val fieldType = SelectDerivedField.this.fieldType; + } + def of (datatype:dbc.DataType): SelectDerivedField = new SelectDerivedField { + val fieldValue = SelectDerivedField.this.fieldValue; + override val fieldRename = SelectDerivedField.this.fieldRename; + override val fieldType = Some(datatype); + } + } + + implicit def statementFieldToSelectDerivedField (fv:StatementField): SelectDerivedField = new SelectDerivedField { + val fieldValue = fv; + } + + implicit def stringToSelectDerivedField (fv:String): SelectDerivedField = new SelectDerivedField { + val fieldValue: StatementField = StatementExpression.stringToStatementField(fv); + } + + abstract class SelectDerivedColumns { + def selectList: List[statement.DerivedColumn]; + def selectTypes: List[DataType]; + def and (sdc:SelectDerivedColumns): SelectDerivedColumns = new SelectDerivedColumns { + val selectList = SelectDerivedColumns.this.selectList ::: sdc.selectList; + val selectTypes = + if (SelectDerivedColumns.this.selectTypes.isEmpty | sdc.selectTypes.isEmpty) + Nil + else + SelectDerivedColumns.this.selectTypes ::: sdc.selectTypes; + } + } + + implicit def selectDerivedFieldToSelectDerivedColumns (sdf:SelectDerivedField): SelectDerivedColumns = new SelectDerivedColumns { + val selectList = List(new statement.DerivedColumn { + val valueExpression = sdf.fieldValue.toStatement; + val asClause = sdf.fieldRename; + }); + val selectTypes = if (sdf.fieldType.isEmpty) Nil else List(sdf.fieldType.get); + } + + implicit def stringToSelectDerivedColumns (sdfs:String): SelectDerivedColumns = { + val sdf: SelectDerivedField = sdfs; + selectDerivedFieldToSelectDerivedColumns(sdf); + } + + // SELECT OF ... + + abstract class SelectOf { + def setQuantifier: Option[statement.SetQuantifier]; + def selectList: List[statement.DerivedColumn]; + def selectTypes: List[DataType]; + def from (sst:SelectSourceTables): SelectBeyond = new SelectBeyond { + val setQuantifier = SelectOf.this.setQuantifier; + val selectList = SelectOf.this.selectList; + val selectTypes = SelectOf.this.selectTypes; + val fromClause = sst.fromClause; + val whereClause = None; + val groupByClause = None; + val havingClause = None; + } + } + + abstract class SelectSourceTable { + def fromRelation: statement.Relation; + def innerJoin (sst: SelectSourceTable): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Jointure { + val leftRelation = SelectSourceTable.this.fromRelation; + val rightRelation = sst.fromRelation; + val joinType = statement.JoinType.Inner; + val joinCondition = None; + val fieldTypes = leftRelation.fieldTypes ::: rightRelation.fieldTypes; + } + } + def leftOuterJoin (sst: SelectSourceTable): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Jointure { + val leftRelation = SelectSourceTable.this.fromRelation; + val rightRelation = sst.fromRelation; + val joinType = statement.JoinType.Outer.Left; + val joinCondition = None; + val fieldTypes = leftRelation.fieldTypes ::: rightRelation.fieldTypes; + } + } + def rightOuterJoin (sst: SelectSourceTable): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Jointure { + val leftRelation = SelectSourceTable.this.fromRelation; + val rightRelation = sst.fromRelation; + val joinType = statement.JoinType.Outer.Right; + val joinCondition = None; + val fieldTypes = leftRelation.fieldTypes ::: rightRelation.fieldTypes; + } + } + def fullOuterJoin (sst: SelectSourceTable): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Jointure { + val leftRelation = SelectSourceTable.this.fromRelation; + val rightRelation = sst.fromRelation; + val joinType = statement.JoinType.Outer.Full; + val joinCondition = None; + val fieldTypes = leftRelation.fieldTypes ::: rightRelation.fieldTypes; + } + } + } + + implicit def stringToSelectSourceTable (sct:String): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Table { + val tableName = sct; + val tableRename = None; + val fieldTypes = Nil; + } + } + + implicit def selectToSelectSourceTable (sct:statement.Select): SelectSourceTable = new SelectSourceTable { + val fromRelation = sct; + } + + abstract class SelectSourceTables { + def fromClause: List[statement.Relation]; + def join (sct:SelectSourceTable): SelectSourceTables = new SelectSourceTables { + val fromClause = SelectSourceTables.this.fromClause ::: List(sct.fromRelation); + } + } + + implicit def stringToSelectSourceTables (sct:String): SelectSourceTables = new SelectSourceTables { + val fromClause = List(new statement.Table { + val tableName = sct; + val tableRename = None; + val fieldTypes = Nil; + }); + } + + implicit def selectToSelectSourceTables (sct:statement.Select): SelectSourceTables = new SelectSourceTables { + val fromClause = List(sct); + } + + implicit def selectSourceTableToSelectSourceTables (sct:SelectSourceTable): SelectSourceTables = new SelectSourceTables { + val fromClause = List(sct.fromRelation); + } + + // SELECT BEYOND ... + + abstract class SelectBeyond { + def setQuantifier: Option[statement.SetQuantifier]; + def selectList: List[statement.DerivedColumn]; + def selectTypes: List[DataType]; + def fromClause: List[statement.Relation]; + def whereClause: Option[statement.Expression]; + def groupByClause: Option[List[statement.Expression]]; + def havingClause: Option[statement.Expression]; + def where (se:StatementExpression): SelectBeyond = new SelectBeyond { + val setQuantifier = SelectBeyond.this.setQuantifier; + val selectList = SelectBeyond.this.selectList; + val selectTypes = SelectBeyond.this.selectTypes; + val fromClause = SelectBeyond.this.fromClause; + val whereClause = Some(se.toStatement); + val groupByClause = SelectBeyond.this.groupByClause; + val havingClause = SelectBeyond.this.havingClause; + } + def groupBy (sgb:SelectGroupBy): SelectBeyond = new SelectBeyond { + val setQuantifier = SelectBeyond.this.setQuantifier; + val selectList = SelectBeyond.this.selectList; + val selectTypes = SelectBeyond.this.selectTypes; + val fromClause = SelectBeyond.this.fromClause; + val whereClause = SelectBeyond.this.whereClause; + val groupByClause = Some(sgb.groupByClause); + val havingClause = SelectBeyond.this.havingClause; + } + def having (se:StatementExpression): SelectBeyond = new SelectBeyond { + val setQuantifier = SelectBeyond.this.setQuantifier; + val selectList = SelectBeyond.this.selectList; + val selectTypes = SelectBeyond.this.selectTypes; + val fromClause = SelectBeyond.this.fromClause; + val whereClause = SelectBeyond.this.whereClause; + val groupByClause = SelectBeyond.this.groupByClause; + val havingClause = Some(se.toStatement); + } + } + + implicit def selectBeyondToStatementSelect (sb:SelectBeyond): statement.Select = new statement.Select { + val setQuantifier = sb.setQuantifier; + val selectList = sb.selectList; + val fromClause = sb.fromClause; + val whereClause = sb.whereClause; + val groupByClause = sb.groupByClause; + val havingClause = sb.havingClause; + val fieldTypes = sb.selectTypes; + } + + abstract class SelectGroupBy { + def groupByClause: List[statement.Expression]; + def then (se:StatementExpression): SelectGroupBy = new SelectGroupBy { + val groupByClause = + SelectGroupBy.this.groupByClause ::: List(se.toStatement); + } + def then (se:String): SelectGroupBy = new SelectGroupBy { + val groupByClause = + SelectGroupBy.this.groupByClause ::: List(new statement.expression.Field { + val tableName = None; + val fieldName = se; + }); + } + } + + implicit def statementExpressionToSelectGroupBy (se:StatementExpression): SelectGroupBy = new SelectGroupBy { + val groupByClause = List(se.toStatement); + } + + implicit def stringToSelectGroupBy (se:String): SelectGroupBy = new SelectGroupBy { + val groupByClause = List(new statement.expression.Field { + val tableName = None; + val fieldName = se; + }); + } + +} diff --git a/src/dbc/scala/dbc/syntax/StatementExpression.scala b/src/dbc/scala/dbc/syntax/StatementExpression.scala new file mode 100644 index 0000000000..78c75ae174 --- /dev/null +++ b/src/dbc/scala/dbc/syntax/StatementExpression.scala @@ -0,0 +1,221 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.syntax; + + +import java.math.BigDecimal; +import java.math.BigInteger; + +abstract class StatementExpression { + + def toStatement: statement.Expression; + + def and (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "AND"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def or (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "OR"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def == (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "="; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def < (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "<"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def > (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = ">"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def <= (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "<="; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def >= (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = ">="; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def <> (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "<>"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def isNull: StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "IS NULL"; + val operatorIsLeft = false; + val operand = StatementExpression.this.toStatement; + } + } + def isNotNull: StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "IS NOT NULL"; + val operatorIsLeft = false; + val operand = StatementExpression.this.toStatement; + } + } + def + (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "+"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def - (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "-"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def * (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "*"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def / (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "/"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def % (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "%"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def ^ (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "^"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def not : StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "!"; + val operatorIsLeft = false; + val operand = StatementExpression.this.toStatement; + } + } + def || (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "||"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def like (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "LIKE"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def similar (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "SIMILAR"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def in (se:statement.Select): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "IN"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = new statement.expression.Select { + val selectStatement = se; + }; + } + } + +} + +object StatementExpression { + + def not (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "NOT"; + val operatorIsLeft = true; + val operand = se.toStatement; + } + } + def abs (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "@"; + val operatorIsLeft = true; + val operand = se.toStatement; + } + } + def exists (se:statement.Select): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "EXISTS"; + val operatorIsLeft = true; + val operand = new statement.expression.Select { + val selectStatement = se; + }; + } + } + + abstract class StatementField extends StatementExpression { + def fieldName: String; + def tableName: Option[String] = None; + def in (tn:String): StatementField = new StatementField { + val fieldName = StatementField.this.fieldName; + override val tableName = Some(tn); + } + def toStatement: statement.expression.Field = new statement.expression.Field { + override val schemaName = None; + val tableName = StatementField.this.tableName; + val fieldName = StatementField.this.fieldName; + } + } + + implicit def stringToStatementField (ef:String): StatementField = new StatementField { + val fieldName = ef; + } + + + + +} diff --git a/src/dbc/scala/dbc/value/ApproximateNumeric.scala b/src/dbc/scala/dbc/value/ApproximateNumeric.scala new file mode 100644 index 0000000000..5da4fec640 --- /dev/null +++ b/src/dbc/scala/dbc/value/ApproximateNumeric.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +abstract class ApproximateNumeric [Type] extends Value { + + val dataType: datatype.ApproximateNumeric[Type]; + + def sqlString = nativeValue.toString(); + + } + +object ApproximateNumeric { + + implicit def approximateNumericToFloar (obj:value.ApproximateNumeric[Float]): Float = obj.nativeValue; + implicit def approximateNumericToDouble (obj:value.ApproximateNumeric[Double]): Double = obj.nativeValue; + +} diff --git a/src/dbc/scala/dbc/value/Boolean.scala b/src/dbc/scala/dbc/value/Boolean.scala new file mode 100644 index 0000000000..fba78dcfd1 --- /dev/null +++ b/src/dbc/scala/dbc/value/Boolean.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +abstract class Boolean extends Value { + + val dataType: datatype.Boolean; + + def sqlString = if (nativeValue) "TRUE" else "FALSE"; + +} + +object Boolean { + + implicit def booleanToBoolean (obj:value.Boolean): scala.Boolean = obj.nativeValue; + +} diff --git a/src/dbc/scala/dbc/value/Character.scala b/src/dbc/scala/dbc/value/Character.scala new file mode 100644 index 0000000000..c02a21b28e --- /dev/null +++ b/src/dbc/scala/dbc/value/Character.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +/** A SQL-99 value of type character string. */ +abstract class Character extends Value { + + override val dataType: datatype.Character; + + /** An SQL-99 compliant string representation of the value. */ + def sqlString: String = { + "'" + nativeValue + "'" + } + +} + +/** An object offering transformation methods (views) on the value. + * This object must be visible in an expression to use value auto- + * conversion. */ +object Character { + + /** A character string value as a native string. */ + implicit def characterToString (obj:value.Character): String = obj.nativeValue; + +} diff --git a/src/dbc/scala/dbc/value/CharacterLargeObject.scala b/src/dbc/scala/dbc/value/CharacterLargeObject.scala new file mode 100644 index 0000000000..61f55f868a --- /dev/null +++ b/src/dbc/scala/dbc/value/CharacterLargeObject.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +/** A SQL-99 value of type character large object. */ +abstract class CharacterLargeObject extends Value { + + override val dataType: datatype.CharacterLargeObject; + + /** An SQL-99 compliant string representation of the value. */ + def sqlString: String = { + "'" + nativeValue + "'" + } + +} + +/** An object offering transformation methods (views) on the value. + * This object must be visible in an expression to use value auto- + * conversion. */ +object CharacterLargeObject { + + /** A character large object value as a native string. */ + implicit def characterLargeObjectToString (obj:value.CharacterLargeObject): String = obj.nativeValue; + +} diff --git a/src/dbc/scala/dbc/value/CharacterVarying.scala b/src/dbc/scala/dbc/value/CharacterVarying.scala new file mode 100644 index 0000000000..177dbdba5b --- /dev/null +++ b/src/dbc/scala/dbc/value/CharacterVarying.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +/** A SQL-99 value of type character varying string. */ +abstract class CharacterVarying extends Value { + + override val dataType: datatype.CharacterVarying; + + /** An SQL-99 compliant string representation of the value. */ + def sqlString: String = { + "'" + nativeValue + "'" + } + +} + +/** An object offering transformation methods (views) on the value. + * This object must be visible in an expression to use value auto- + * conversion. */ +object CharacterVarying { + + /** A character varying string value as a native string. */ + implicit def characterVaryingToString (obj:value.CharacterVarying): String = obj.nativeValue; + +} diff --git a/src/dbc/scala/dbc/value/Conversion.scala b/src/dbc/scala/dbc/value/Conversion.scala new file mode 100644 index 0000000000..093cf55902 --- /dev/null +++ b/src/dbc/scala/dbc/value/Conversion.scala @@ -0,0 +1,157 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +import java.math._; + +object Conversion { + + class Illegal (msg:String) extends Exception(msg); + + implicit def view1 (value:Value): Byte = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to byte: "+value) + } + } + + implicit def view2 (value:Value): Short = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to short: "+value) + } + } + + implicit def view3 (value:Value): Int = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.INT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Int]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to int: "+value) + } + } + + implicit def view4 (value:Value): Long = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.INT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Int]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.LONG) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Long]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to long: "+value) + } + } + + implicit def view5 (value:Value): BigInteger = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + new BigInteger(v.nativeValue.toString(),10) + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + new BigInteger(v.nativeValue.toString(),10) + } else if (value.dataType.nativeTypeId == DataType.INT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Int]]; + new BigInteger(v.nativeValue.toString(),10) + } else if (value.dataType.nativeTypeId == DataType.LONG) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Long]]; + new BigInteger(v.nativeValue.toString(),10) + } else if (value.dataType.nativeTypeId == DataType.BIG_INTEGER) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[BigInteger]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to big integer: "+value) + } + } + + implicit def view6 (value:Value): BigDecimal = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + new BigDecimal(v.nativeValue.toString()) + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + new BigDecimal(v.nativeValue.toString()) + } else if (value.dataType.nativeTypeId == DataType.INT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Int]]; + new BigDecimal(v.nativeValue.toString()) + } else if (value.dataType.nativeTypeId == DataType.LONG) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Long]]; + new BigDecimal(v.nativeValue.toString()) + } else if (value.dataType.nativeTypeId == DataType.BIG_INTEGER) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[BigInteger]]; + new BigDecimal(v.nativeValue) + } else if (value.dataType.nativeTypeId == DataType.BIG_DECIMAL) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[BigDecimal]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to big decimal: "+value) + } + } + + implicit def view7 (value:Value): Float = { + if (value.dataType.nativeTypeId == DataType.FLOAT) { + val v = value.asInstanceOf[dbc.value.ApproximateNumeric[Float]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to float: "+value) + } + } + + implicit def view8 (value:Value): Double = { + if (value.dataType.nativeTypeId == DataType.FLOAT) { + val v = value.asInstanceOf[dbc.value.ApproximateNumeric[Float]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.DOUBLE) { + val v = value.asInstanceOf[dbc.value.ApproximateNumeric[Double]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to double: "+value) + } + } + + implicit def view9 (value:Value): scala.Boolean = { + if (value.dataType.nativeTypeId == DataType.BOOLEAN) { + val v = value.asInstanceOf[dbc.value.Boolean]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to boolean: "+value) + } + } + + implicit def view10 (value:Value): String = value match { + case v:dbc.value.Character => v.nativeValue; + case v:dbc.value.CharacterLargeObject => v.nativeValue; + case v:dbc.value.CharacterVarying => v.nativeValue; + case _ => throw new Illegal("Cannot convert value to string") + } + +} diff --git a/src/dbc/scala/dbc/value/ExactNumeric.scala b/src/dbc/scala/dbc/value/ExactNumeric.scala new file mode 100644 index 0000000000..1c30c89c29 --- /dev/null +++ b/src/dbc/scala/dbc/value/ExactNumeric.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +import java.math.BigInteger; +import java.math.BigDecimal; + +abstract class ExactNumeric [Type] extends Value { + + val dataType: datatype.ExactNumeric[Type]; + + def sqlString = nativeValue.toString(); + +} + +object ExactNumeric { + + implicit def exactNumericToByte (obj:value.ExactNumeric[Byte]): Byte = obj.nativeValue; + implicit def exactNumericToShort (obj:value.ExactNumeric[Short]): Short = obj.nativeValue; + implicit def exactNumericToInt (obj:value.ExactNumeric[Int]): Int = obj.nativeValue; + implicit def exactNumericToLong (obj:value.ExactNumeric[Long]): Long = obj.nativeValue; + implicit def exactNumericToBigInteger (obj:value.ExactNumeric[BigInteger]): BigInteger = obj.nativeValue; + implicit def exactNumericToBigDecimal (obj:value.ExactNumeric[BigDecimal]): BigDecimal = obj.nativeValue; + +} diff --git a/src/dbc/scala/dbc/value/Factory.scala b/src/dbc/scala/dbc/value/Factory.scala new file mode 100644 index 0000000000..34975937e7 --- /dev/null +++ b/src/dbc/scala/dbc/value/Factory.scala @@ -0,0 +1,95 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +import java.math.BigInteger; +import java.math.BigDecimal; + +object Factory { + + def create (result: java.sql.ResultSet, index: Int, expectedDataType: DataType): Value = { + expectedDataType.nativeTypeId match { + case DataType.OBJECT => + new value.Unknown { + val dataType = expectedDataType.asInstanceOf[datatype.Unknown]; + val nativeValue: Object = result.getObject(index); + } + case DataType.STRING => { + expectedDataType match { + case t:datatype.Character => + new value.Character { + val dataType = t; + val nativeValue: String = result.getString(index); + } + case t:datatype.CharacterVarying => + new value.CharacterVarying { + val dataType = t; + val nativeValue: String = result.getString(index); + } + case t:datatype.CharacterLargeObject => + new value.CharacterLargeObject { + val dataType = t; + val nativeValue: String = result.getString(index); + } + } + } + case DataType.BOOLEAN => + new value.Boolean { + val dataType = expectedDataType.asInstanceOf[datatype.Boolean]; + val nativeValue: scala.Boolean = result.getBoolean(index); + } + case DataType.FLOAT => + new value.ApproximateNumeric[Float] { + val dataType = expectedDataType.asInstanceOf[datatype.ApproximateNumeric[Float]]; + val nativeValue: Float = result.getFloat(index); + } + case DataType.DOUBLE => + new value.ApproximateNumeric[Double] { + val dataType = expectedDataType.asInstanceOf[datatype.ApproximateNumeric[Double]]; + val nativeValue: Double = result.getDouble(index); + } + case DataType.BYTE => + new value.ExactNumeric[Byte] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[Byte]]; + val nativeValue: Byte = result.getByte(index); + } + case DataType.SHORT => + new value.ExactNumeric[Short] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[Short]]; + val nativeValue: Short = result.getShort(index); + } + case DataType.INT => + new value.ExactNumeric[Int] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[Int]]; + val nativeValue: Int = result.getInt(index); + } + case DataType.LONG => + new value.ExactNumeric[Long] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[Long]]; + val nativeValue:Long = result.getLong(index); + } + case DataType.BIG_INTEGER => + new value.ExactNumeric[BigInteger] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[BigInteger]]; + val nativeValue: BigInteger = result.getBigDecimal(index).unscaledValue(); + } + case DataType.BIG_DECIMAL => + new value.ExactNumeric[BigDecimal] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[BigDecimal]]; + val nativeValue: BigDecimal = result.getBigDecimal(index); + } + + } + } + +} diff --git a/src/dbc/scala/dbc/value/Unknown.scala b/src/dbc/scala/dbc/value/Unknown.scala new file mode 100644 index 0000000000..b1ba51d064 --- /dev/null +++ b/src/dbc/scala/dbc/value/Unknown.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.value; + + +abstract class Unknown extends Value { + + val dataType: datatype.Unknown; + + def sqlString = error ("An 'ANY' value cannot be represented."); + +} + +object UnknownType { + + def view (obj:value.Unknown): Object = obj.nativeValue; + +} diff --git a/src/dbc/scala/dbc/vendor/PostgreSQL.scala b/src/dbc/scala/dbc/vendor/PostgreSQL.scala new file mode 100644 index 0000000000..f637b32a82 --- /dev/null +++ b/src/dbc/scala/dbc/vendor/PostgreSQL.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2006, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.dbc.vendor; + + +abstract class PostgreSQL extends Vendor { + + def uri:java.net.URI; + def user:String; + def pass:String; + + val retainedConnections = 5; + + val nativeDriverClass = Class.forName("org.postgresql.Driver"); + + val urlProtocolString = "jdbc:postgresql:" + +} |