An Improved C-Language Interface for AceDB
Version 3

Motivation

In my work with ACeDB over the last year or so, I have done extensive development with the database management system. To adapt AceDB for use as a sample-tracking database, and to determine the true functionality of the system relative to what is shown in the (minimal) written documentation, I have extensively explored the C source code.

 

This work has allowed me to write a number of smaller interfaces to ACE in the C programming language. These interfaces perform very simple, specialized tasks. In many cases, these tasks could have been performed using existing ACE tools, or a combination of parsers and other processing steps. But in many cases in our view we would have been forced to compromise other design priorities, including the maintainability of the overall system and usability.

 

I would hope that we avoid excessive discussion of whether or not the solutions we adopted at GlaxoWellcome are "correct". This is certainly a topic for discussion. Rather, I would hope we agree on the following statements:

 

 

An interface such as the one I envision does not exist for any non-object-oriented database to the best of my knowledge. The AceDB community must make every effort to counteract the perception that AceDB is too complicated, too closed, and too unstable for use as an integral part of a development effort. Do not presume that this decision is a rational one. Most managers and software engineers have never seen nor heard of AceDB. In this environment, the services provided by AceDB cannot afford to be as good as those that are provided by competitors. They have to be better. Developers who want to work with ACE cannot wander in the desert forever. Eventually, they must other convert their supervisors and co-workers, or they must give up and move on.

 

Providing a simplified API for AceDB is only one enhancement that will eventually be required to make this conversion possibly, but it is quite possibly a necessary antecedent.

Current Status

I understand that AceDB is an evolving product, and many of the enhancements in C-language access may already have been incorporated into the current release. I understand that a PERL language interface exists that provides relatively straightforward access. I understand that the aceclient could be used to provide a fairly simple interface to ACE embedded within a program. I understand that tace can be scripted on the command-line to provide access from the shell. But would the developers of a new AceDB display use these methods to access the data in the database? Never. Until the C-language interfaces are as simple as they are efficient, enhancement and acceptance of ACeDB will suffer.

Proposal

  1. To produce a C-language API of no more than 50 routines, sufficient to code the data access requirements of a new ACeDB display. Note that the interface proposed in this language is over 60 routines in size. Usage may allow us to trim this library further than we currently believe possible.
  2. To produce as a part of the standard distribution a single header file and a single library sufficient to fully utilize this API.
  3. To document this API fully, including the semantics of all routines. The documentation should also include a cookbook demonstrating the use of all of these routines in small, but functional, C programs. This documentation (The ACeDB Programmer’s Handbook) should be available as a text document, a postscript document, a Word document, and on-line as an HTML document at the ACeDB documentation repository.
  4. To provide equivalent APIs through PERL and Java. Ideally, the API would be supported both by native-language bindings of the C library described above, and by a "remote method invocation" requiring network access to a remote server of an ACeDB database. Note that this does NOT require utilization of RMI, or of the existing aceserver, although those technologies should certainly be examined.
  5. To create a series of small executables which can be utilized at the UNIX command line. These executables should preserve the flavor of common UNIX tools, and should be designed so that the output fits seamlessly into standard UNIX expectations. An example would be a proposed "aceinstances CLASS [dburl]" program, which would simply return the names of all instances of a class in the database specified by the dburl. Another would be the tool "acequery" which takes a query as the first argument, and the dburl as the second, and returns a listing of all objects satisfying the query.
  6. To recode the AceDB command-processing kernel using this library, as well as the current VMAP display. These experiments should provide insight into the level of performance and flexibility provided by this interface.

A Straw API for C

 

/* Basic manipulations */

 

/* open the database. */

/* the db url is of the form

* "file://usr/local/acedb/worm" indicating that the desired database

* is local, or of the form "acedb://moot.cadif.cornell.edu:211014"

* indicating that the desired database is remote, and should be

* accessed over the network protocol. The nature of that protocol

* will remain transparent to the user of the library, and the

* functionality of the API is not altered in any way.

*/

/* ACE structures represent the context information required to

* distinguish between multiple connections in the same program.

* It is possible that there may be a restriction to only a single

* local open database in a given address space. There will be no

* restriction on the number of remote databases which may be open

* at one time. Likewise, there may be simultaneously open a single

* local database and one or more remote databases.

*/

/* in the few places where an instancename is required, it should be a

* qualified name, in the format " classname : name ". The exact

* lexical analysis of this string is to be determined.

*/

 

#define BLOCKING /* indicates routines which may block waiting */

 

typedef enum { _Int, _Text, _Float, _DateType, _Key, _Tag } ACETYPE;

 

extern int aceErrno;

char* aceGetErrorBuffer(int* bufsize);

BOOL aceSetErrorBuffer(char* buffer);

 

ACE* aceOpen(char* /* db url */);

BOOL aceSetContext(ACE*);

ACE* aceGetContext(void);

 

/* get write lock */

/* will calls requesting write locks be allowed to block? */

/* BOOL aceAllowBlocking(BOOL canBlock); -- unsupported */

BLOCKING BOOL aceWriteLock(void);

 

/* commit changes */

/* BOOL aceCheckpoint(void); -- unsupported */

/* check point a long transaction */

/* BOOL aceRollback( BOOL isAbort ); -- unsupported */

/* rollback. If isAbort, rollback to start of transaction,

* otherwise, rollback to last checkpoint.

*/

BOOL aceCommit( BOOL releaseLock );

 

/* close the database. */

void aceClose(ACE*, BOOL autoCommit );

 

/* high-granularity operations */

 

/* list the ace classes */

char** aceClasses(void); /* a null-terminated list of C-strings */

