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