EDC
From ESEwiki
Contents |
Introduction
EDC stands for Eiffel Data Connection. It is all about Data access.
Beware: not database, but data. Certainly databases are part of the design but EDC can afford other kinds of storage. One such storage, currently, is XML files.
EDC is built in two layers: the abstract layer, and the implementations. That's why data can be exported in databases, but also in other kinds of data storage. Even a pure-memory implementation would be possible.
All the same, EDC's design is influenced by relational data organisation. That's because most important databases nowadays are relational, and because it is a good paradigm for data organization.
Abstract Design
The design of EDC is Objects. Of course an SQL-like parser can be added in the future but that's not the focus.
EDC has three important abstractions:
- The drivers. Those are the link to the low-level implementations. EDC has a driver registry where drivers can be registered. After registering drivers, an application opens a connection to a particular database using URLs. The driver registry uses a simple Chain Of Responsibility pattern to let one driver handle a URL.
- The data structure. It represents how the data should be organized. EDC being relational, the following concepts appear: the Connection, the Table, the Column and the Index.
- The queries. Those allow to operate on data from the backend. There are four kinds of queries: Select, Update, Delete and Insert. The first three are Selectable, meaning that they work on a data subset (either simply fetch it, or modify it, or delete it). The fourth is obviously data insertion.
Of course, not all the classes are shown. It is but a proof of concept. See the following sections for a list of all classes.
Example:
class TEST1 create make feature {}
make is
local
cnx: EDC_CONNECTION; set: EDC_RESULT_SET
users: SQL_TABLE; user_id, user_name: EDC_STRING_COLUMN
i: INTEGER
do
register_driver(create {EDC_STORABLE_XML_FILE_DRIVER})
cnx := connect_to("xml://test.xml", Void)
cnx.set_auto_commit(True)
users:= cnx.table(once "users")
user_id ::= users.column(once "id")
user_name ::= users.column(once "name")
set := stmt.select_data({FAST_ARRAY[SQL_COLUMN] <<
user_id, user_name
>>})
.where(user_id ~= (user_id @ "1.*")
and user_name == (user_name @ "Dupont")
.call(Void)
end
end
EDC
This class is the entry point of EDC. It can be either inserted or used via an entity (it is indeed expanded).
An application must register the drivers it uses (objects of the EDC_DRIVER class) to this class. It is also this class that is called when a new connection is needed.
EDC_CONNECTION
The EDC_CONNECTION class represents on access point to a database. It is used to issue commit/rollback commands and to prepare queries.
EDC_QUERY
This class is the master class of all queries. There are four types of queries:
- EDC_SELECT allows to fetch data. Its call query will return an EDC_RESULT_SET which allows to iterate through the fetched data.
- EDC_UPDATE allows to change existing data. Its call query requires the data by which the fetched data will be replaced; it modifies the data and returns the number of changed rows. ** TODO: CQS?
- EDC_DELETE allows to remove data. Its call query deletes the data and returns the number of deleted rows. ** TODO: CQS?
- EDC_INSERT allows to insert data.
The first three classes are in fact descendants of a single class, EDC_SELECTABLE, which allows to set the selection criteria via the where procedure which takes an EDC_EXPRESSION. In the special case of EDC_UPDATE and EDC_DELETE the expression must select data in a single table. The call method of the three classes taks at least one argument: a collection of EDC_VALUE which allow to fill in open arguments. This way you can call the same selectable more than once with different arguments, provided you prepared your where expression with open arguments.
EDC_COLUMN
Currently EDC_TYPED_COLUMN has the following implementations:
- EDC_BLOB_COLUMN is a EDC_TYPED_VALUE[REPOSITORY[STORABLE]]
- EDC_CHARACTER_COLUMN is a EDC_TYPED_VALUE[CHARACTER]
- EDC_INCREMENT_COLUMN is a EDC_TYPED_COLUMN[INTEGER] but will have special methods for auto-increment (usually fitting for primary keys)
- EDC_INTEGER_COLUMN is a EDC_TYPED_COLUMN[INTEGER]
- EDC_STRING_COLUMN is a EDC_TYPED_COLUMN[STRING]
- EDC_TIME_COLUMN is a EDC_TYPED_COLUMN[TIME]
EDC_HASHABLE_COLUMN and EDC_COMPARABLE_COLUMN are two classes that are inserted by most column classes to give some columns more features depending on the value's abilities (resp. hashable, comparable).
EDC_TYPED_COLUMN is one of the most important types used when creating values. It provides three methods that give different types of values:
- the manifest query (or its infix "@") takes a manifest value (the type depending on the type of the column) and returns a constant value;
- the parameter query (or its prefix "&") returns an open value that must be filled by the call query (i.e. set just before fetching data);
- the as_value query returns the column as a value that will be set when a row is fetched.
As a shorthand, the class also supports most methods of EDC_TYPED_VALUE (is_null, and so forth) called with the target's as_value.
EDC_VALUE
The values allow to compare data when it is being fetched. There are many operators that allow to build expressions used when comparing data. In fact those operators are provided by EDC_TYPED_VALUE:
- is_null returns an expression that checks that the value is undefined (NULL in Relational parlance).
- equals (or infix "==") returns an expression that checks that both values are equal (either strictly equal for expanded values, or is_equal for references).
- in (or infix "~~") returns an expression that checks that the value is in the data set returns by the given EDC_SELECT.
- is_like (or infix "~=") returns an expression that checks that the values match.
The left value must be an EDC_STRING_COLUMN_VALUE, and the right value must be an EDC_TYPED_VALUE[STRING]. The right value is considered as a regular expression pattern to be compiled using the lib/regular_expression cluster. - lt, le, gt, ge (or resp. infix "<", infix "<=", infix ">", infix ">=") return an expression that checks that values compare in the given direction.
The left value must be an EDC_COMPARABLE_COLUMN_VALUE, and the right value must be an EDC_TYPED_VALUE[COMPARABLE].
EDC_EXPRESSION
Currently EDC_EXPRESSION has the following implementations:
- AND_EXPRESSION which agregates two EDC_EXPRESSION
- EQUALS_EXPRESSION[T_] which agregates two EDC_TYPED_VALUE[T_] which will be compared by the feature is_equal
- GE_EXPRESSION[N_->COMPARABLE] which agregates two EDC_TYPED_VALUE[N_] which will be compared by the feature >=
- GT_EXPRESSION[N_->COMPARABLE] which agregates two EDC_TYPED_VALUE[N_] which will be compared by the feature >
- IN_EXPRESSION[T_] which agregates a EDC_TYPED_VALUE[T_] and a EDC_SELECT
- ISNULL_EXPRESSION[T_] which agregates a single EDC_TYPED_VALUE[T_]
- LE_EXPRESSION[N_->COMPARABLE] which agregates two EDC_TYPED_VALUE[N_] which will be compared by the feature <=
- LIKE_EXPRESSION which agregates two EDC_TYPED_VALUE[STRING] which will be compared using the lib/regular_expression cluster (caveat: it means that we don't use the SQL regular expressions, but proper POSIX regexps)
- LT_EXPRESSION[N_->COMPARABLE] which agregates two EDC_TYPED_VALUEs which will be compared by the feature <
- NOT_EXPRESSION which agregates two EDC_EXPRESSION
- OR_EXPRESSION which agregates two EDC_EXPRESSION
Expressions provide three operators that allow to combine them to make powerful expressions: infix "and", infix "or" and infix "not".
EDC_RESULT_SET
This is an iterator of fetched data. An object of this class is returned by EDC_SELECT's call method.
To iterate though the returned rows, use the previous, next, is_off methods.
To fetch data, use the columns to get typed data: EDC_TYPED_COLUMN provides an item query that returns a REFERENCE to the data. The reference can be Void in the case of undefined data. If the reference is not Void and the data type is a reference type, the reference's item will not be Void either.
Implementations
Implementations are expected to provide implementations of the following classes:
- EDC_DRIVER: the functions can_connect_to and connect_to;
- EDC_CONNECTION: the functions connect, rollback and so on, and especially the methods call_insert to insert data and call_select to select data;
- EDC_RESULT_SET which is a kind of iterator of fetched data: all the iterator classic features, plus escpecially the query item that is used by EDC_TYPED_COLUMN to return typed data.
Log
This implementation is a Proxy pattern. It just logs actions in an OUTPUT_STREAM and lets another implementation do the real things to the data.
The URL recognized by the EDC_LOG_DRIVER is "log://..." with the sequel of the URL being used to determine the real backend.
The log format is SQL.
Storable
This implementation relies on the storage/repository cluster which implements SmartEiffel's data marshalling.
One special driver is implemented: EDC_STORABLE_XML_FILE_DRIVER which stores data in an XML file. But one can have other storable implementations and provide a new child of the deferred class EDC_STORABLE_DRIVER. The URL recognized by that driver is "xml://..." with the sequel of the URL being the path of the XML file.
GNOME
Note: not yet coded -- Rely on Paolo's work
libgda is the GNOME implementation of database access. It already encapsulates most of the database connectivity we need. Furthermore, it would help SmartEiffel to integrate nicely in a GNOME environment which is a good aim to pursue.
We want to rely on libgda instead of once again reinvent the wheel and remake wrappers around ODBC, MySQL, PostgreSQL, SQLite, OCI and so many others.
The driver will recognize URLs of the form "gda://..." with the sequel of the URL being the GNOME Data Access URL.
JDBC
Java users should not be left out in the cold. compile_to_jvm is in a good enough shape nowadays to be able to afford new features.
The driver will recognize URLs or the form "java://..." with the sequel of the URL being the Java DataBase Connection URL.

