T2C -- A SHUTTLE CLASS GENERATOR GUNTHER SCHADOW JULY 21, 1995 INTRODUCTION Data abstraction is an important technique which is supported by modern programming languages like C++ as well as data base management systems (DBMS). The loosely coupled approach to DBMS based programming which uses a common programming language rather than a special (so called ``4th generation'') data base programming language fits nicely into the modern open systems paradigma. However, since SQL does not know about data structures independently from relations, and since the general programming languages do not know about Relations the interface between DBMS and application programm remains a problem. While data abstraction is high in both parts, programmers have to break down any structuring at the DBMS iterface and supply every single value to the SQL statement. Shuttle classes are a convenient means to reduce this problem. SHUTTLE CLASSES Basically a shuttle class is a data structure that reflects a certain relation within a DBMS. At a single point in time, a shuttle class is ready to contains one tuple of it's corresponding relation. It's purpose is to retrieve a tuple from the DBMS making it available as a whole to the application program. On the other hand a shuttle class stores the tuple back into the DBMS with data supplied by the application program. Because it's most important task is to transport tuples back and forth between DBMS and application program it is called a shuttle class. ABOUT T2C T2C generates C++ source code for shuttle classes from the data dictionary of a DBMS. It is based on CB++, a C++ SQL interface written by Bernhard Strassl. Today, T2C supports the Oracle DBMS, but as the CB++ interface may support others, T2C can do so. The name T2C stays for ``Table to Class'' compiler. The following example shall give an idea of what T2C does and how the shuttle classes can be used. EXAMPLE FOR A SHUTTLE CLASS THE DBMS RELATION Suppose there is a relation which was created by the following SQL statement: CREATE TABLE SCOTT.TEST ( FOO CHAR(10), BAR CHAR(10), BLA NUMBER ); RUNNING T2C You run T2C on this table by invoking it as follows: > t2c -d scott/tiger TEST The synopsis of t2c invocation is as follows: t2c [-d database] [-o owner] [objects ...] Generally T2C is invoked with an optional database connect string, an owner or schema name whose tables you need shuttle classes for and finally a list of table or view names. Please note that the object and owner names have to be provided in the form you would provide them to a "WHERE ... = '...'" clause of a select statement. While SQL identifiers are generally case insensitive, the string expression ``'...''' still is. Thus if you would write ``test'' instead of ``TEST'' t2c will not find the desired object. THE SHUTTLE CLASS If invoked correctly T2C selects the data dictionary tables (ALL_OBJECTS and ALL_TAB_COLUMNS) for information about the `TEST' relation. It creates three files: `Test.h', `Test.icc' and `Test.cc'. The first file contains the class declaration, the second defines some inline functions while the third has the definition of all other functions and static data. In the following section the class declaration is shown and commented. Any shuttle class is derived from the abstract base class ``Table''. class Test : public Table { These are the definitions of field lengths for string type domains. public: const size_t S_foo = 10; const size_t S_bar = 10; What follows are access funtions to the variable members. The variable members are not public, since the information about the null value must be handled for each access to them in order to keep the object in a consistent state. The access methods are set_...() and get_...() functions to store and read a value to a variable member, unset_...() functions to set a member to the null value and the ..._is_null() predicates which tell if a variable is valid or null. void set_foo(char *x); void set_bar(char *x); void set_bla(long x); char *get_foo(); char *get_bar(); long get_bla(); void unset(); // unset everything void unset_foo(); void unset_bar(); void unset_bla(); bool foo_is_null(); bool bar_is_null(); bool bla_is_null(); The constructor currently only tests if a database connection was correctly established before a shuttle object is instantiated. These topics are explained below. Test(); The basic database functionality is performed by the insert(), retrieve(), update() and remove() functions. Please note the polymorphism in semantics that these functions have. The insert() functions just inserts a the current object as a tuple into the relation. void insert(); The retrieve() and next() functions look up tuples in the relation and update the objects variable members. The retrieve() function may be passed a clause argument. This is a string that completes the SQL SELECT statement and will most often be a "WHERE ..." clause. If the clause parameter is ommitted, any tuple is selected. The next() function is the iterator for retrieval of subsequent tuples. result retrieve(const char *clause = ""); result next(); Update can have two different meanings: To update a *tuple* means to store the values of the object into the database at the same tuple which they have been read from. This is done by the DBMS internal pseudocolumn that uniquely describes the tuple (``ROWID'' in Oracle). On the other hand, to update the *relation* means to set other tuples, possibly more than one, to the values of the object. void update(); void update(const char *clause); The remove() functions are similar to update(). There is one remove() function that removes the current tuple. The other remove() function deletes possibly more than one objects for which the clause is true. However, since the second remove() does not refer to the current tuple in any way, it is declared a static function. void remove(); static void remove(const char *clause); There are more static functions, which creates the same relation possibly in the table space or schema of another owner. Or which drops the relation. static void create(); static void drop(TabType t = table); static const char *owner(const char *); static const char *owner(); Finally there are the variable members and the corresponding indicator variables. Some implementation dependent private members are not shown here. private: char V_foo[S_foo + 1]; char V_bar[S_bar + 1]; long V_bla; short I_foo; short I_bar; short I_bla; }; THE UNDERLYING ``TABLE'' CLASS Finally I will show some major aspects about the ``Table'' abstract base class. class Table { public: Table(); virtual ~Table(); Prior to any instantiation of a Table derived object you have to setup a DBMS connecttion with use(), to which you supply the DBMS specific connect string. In a standard Oracle installation use("scott/tiger") would allow you to access the example tables. In fact "scott/tiger" is the default connection which is done by the T2C compiler bootstrap to generate the shuttle objects that it needs for it's work. You can also adopt a SqlMgr object that is defined in CB++. The database() function or the smart pointer operator gives access to the underlying database connection by the means of the CB++ SqlMgr class. static void use(const char *dbname); static void use(SqlMgr *); static SqlMgr *database(); SqlMgr *operator -> (); Oracle defines two pseudo columns which are given access to by the functions retrieve_count() and retrieve_level(), the former counting the number of the selected tuple and the second giving the level that was computed by the SQL terms "START WITH" and "CONNECT BY". virtual u_int retrieve_count(); virtual u_int retrieve_level(); You can accsess the DBMS dependent tuple address by the object_id() function. However, since this part of the t2c generated objects might change, the direct use of object id's is currently discuraged. virtual oid_t object_id(); The TabType enumerated type is used when a table is dropped, since due to a change of the owner or database connection with a subsequent create(), what might have been a view becomes a table. You should generally use drop() and create() and user(...) only if you are sure about what you are doing. enum TabType { table, view }; SqlHandle cursor() { return Cursor; } The cursor() function returns the current retrieve cursor or FAIL (= -1) if noone is allocated. The SqlHandle type is defined by CB++. The cursor is, however, rarely needed indepentently from the shuttle object. COPYRIGHT Copyright (c) 1995 Gunther Schadow. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Enjoy -Gunther Schadow