Fan

 

const class

sql::SqlService

sys::Obj
  sys::Thread
    sql::SqlService
//
// Copyright (c) 2008, John Sublett
// Licensed under the Academic Free License version 3.0
//
// History:
//   12 Jan 08  John Sublett  Creation
//

**
** SqlService is the interface to a relational database.  It is const
** and all state is stored as thread local variables.
**
** See `docLib::Sql`.
**
const class SqlService : Thread
{
  **
  ** Make a new SqlService.
  **
  ** - 'threadName' is the unique name used to identify the thread.
  ** - 'connection' is the connection string.  For java this is the jdbc url.  For .Net
  **   this is the connection string.
  ** - 'username' is the username for the database login.
  ** - 'password' is the password for the database login.
  ** - 'dialect' is the database specific dialect implementation.  If null,
  **   MySqlDialect is assumed.
  **
  new make(Str? threadName  := null,
           Str connection  := "",
           Str? username    := "",
           Str? password    := "",
           Dialect? dialect := null) : super(threadName)
  {
    this.connection = connection
    this.username   = username
    this.password   = password

    if (dialect == null)
      dialect = MySqlDialect.make
    else
      this.dialect = dialect
  }

  **
  ** Return true.
  **
  override Bool isService() { return true }

  override protected Obj? run()
  {
    log.info("SqlService started ($name)")
    log.info("SqlService connection: $connection")
    loop |Obj->Obj?| { return null }
    log.info("SqlService stopped ($name)")
    return null
  }

  **
  ** Open the database.  This opens a connection to the database
  ** for the calling thread.  A database must be open before
  ** it can be accessed.  'open' may be called multiple times.
  ** Each time 'open' is called, a counter is incremented.  The
  ** database will not be closed until 'close' has been called
  ** for each call to 'open'.
  **
  SqlService open()
  {
    if (!isRunning()) throw Err("SqlService not started")

    Connection? conn := Thread.locals[name]
    if (conn == null)
    {
      conn = Connection.open(connection, username, password)
      Thread.locals[name] = conn
    }
    else
    {
      conn.increment
    }

    return this
  }

  **
  ** Get the connection to this database for the current thread.
  **
  private Connection? threadConnection(Bool checked := true)
  {
    Connection? conn := Thread.locals[name]
    if (conn == null)
    {
      if (checked)
        throw SqlErr("Database is not open.")
      else
        return null
    }

    if (conn.isClosed)
    {
      if (checked)
      {
        conn.close
        Thread.locals.remove(name)
        throw SqlErr("Database has been closed.")
      }
      else
        return null
    }

    return conn
  }

  **
  ** Close the database.  A call to 'close' may not actually
  ** close the database.  It depends on how many times 'open' has
  ** been called for the current thread.  The database will
  ** not be closed until 'close' has been called once for every time
  ** that 'open' has been called.
  **
  Void close()
  {
    Connection? conn := Thread.locals[name]
    if (conn != null)
    {
      if (conn.decrement == 0)
      {
        conn.close
        Thread.locals.remove(name)
      }
    }
  }

  **
  ** Test the closed state of the database.
  **
  Bool isClosed()
  {
    conn := threadConnection(false)
    if (conn == null) return true
    return conn.isClosed
  }

//////////////////////////////////////////////////////////////////////////
// Metadata
//////////////////////////////////////////////////////////////////////////

  **
  ** Does the specified table exist in the database?
  **
  Bool tableExists(Str tableName)
  {
    return threadConnection.tableExists(tableName)
  }

  **
  ** List the tables in the database.  Returns a list of the
  ** table names.
  **
  Str[] tables()
  {
    return threadConnection.tables();
  }

  **
  ** Get a default row instance for the specified table.  The
  ** result has a field for each table column.
  **
  Obj tableRow(Str tableName)
  {
    return threadConnection.tableRow(tableName)
  }

//////////////////////////////////////////////////////////////////////////
// Statement
//////////////////////////////////////////////////////////////////////////

  **
  ** Create a statement for this database.
  **
  Statement sql(Str sql)
  {
    return threadConnection.sql(sql)
  }

  **
  ** Get the key that was generated by the last statement
  ** or null if no key was generated.
  **
  Int? lastAutoKey()
  {
    return threadConnection.lastAutoKey
  }

//////////////////////////////////////////////////////////////////////////
// Transactions
//////////////////////////////////////////////////////////////////////////

  **
  ** Set the auto-commit state of this database.  If true, each
  ** statement is committed as it is executed.  If false, statements
  ** are grouped into transactions and committed when 'commit'
  **
  Void autoCommit(Bool val)
  {
    threadConnection.autoCommit = val
  }

  **
  ** Test the auto-commit state of this database.  If true, each
  ** statement is committed as it is executed.  If false, statements
  ** are grouped into transactions and committed when 'commit'
  **
  Bool isAutoCommit()
  {
    return threadConnection.autoCommit
  }

  **
  ** Commit all the changes made inside the current transaction.
  **
  Void commit()
  {
    threadConnection.commit
  }

  **
  ** Undo any changes made inside the current transaction.
  **
  Void rollback()
  {
    threadConnection.rollback
  }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  ** Standard log for the sql service
  static const Log log := Log.get("sql")

  **
  ** The database dialect for this service.
  **
  const Dialect dialect

  **
  ** The username used to connect to this database.
  **
  const Str? username

  **
  ** The password used to connect to this database.
  **
  const Str? password

  **
  ** The database specific string used to connect to the database.
  **
  const Str connection

}