Session
The ADO.Sessions package defines the control and management of database sessions.
The database session is represented by the Session or Master_Session types.
It provides operation to create a database statement that can be executed.
The Session type is used to represent read-only database sessions. It provides
operations to query the database but it does not allow to update or delete content.
The Master_Session type extends the Session type to provide write
access and it provides operations to get insert, update or delete statements.
The differentiation between the two sessions is provided for the support of database
replications with databases such as MySQL or PostgreSQL.
Connection string
The database connection string is an URI that specifies the database driver to use as well as the information for the database driver to connect to the database. The driver connection is a string of the form:
driver://[host][:port]/[database][?property1][=value1]...
The database connection string is passed to the session factory that maintains connections to the database (see ADO.Sessions.Factory).
Database Drivers
Database drivers provide operations to access the database. These operations are
specific to the database type and the ADO.Drivers package among others provide
an abstraction that allows to make the different databases look like they have almost
the same interface.
A database driver exists for SQLite/SQLCipher, MySQL and PostgreSQL. The driver
is either statically linked to the application or it can be loaded dynamically if it was
built as a shared library. For a dynamic load, the driver shared library name must be
prefixed by libada_ado_. For example, for a mysql driver, the shared
library name is libada_ado_mysql.so.
| Driver name | Database |
|---|---|
| mysql | MySQL, MariaDB |
| sqlite | SQLite, SQLCipher |
| postgresql | PostgreSQL |
The database drivers are initialized automatically but in some cases, you may want
to control some database driver configuration parameter. In that case,
the initialization must be done only once before creating a session
factory and getting a database connection. The initialization can be made using
a property file which contains the configuration for the database drivers and
the database connection properties. For such initialization, you will have to
call one of the Initialize operation from the ADO.Drivers package.
ADO.Drivers.Initialize ("db.properties");
The set of configuration properties can be set programatically and passed to the
Initialize operation.
Config : Util.Properties.Manager;
...
Config.Set ("ado.database", "sqlite:///mydatabase.db");
Config.Set ("ado.queries.path", ".;db");
ADO.Drivers.Initialize (Config);
Once initialized, a configuration property can be retrieved by using the Get_Config
operation.
URI : constant String := ADO.Drivers.Get_Config ("ado.database");
Dynamic loading of database drivers is disabled by default for security reasons and it can be enabled by setting the following property in the configuration file:
ado.drivers.load=true
Dynamic loading is triggered when a database connection string refers to a database driver which is not known.
MySQL Database Driver
The MySQL database driver can be initialize explicitly by using the ado_mysql
GNAT project and calling the initialization procedure.
ADO.Mysql.Initialize ("db.properties");
The set of configuration properties can be set programatically and passed to the
Initialize operation.
Config : Util.Properties.Manager;
...
Config.Set ("ado.database", "mysql://localhost:3306/ado_test");
Config.Set ("ado.queries.path", ".;db");
ADO.Mysql.Initialize (Config);
The MySQL database driver supports the following properties:
| Name | Description |
|---|---|
| user | The user name to connect to the server |
| password | The user password to connect to the server |
| socket | The optional Unix socket path for a Unix socket base connection |
| encoding | The encoding to be used for the connection (ex: UTF-8) |
SQLite Database Driver
The SQLite database driver can be initialized explicitly by using the ado_sqlite
GNAT project and calling the initialization procedure.
ADO.Sqlite.Initialize ("db.properties");
The set of configuration properties can be set programatically and passed to the
Initialize operation.
Config : Util.Properties.Manager;
...
Config.Set ("ado.database", "sqlite:regtests.db?synchronous=OFF&encoding=UTF-8");
Config.Set ("ado.queries.path", ".;db");
ADO.Sqlite.Initialize (Config);
The SQLite database driver will pass all the properties as SQLite pragma allowing
the configuration of the SQLite database. When the driver is compiled with the
-XADO_USE_SQLCIPHER=yes option, the SQLCipher runtime will be used and you can
either use a standard SQLite database or an SQLCipher database. The database
encryption key must be configured as a key configuration property, for
example:
Config.Set ("ado.database",
"sqlite:regtests.cipher?key='db-password'");
PostgreSQL Database Driver
The PostgreSQL database driver can be initialize explicitly by using the ado_postgresql
GNAT project and calling the initialization procedure.
ADO.Postgresql.Initialize ("db.properties");
The set of configuration properties can be set programatically and passed to the
Initialize operation.
Config : Util.Properties.Manager;
...
Config.Set ("ado.database", "postgresql://localhost:5432/ado_test?user=ado&password=ado");
Config.Set ("ado.queries.path", ".;db");
ADO.Postgresql.Initialize (Config);
The PostgreSQL database driver supports the following properties:
| Name | Description |
|---|---|
| user | The user name to connect to the server |
| password | The user password to connect to the server |
Session Factory
The session factory is the entry point to obtain a database session.
The ADO.Sessions.Factory package defines the factory for creating
sessions.
with ADO.Sessions.Factory;
...
Sess_Factory : ADO.Sessions.Factory;
The session factory can be initialized by using the Create operation and
by giving a URI string that identifies the driver and the information to connect
to the database. The session factory is created only once when the application starts.
ADO.Sessions.Factory.Create (Sess_Factory, "mysql://localhost:3306/ado_test?user=test");
Having a session factory, one can get a database by using the Get_Session or
Get_Master_Session function. Each time this operation is called, a new session
is returned. The session is released when the session variable is finalized.
DB : ADO.Sessions.Session := Sess_Factory.Get_Session;
The session factory is also responsible for maintaining some data that is shared by all the database connections. This includes:
- the sequence generators used to allocate unique identifiers for database tables,
- the entity cache,
- some application specific global cache.
Database Caches
The ADO cache manager allows to create and maintain cache of values and use the cache from the SQL expander to replace cached values before evaluating the SQL. The SQL expander identifies constructs as follows:
$cache_name[entry-name]
and look for the cache identified by cache_name and then replace the cache entry registered with the name entry-name.
The cache manager is represented by the Cache_Manager type and the database session contains one cache manager. Applications may use their own cache in that case they will declare their cache as follows:
M : ADO.Caches.Cache_Manager;
A cache group is identified by a unique name and is represented by the Cache_Type base class. The cache group instance is registered in the cache manager by using the Add_Cache operation.
Sequence Generators
The sequence generator is responsible for creating unique ID's
across all database objects. Each table can be associated with a sequence generator
which is configured on the session factory. The sequence generators are shared by
several database sessions and the implementation is thread-safe. The sequence generator
provides an efficient alternative to the AUTO INCREMENT support provided by SQL databases.
The HiLo_Generator implements a simple High Low sequence generator
by using sequences that avoid to access the database.
The Snowflake_Generator implements an efficient algorithm that can be used in
distributed environments.
For example, to allocate a new database identifier for a table user, you could write:
DB : ADO.Sessions.Master_Session := ...;
Id : ADO.Identifier;
...
DB.Allocate (Name => "user", Id => Id);
HiLo Sequence Generator
The HiLo sequence generator. This sequence generator uses a database table
ado_sequence to allocate blocks of identifiers for a given sequence name.
The sequence table contains one row for each sequence. It keeps track of
the next available sequence identifier (in the value column).
To allocate a sequence block, the HiLo generator obtains the next available sequence identified and updates it by adding the sequence block size. The HiLo sequence generator will allocate the identifiers until the block is full after which a new block will be allocated.
Snowflake ID Generator
The snowflake generator is a form of unique ID generator that can be used in distributed environment. It generates a 64-bit number decomposed in a first 42 bits representing a timestamp since a given epoch in milliseconds. The next N bits represent the machine ID and the last P bits a sequence number. Some snowflake generators are using 10 bits for the machine ID and 12 bits for the sequence number. Some others are using 13 bits for a shard ID and 10 bits for the sequence number. To give some flexibility, the snowflake generator is provided as a generic package that must be instantiated with two constants:
- a Node_Bits representing the number of bits for the machine ID, shard ID or node ID,
- a Sequence_Bits representing the number of bits for the sequence.
The timestamp will use 64 - Node_Bits - Sequence_Bits bits.
For example, the package is first instantiated with the desired configuration:
package My_Snowflake is
new ADO.Sequences.Snowflake (Node_Bits => 12,
Sequence_Bits => 10);
An instance of the generator must be created and registered in the session factory.
The generator is assigned a name (in most cases a database table name) which will be used
to identify it by the Allocate procedure. The Snowflake generator needs an epoch
timestamp representing the start of epoch for the sequence generation and a machine ID or
node ID which should be unique and specific to each server instance that will create and
use such generator.
S : ADO.Sequences.Generator_Access
:= My_Snowflake.Create_Snowflake_Generator
(Sess_Factory'Unchecked_Access, "user", Epoch, 10);
begin
Sess_Factory.Set_Generator (S);