diff options
author | Burak Emir <emir@epfl.ch> | 2005-11-15 11:44:40 +0000 |
---|---|---|
committer | Burak Emir <emir@epfl.ch> | 2005-11-15 11:44:40 +0000 |
commit | 87f227fedd7374d7513c971cbdedbc2efe7ed53c (patch) | |
tree | 9dc96358722c774a79b63332347f645ae19ec9c7 /sources | |
parent | 44ec9c5d1eb9c1ce4c6ba231dee91adc0dc42e21 (diff) | |
download | scala-87f227fedd7374d7513c971cbdedbc2efe7ed53c.tar.gz scala-87f227fedd7374d7513c971cbdedbc2efe7ed53c.tar.bz2 scala-87f227fedd7374d7513c971cbdedbc2efe7ed53c.zip |
Gilles "scala database connectivity" files added
Diffstat (limited to 'sources')
65 files changed, 3128 insertions, 0 deletions
diff --git a/sources/scala/dbc/DataType.scala b/sources/scala/dbc/DataType.scala new file mode 100644 index 0000000000..b8cdb657a8 --- /dev/null +++ b/sources/scala/dbc/DataType.scala @@ -0,0 +1,58 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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 A is said to be subtype of type B if any value of type A + * can be represented as a value of type B. */ + 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/sources/scala/dbc/Database.scala b/sources/scala/dbc/Database.scala new file mode 100644 index 0000000000..7eb235fa01 --- /dev/null +++ b/sources/scala/dbc/Database.scala @@ -0,0 +1,158 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/Syntax.scala b/sources/scala/dbc/Syntax.scala new file mode 100644 index 0000000000..93dec0232b --- /dev/null +++ b/sources/scala/dbc/Syntax.scala @@ -0,0 +1,39 @@ +/* __ *\ +** ________ ___ / / ___ Scala API tests ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc; + +import java.math.BigDecimal; +import java.math.BigInteger; + +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 numeric (precision:Int) = DataTypeUtil.numeric(precision); + def numeric (precision:Int, scale:Int) = DataTypeUtil.numeric(precision, scale); + def real = DataTypeUtil.real; + 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/sources/scala/dbc/Utilities.scala b/sources/scala/dbc/Utilities.scala new file mode 100644 index 0000000000..b7bc4f1ad0 --- /dev/null +++ b/sources/scala/dbc/Utilities.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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 { + + def view (obj:statement.expression.Constant): Value = + obj.constantValue; + + def view (obj:Value): statement.expression.Constant = + new statement.expression.Constant { + val constantValue = obj; + } + +} diff --git a/sources/scala/dbc/Value.scala b/sources/scala/dbc/Value.scala new file mode 100644 index 0000000000..3ab2d0cbe5 --- /dev/null +++ b/sources/scala/dbc/Value.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/Vendor.scala b/sources/scala/dbc/Vendor.scala new file mode 100644 index 0000000000..5e2b754263 --- /dev/null +++ b/sources/scala/dbc/Vendor.scala @@ -0,0 +1,34 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc; + +import java.sql._; + +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 = { + nativeDriverClass.newInstance().asInstanceOf[java.sql.Driver].connect(uri.toString(),nativeProperties) + } + + def urlProtocolString: String; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/datatype/ApproximateNumeric.scala b/sources/scala/dbc/datatype/ApproximateNumeric.scala new file mode 100644 index 0000000000..be20ed8d72 --- /dev/null +++ b/sources/scala/dbc/datatype/ApproximateNumeric.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A type category for all SQL types that store varying-precision + * numbers. */ +abstract class ApproximateNumeric [Type] ( + 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/sources/scala/dbc/datatype/Boolean.scala b/sources/scala/dbc/datatype/Boolean.scala new file mode 100644 index 0000000000..7c68ab9c7d --- /dev/null +++ b/sources/scala/dbc/datatype/Boolean.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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"; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/datatype/Character.scala b/sources/scala/dbc/datatype/Character.scala new file mode 100644 index 0000000000..4e2c461bc6 --- /dev/null +++ b/sources/scala/dbc/datatype/Character.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API tests ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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() + ")"; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/datatype/CharacterLargeObject.scala b/sources/scala/dbc/datatype/CharacterLargeObject.scala new file mode 100644 index 0000000000..adbd979f6e --- /dev/null +++ b/sources/scala/dbc/datatype/CharacterLargeObject.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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"; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/datatype/CharacterString.scala b/sources/scala/dbc/datatype/CharacterString.scala new file mode 100644 index 0000000000..e937d10b9e --- /dev/null +++ b/sources/scala/dbc/datatype/CharacterString.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/datatype/CharacterVarying.scala b/sources/scala/dbc/datatype/CharacterVarying.scala new file mode 100644 index 0000000000..f39eab9b79 --- /dev/null +++ b/sources/scala/dbc/datatype/CharacterVarying.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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() + ")"; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/datatype/ExactNumeric.scala b/sources/scala/dbc/datatype/ExactNumeric.scala new file mode 100644 index 0000000000..b2637f944f --- /dev/null +++ b/sources/scala/dbc/datatype/ExactNumeric.scala @@ -0,0 +1,59 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A type category for all SQL types that store constant-precision + * numbers. */ +abstract class ExactNumeric [Type] ( + 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/sources/scala/dbc/datatype/Factory.scala b/sources/scala/dbc/datatype/Factory.scala new file mode 100644 index 0000000000..13d9cd35e2 --- /dev/null +++ b/sources/scala/dbc/datatype/Factory.scala @@ -0,0 +1,246 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/datatype/Numeric.scala b/sources/scala/dbc/datatype/Numeric.scala new file mode 100644 index 0000000000..b75bfe8b15 --- /dev/null +++ b/sources/scala/dbc/datatype/Numeric.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/datatype/String.scala b/sources/scala/dbc/datatype/String.scala new file mode 100644 index 0000000000..b499a104b6 --- /dev/null +++ b/sources/scala/dbc/datatype/String.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/datatype/Unknown.scala b/sources/scala/dbc/datatype/Unknown.scala new file mode 100644 index 0000000000..b081881cad --- /dev/null +++ b/sources/scala/dbc/datatype/Unknown.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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."); + +}
\ No newline at end of file diff --git a/sources/scala/dbc/exception/IncompatibleSchema.scala b/sources/scala/dbc/exception/IncompatibleSchema.scala new file mode 100644 index 0000000000..6095fbec82 --- /dev/null +++ b/sources/scala/dbc/exception/IncompatibleSchema.scala @@ -0,0 +1,15 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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;
\ No newline at end of file diff --git a/sources/scala/dbc/exception/UnsupportedFeature.scala b/sources/scala/dbc/exception/UnsupportedFeature.scala new file mode 100644 index 0000000000..9fbaed81d7 --- /dev/null +++ b/sources/scala/dbc/exception/UnsupportedFeature.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.exception; + +/** A type category for all SQL types that store constant-precision numbers. */ +case class UnsupportedFeature (msg: String) extends Exception;
\ No newline at end of file diff --git a/sources/scala/dbc/result/Field.scala b/sources/scala/dbc/result/Field.scala new file mode 100644 index 0000000000..af129d18b7 --- /dev/null +++ b/sources/scala/dbc/result/Field.scala @@ -0,0 +1,60 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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 view (field:Field): Value = field.content; + + + +} diff --git a/sources/scala/dbc/result/FieldMetadata.scala b/sources/scala/dbc/result/FieldMetadata.scala new file mode 100644 index 0000000000..97c5bdb3a3 --- /dev/null +++ b/sources/scala/dbc/result/FieldMetadata.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/result/Relation.scala b/sources/scala/dbc/result/Relation.scala new file mode 100644 index 0000000000..ff8a17ea26 --- /dev/null +++ b/sources/scala/dbc/result/Relation.scala @@ -0,0 +1,68 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.result; + +/** An ISO-9075:2003 (SQL) table. This is equivalent to a relation in the + * relational model. */ +abstract class Relation 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/sources/scala/dbc/result/Status.scala b/sources/scala/dbc/result/Status.scala new file mode 100644 index 0000000000..56d83d4ae6 --- /dev/null +++ b/sources/scala/dbc/result/Status.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/result/Tuple.scala b/sources/scala/dbc/result/Tuple.scala new file mode 100644 index 0000000000..41c2f1f6d1 --- /dev/null +++ b/sources/scala/dbc/result/Tuple.scala @@ -0,0 +1,38 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/AccessMode.scala b/sources/scala/dbc/statement/AccessMode.scala new file mode 100644 index 0000000000..815f8b3c37 --- /dev/null +++ b/sources/scala/dbc/statement/AccessMode.scala @@ -0,0 +1,22 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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" + } +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/DerivedColumn.scala b/sources/scala/dbc/statement/DerivedColumn.scala new file mode 100644 index 0000000000..3948807885 --- /dev/null +++ b/sources/scala/dbc/statement/DerivedColumn.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/Expression.scala b/sources/scala/dbc/statement/Expression.scala new file mode 100644 index 0000000000..51cada90bb --- /dev/null +++ b/sources/scala/dbc/statement/Expression.scala @@ -0,0 +1,25 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/Insert.scala b/sources/scala/dbc/statement/Insert.scala new file mode 100644 index 0000000000..b4302b9c19 --- /dev/null +++ b/sources/scala/dbc/statement/Insert.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/InsertionData.scala b/sources/scala/dbc/statement/InsertionData.scala new file mode 100644 index 0000000000..c814793387 --- /dev/null +++ b/sources/scala/dbc/statement/InsertionData.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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(" (",", ",")") + } + } +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/IsolationLevel.scala b/sources/scala/dbc/statement/IsolationLevel.scala new file mode 100644 index 0000000000..f63ebe39e8 --- /dev/null +++ b/sources/scala/dbc/statement/IsolationLevel.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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" + } +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/JoinType.scala b/sources/scala/dbc/statement/JoinType.scala new file mode 100644 index 0000000000..60d14cf968 --- /dev/null +++ b/sources/scala/dbc/statement/JoinType.scala @@ -0,0 +1,45 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/Jointure.scala b/sources/scala/dbc/statement/Jointure.scala new file mode 100644 index 0000000000..85961a857f --- /dev/null +++ b/sources/scala/dbc/statement/Jointure.scala @@ -0,0 +1,43 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/Relation.scala b/sources/scala/dbc/statement/Relation.scala new file mode 100644 index 0000000000..1e8aaee8be --- /dev/null +++ b/sources/scala/dbc/statement/Relation.scala @@ -0,0 +1,53 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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 = { + if (typeCheck != Nil) { + 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/sources/scala/dbc/statement/Select.scala b/sources/scala/dbc/statement/Select.scala new file mode 100644 index 0000000000..b39ec8a3e5 --- /dev/null +++ b/sources/scala/dbc/statement/Select.scala @@ -0,0 +1,89 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/SetClause.scala b/sources/scala/dbc/statement/SetClause.scala new file mode 100644 index 0000000000..292879d3d0 --- /dev/null +++ b/sources/scala/dbc/statement/SetClause.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/SetQuantifier.scala b/sources/scala/dbc/statement/SetQuantifier.scala new file mode 100644 index 0000000000..a2fbbb854a --- /dev/null +++ b/sources/scala/dbc/statement/SetQuantifier.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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"; + } +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/Statement.scala b/sources/scala/dbc/statement/Statement.scala new file mode 100644 index 0000000000..64f0967483 --- /dev/null +++ b/sources/scala/dbc/statement/Statement.scala @@ -0,0 +1,14 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** An ISO-9075:2003 (SQL) statement. */ +abstract class Statement { + +} diff --git a/sources/scala/dbc/statement/Status.scala b/sources/scala/dbc/statement/Status.scala new file mode 100644 index 0000000000..61ab479199 --- /dev/null +++ b/sources/scala/dbc/statement/Status.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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); + } + +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/Table.scala b/sources/scala/dbc/statement/Table.scala new file mode 100644 index 0000000000..d8e0d02289 --- /dev/null +++ b/sources/scala/dbc/statement/Table.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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 + }) + } +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/Transaction.scala b/sources/scala/dbc/statement/Transaction.scala new file mode 100644 index 0000000000..b294cf71a0 --- /dev/null +++ b/sources/scala/dbc/statement/Transaction.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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); + } + +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/Update.scala b/sources/scala/dbc/statement/Update.scala new file mode 100644 index 0000000000..cb32d89f60 --- /dev/null +++ b/sources/scala/dbc/statement/Update.scala @@ -0,0 +1,43 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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]; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/expression/Aggregate.scala b/sources/scala/dbc/statement/expression/Aggregate.scala new file mode 100644 index 0000000000..c4847ade1c --- /dev/null +++ b/sources/scala/dbc/statement/expression/Aggregate.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/expression/BinaryOperator.scala b/sources/scala/dbc/statement/expression/BinaryOperator.scala new file mode 100644 index 0000000000..a0d964a798 --- /dev/null +++ b/sources/scala/dbc/statement/expression/BinaryOperator.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/expression/Constant.scala b/sources/scala/dbc/statement/expression/Constant.scala new file mode 100644 index 0000000000..12a089ea48 --- /dev/null +++ b/sources/scala/dbc/statement/expression/Constant.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/expression/Default.scala b/sources/scala/dbc/statement/expression/Default.scala new file mode 100644 index 0000000000..2b84a6ce4f --- /dev/null +++ b/sources/scala/dbc/statement/expression/Default.scala @@ -0,0 +1,17 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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"; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/expression/Field.scala b/sources/scala/dbc/statement/expression/Field.scala new file mode 100644 index 0000000000..bcf5d9f29d --- /dev/null +++ b/sources/scala/dbc/statement/expression/Field.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/expression/FunctionCall.scala b/sources/scala/dbc/statement/expression/FunctionCall.scala new file mode 100644 index 0000000000..030b05812b --- /dev/null +++ b/sources/scala/dbc/statement/expression/FunctionCall.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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]; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/expression/Select.scala b/sources/scala/dbc/statement/expression/Select.scala new file mode 100644 index 0000000000..bce9a5d002 --- /dev/null +++ b/sources/scala/dbc/statement/expression/Select.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/expression/SetFunction.scala b/sources/scala/dbc/statement/expression/SetFunction.scala new file mode 100644 index 0000000000..3779dac581 --- /dev/null +++ b/sources/scala/dbc/statement/expression/SetFunction.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/statement/expression/TypeCast.scala b/sources/scala/dbc/statement/expression/TypeCast.scala new file mode 100644 index 0000000000..25d1874381 --- /dev/null +++ b/sources/scala/dbc/statement/expression/TypeCast.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; +}
\ No newline at end of file diff --git a/sources/scala/dbc/statement/expression/UnaryOperator.scala b/sources/scala/dbc/statement/expression/UnaryOperator.scala new file mode 100644 index 0000000000..1e7ab028f7 --- /dev/null +++ b/sources/scala/dbc/statement/expression/UnaryOperator.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/syntax/DataTypeUtil.scala b/sources/scala/dbc/syntax/DataTypeUtil.scala new file mode 100644 index 0000000000..1df3d20317 --- /dev/null +++ b/sources/scala/dbc/syntax/DataTypeUtil.scala @@ -0,0 +1,94 @@ +/* __ *\ +** ________ ___ / / ___ Scala API tests ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/syntax/Database.scala b/sources/scala/dbc/syntax/Database.scala new file mode 100644 index 0000000000..d819931e95 --- /dev/null +++ b/sources/scala/dbc/syntax/Database.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API tests ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/syntax/Statement.scala b/sources/scala/dbc/syntax/Statement.scala new file mode 100644 index 0000000000..c4dd2e7434 --- /dev/null +++ b/sources/scala/dbc/syntax/Statement.scala @@ -0,0 +1,270 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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 view (fv:StatementField): SelectDerivedField = new SelectDerivedField { + val fieldValue = fv; + } + + implicit def view (fv:String): SelectDerivedField = new SelectDerivedField { + val fieldValue: StatementField = StatementExpression.view(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 view (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 view (sdfs:String): SelectDerivedColumns = { + val sdf: SelectDerivedField = sdfs; + view(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 view (sct:String): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Table { + val tableName = sct; + val tableRename = None; + val fieldTypes = Nil; + } + } + + implicit def view (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 view (sct:String): SelectSourceTables = new SelectSourceTables { + val fromClause = List(new statement.Table { + val tableName = sct; + val tableRename = None; + val fieldTypes = Nil; + }); + } + + implicit def view (sct:statement.Select): SelectSourceTables = new SelectSourceTables { + val fromClause = List(sct); + } + + implicit def view (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 view (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 view (se:StatementExpression): SelectGroupBy = new SelectGroupBy { + val groupByClause = List(se.toStatement); + } + + implicit def view (se:String): SelectGroupBy = new SelectGroupBy { + val groupByClause = List(new statement.expression.Field { + val tableName = None; + val fieldName = se; + }); + } + +} diff --git a/sources/scala/dbc/syntax/StatementExpression.scala b/sources/scala/dbc/syntax/StatementExpression.scala new file mode 100644 index 0000000000..e03be2a476 --- /dev/null +++ b/sources/scala/dbc/syntax/StatementExpression.scala @@ -0,0 +1,217 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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 view (ef:String): StatementField = new StatementField { + val fieldName = ef; + } + + + + +} diff --git a/sources/scala/dbc/value/ApproximateNumeric.scala b/sources/scala/dbc/value/ApproximateNumeric.scala new file mode 100644 index 0000000000..4b0c495f54 --- /dev/null +++ b/sources/scala/dbc/value/ApproximateNumeric.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +abstract class ApproximateNumeric [Type] extends Value { + + val dataType: datatype.ApproximateNumeric[Type]; + + def sqlString = nativeValue.toString(); + + } + +object ApproximateNumeric { + + def view (obj:value.ApproximateNumeric[Float]): Float = obj.nativeValue; + def view (obj:value.ApproximateNumeric[Double]): Double = obj.nativeValue; + +} diff --git a/sources/scala/dbc/value/Boolean.scala b/sources/scala/dbc/value/Boolean.scala new file mode 100644 index 0000000000..fa20a066af --- /dev/null +++ b/sources/scala/dbc/value/Boolean.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +abstract class Boolean extends Value { + + val dataType: datatype.Boolean; + + def sqlString = if (nativeValue) "TRUE" else "FALSE"; + +} + +object Boolean { + + def view (obj:value.Boolean): scala.Boolean = obj.nativeValue; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/value/Character.scala b/sources/scala/dbc/value/Character.scala new file mode 100644 index 0000000000..1d5433b991 --- /dev/null +++ b/sources/scala/dbc/value/Character.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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. */ + def view (obj:value.Character): String = obj.nativeValue; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/value/CharacterLargeObject.scala b/sources/scala/dbc/value/CharacterLargeObject.scala new file mode 100644 index 0000000000..c4fb7097e0 --- /dev/null +++ b/sources/scala/dbc/value/CharacterLargeObject.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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. */ + def view (obj:value.CharacterLargeObject): String = obj.nativeValue; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/value/CharacterVarying.scala b/sources/scala/dbc/value/CharacterVarying.scala new file mode 100644 index 0000000000..61142e536c --- /dev/null +++ b/sources/scala/dbc/value/CharacterVarying.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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. */ + def view (obj:value.CharacterVarying): String = obj.nativeValue; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/value/Conversion.scala b/sources/scala/dbc/value/Conversion.scala new file mode 100644 index 0000000000..5c5dda4220 --- /dev/null +++ b/sources/scala/dbc/value/Conversion.scala @@ -0,0 +1,153 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +import java.math._; + +object Conversion { + + class Illegal (msg:String) extends Exception(msg); + + implicit def view (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 view (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 view (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 view (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 view (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 view (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 view (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 view (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 view (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 view (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") + } + +}
\ No newline at end of file diff --git a/sources/scala/dbc/value/ExactNumeric.scala b/sources/scala/dbc/value/ExactNumeric.scala new file mode 100644 index 0000000000..f3e1a54aac --- /dev/null +++ b/sources/scala/dbc/value/ExactNumeric.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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 { + + def view (obj:value.ExactNumeric[Byte]): Byte = obj.nativeValue; + def view (obj:value.ExactNumeric[Short]): Short = obj.nativeValue; + def view (obj:value.ExactNumeric[Int]): Int = obj.nativeValue; + def view (obj:value.ExactNumeric[Long]): Long = obj.nativeValue; + def view (obj:value.ExactNumeric[BigInteger]): BigInteger = obj.nativeValue; + def view (obj:value.ExactNumeric[BigDecimal]): BigDecimal = obj.nativeValue; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/value/Factory.scala b/sources/scala/dbc/value/Factory.scala new file mode 100644 index 0000000000..b24f985354 --- /dev/null +++ b/sources/scala/dbc/value/Factory.scala @@ -0,0 +1,91 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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/sources/scala/dbc/value/Unknown.scala b/sources/scala/dbc/value/Unknown.scala new file mode 100644 index 0000000000..b40df69011 --- /dev/null +++ b/sources/scala/dbc/value/Unknown.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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; + +}
\ No newline at end of file diff --git a/sources/scala/dbc/vendor/PostgreSQL.scala b/sources/scala/dbc/vendor/PostgreSQL.scala new file mode 100644 index 0000000000..2653ed714a --- /dev/null +++ b/sources/scala/dbc/vendor/PostgreSQL.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +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:" + +}
\ No newline at end of file |