int aceClassSize(char* classname);

 

/* models */

ACEINSTANCE aceOpenModel(char* classname);

/* ask the database you are connected to to re-read its models */

BLOCKING BOOL aceReadModels(void);

 

/* features of instances */

char* aceName(ACEKEY);

char* aceClassName(ACEKEY);

char* aceInstanceName(char* class, char* instance);

ACETAG aceTagOf(char* anyString, BOOL create);

ACEKEY aceKeyOf(char* instancename, BOOL create);

ACEKEY aceKeyOfInstance(ACEINSTANCE);

 

/* instances */

KEYSET aceClassExtent(char* classname);

/* get a number of objects at once */

BLOCKING INSTANCESET aceOpenAllInstancesNamed(char** names,

BOOL needWriteLock);

BLOCKING INSTANCESET aceOpenAllInstances(KEYSET,

BOOL needWriteLock);

/* get a single object */

BLOCKING ACEINSTANCE aceOpenInstanceNamed(char* instanceName,

BOOL needWriteLock);

BLOCKING ACEINSTANCE aceOpenInstance(ACEKEY,BOOL needWriteLock);

/* finish with an object. */

BOOL aceCloseInstance(ACEINSTANCE, BOOL commit);

BOOL aceCloseAllInstances(INSTANCESET, BOOL commit);

 

/* grep. Optionally, do LongGrep. */

KEYSET aceGrep(char* pattern, BOOL doLong);

KEYSET aceGrepFrom(KEYSET, char* pattern, BOOL doLong);

 

/* filtering a set of keys/instances */

INSTANCESET aceFilterInstances(char* predicate,

INSTANCESET src);

KEYSET aceFilterKeys(KEYSET src, char* predicate);

 

/* reading ace-format */

BOOL aceParse(char*);

BOOL aceParseFile(FILE*);

 

/* editing objects */

/* int aceEditInstances(INSTANCESET, char* command); -- unsupported */

BOOL aceEditKeyset(KEYSET, char* command);

 

/* killing objects */

void aceKillName(char* instancename);

void aceKillKey(ACEKEY);

void aceKillKeyset(KEYSET);

 

/* output */

/* %a = ace %h = human %j = java %p = perl, etc.

* a ‘*’ following the format specifier indicates that either an

* entire keyset or an entire instanceset should be dumped.

* a ‘i’ following the format specifier indicates that we are working

* with an INSTANCE (or INSTANCESET) instead of a ACEKEY or KEYSET.

*/

BOOL acefprintf(FILE*,char* format, ...);

BOOL acesprintf(char*,char* format, size_t buflen);

 

/* new unique objects */

ACEKEY aceMakeKeyFromFormat(char* classname, char* format);

 

/* tree manipulation */

BOOL aceTestInstance(ACEINSTANCE, char* predicate,

BOOL moveCurrent);

/* mark/goto */

ACEMARK aceMark( ACEINSTANCE ); /* create the ACEMARK for the caller */

BOOL aceMarkFree( ACEMARK ); /* who frees it using this routine */

BOOL aceGotoMark( ACEINSTANCE, ACEMARK target );

/* search object */

BOOL aceGotoTag( ACEINSTANCE, ACEKEY target );

BOOL aceHasTag( ACEINSTANCE, ACEKEY target );

/* navigate object */

BOOL aceNextChild( ACEINSTANCE );

BOOL aceGotoChild( ACEINSTANCE );

/* verify the current type */

BOOL aceCheckType( ACEINSTANCE , ACETYPE );

ACETYPE aceGetType( ACEINSTANCE );

/* verify the presence of a type in the current follow set */

BOOL aceFollowedBy( ACEINSTANCE , ACETYPE );

 

DATETYPE aceGetDateType( ACEINSTANCE );

char* aceGetText( ACEINSTANCE );

int aceGetInteger( ACEINSTANCE );

float aceGetFloat( ACEINSTANCE );

ACEKEY aceGetKey( ACEINSTANCE );

ACETAG aceGetTag( ACEINSTANCE );

 

/* in place modifications of the object */

BOOL aceReplaceKey( ACEINSTANCE, ACEKEY );

BOOL aceReplaceTag( ACEINSTANCE, ACETAG );

BOOL aceReplaceInteger( ACEINSTANCE, int );

BOOL aceReplaceText( ACEINSTANCE, char* );

BOOL aceReplaceFloat( ACEINSTANCE, float );

/* modify the position immediately to the right */

BOOL aceAddKey( ACEINSTANCE, ACEKEY );

BOOL aceAddTag( ACEINSTANCE, ACETAG );

BOOL aceAddInteger( ACEINSTANCE, int );

BOOL aceAddText( ACEINSTANCE, char* );

BOOL aceAddFloat( ACEINSTANCE, float );

/* attach comments to the current position */

/* comments will no longer be inserted into the structure of the

* object itself, but will be added along a third dimension.

*/

BOOL aceAddComment( ACEINSTANCE, char* );

char* aceGetComment( ACEINSTANCE );

/* remove everything to the right of the current position,

* as well as the current node.

*/

BOOL acePrune( ACEINSTANCE );

 

/* query interface */

/* there is a problem here, since the results is actually a table */

/* what is the structure of the data structure returned, and do we */

/* need to provide methods for accessing it? */

BOOL aceQuery(ACETABLE init, char* queryString, ACETABLE results);

BOOL aceQueryInstance(ACEINSTANCE, char* queryString, ACEINSTANCE result);

ACETABLE aceMakeInstanceTable(ACEINSTANCE, int depth);

BOOL aceTableCell(ACETABLE table, int i, int j);