summaryrefslogtreecommitdiff
path: root/sources/scala/dbc/Database.scala
diff options
context:
space:
mode:
Diffstat (limited to 'sources/scala/dbc/Database.scala')
-rw-r--r--sources/scala/dbc/Database.scala158
1 files changed, 158 insertions, 0 deletions
diff --git a/sources/scala/dbc/Database.scala b/sources/scala/dbc/Database.scala
new file mode 100644
index 0000000000..7eb235fa01
--- /dev/null
+++ b/sources/scala/dbc/Database.scala
@@ -0,0 +1,158 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.dbc;
+
+import java.sql._;
+
+/** A link to a database. The <code>Database</code> abstract class must be specialised for every different DBMS.
+ ** @author Gilles Dubochet */
+case class Database (dbms:Vendor) {
+
+ class Closed extends Exception;
+
+ /** A lock used for operations that need to be atomic for this database instance. */
+ private val lock: scala.concurrent.Lock = new scala.concurrent.Lock();
+
+ /** The vendor of the DBMS that contains this database. */
+ private val vendor: Vendor = dbms;
+
+ /** The Database connections available to use. */
+ private var availableConnections: List[Connection] = Nil;
+ /** The connections that are currently in use. */
+ private var usedConnections: List[Connection] = Nil;
+
+ /** Whether the database no longer accepts new connections. */
+ private var closing: Boolean = false;
+
+ /** Retrieves a connection from the available connection pool or creates a new one.
+ ** @returns A connection that can be used to access the database.*/
+ private def getConnection: Connection = {
+ if (closing) {
+ throw new Closed;
+ } else {
+ availableConnections match {
+ case Nil => {
+ lock.acquire;
+ val connection = vendor.getConnection;
+ usedConnections = connection :: usedConnections;
+ lock.release;
+ connection
+ }
+ case connection :: cs => {
+ lock.acquire;
+ availableConnections = cs;
+ usedConnections = connection :: usedConnections;
+ lock.release;
+ connection;
+ }
+ }
+ }
+ }
+
+ /** Closes a connection to this database. A closed connection might also return to the available connection pool if the latter is depleted.
+ ** @param connection The connection that should be closed. */
+ private def closeConnection (connection:Connection): Unit = {
+ if (closing) {
+ connection.close();
+ } else {
+ lock.acquire;
+ usedConnections = usedConnections.remove(e=>(e.equals(connection)));
+ if (availableConnections.length < vendor.retainedConnections)
+ availableConnections = connection :: availableConnections
+ else connection.close();
+ lock.release;
+ }
+ }
+
+ def close: Unit = {
+ closing = true;
+ for (val conn <- availableConnections) conn.close;
+ }
+
+ /** Executes a statement that returns a relation on this database.
+ ** @param relationStatement The statement to execute.
+ ** @return The relation returned by the database for this statement. */
+ def executeStatement (relationStatement:statement.Relation): result.Relation =
+ executeStatement(relationStatement,false);
+
+ /** Executes a statement that returns a relation on this database.
+ ** @param relationStatement The statement to execute.
+ ** @param debug Whether debugging information should be printed on the console.
+ ** @return The relation returned by the database for this statement. */
+ def executeStatement (relationStatement:statement.Relation, debug:Boolean): result.Relation =
+ new scala.dbc.result.Relation {
+ val statement = relationStatement;
+ if (debug) Console.println("## "+statement.sqlString);
+ private val connection = getConnection;
+ val sqlResult = connection.createStatement().executeQuery(statement.sqlString);
+ closeConnection(connection);
+ statement.typeCheck(this);
+ }
+
+ /** Executes a statement that updates the state of the database.
+ ** @param statusStatement The statement to execute.
+ ** @return The status of the database after the statement has been executed. */
+ def executeStatement (statusStatement:statement.Status): result.Status[Unit] =
+ executeStatement(statusStatement,false);
+
+ /** Executes a statement that updates the state of the database.
+ ** @param statusStatement The statement to execute.
+ ** @param debug Whether debugging information should be printed on the console.
+ ** @return The status of the database after the statement has been executed. */
+ def executeStatement (statusStatement:statement.Status, debug:Boolean): result.Status[Unit] =
+ new scala.dbc.result.Status[Unit] {
+ val statement = statusStatement;
+ if (debug) Console.println("## "+statement.sqlString);
+ def result = ();
+ private val connection = getConnection;
+ val jdbcStatement: java.sql.Statement = connection.createStatement();
+ jdbcStatement.execute(statement.sqlString);
+ val touchedCount = Some(jdbcStatement.getUpdateCount());
+ closeConnection(connection);
+ }
+
+ /** Executes a list of statements or other operations inside a transaction. Only statements
+ ** are protected in a transaction, other Scala code is not.
+ ** @param transactionStatement The transaction to execute as a closure.
+ ** @return The status of the database after the transaction has been executed. */
+ def executeStatement[ResultType] (transactionStatement:statement.Transaction[ResultType]): result.Status[ResultType] =
+ executeStatement(transactionStatement,false);
+
+ /** Executes a list of statements or other operations inside a transaction. Only statements
+ ** are protected in a transaction, other Scala code is not.
+ ** @param transactionStatement The transaction to execute as a closure.
+ ** @param debug Whether debugging information should be printed on the console.
+ ** @return The status of the database after the transaction has been executed. */
+ def executeStatement[ResultType] (transactionStatement:statement.Transaction[ResultType], debug:Boolean): result.Status[ResultType] = {
+ new scala.dbc.result.Status[ResultType] {
+ val touchedCount = None;
+ val statement = transactionStatement;
+ private val connection = getConnection;
+ connection.setAutoCommit(false);
+ val jdbcStatement: java.sql.Statement = connection.createStatement();
+ if (debug) Console.println("## "+transactionStatement.sqlStartString);
+ jdbcStatement.execute(transactionStatement.sqlStartString);
+ val result: ResultType = try {
+ val buffer = transactionStatement.transactionBody(Database.this);
+ if (debug) Console.println("## "+transactionStatement.sqlCommitString);
+ jdbcStatement.execute(transactionStatement.sqlCommitString);
+ buffer
+ } catch {
+ case e:Throwable => {
+ if (debug) Console.println("## "+transactionStatement.sqlAbortString);
+ jdbcStatement.execute(transactionStatement.sqlAbortString);
+ throw e;
+ }
+ }
+ connection.setAutoCommit(true);
+ closeConnection(connection);
+ }
+ }
+
+}