diff options
Diffstat (limited to 'slib.info-2')
-rw-r--r-- | slib.info-2 | 1193 |
1 files changed, 1193 insertions, 0 deletions
diff --git a/slib.info-2 b/slib.info-2 new file mode 100644 index 0000000..f1c31c5 --- /dev/null +++ b/slib.info-2 @@ -0,0 +1,1193 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Records, Next: Base Table, Prev: Queues, Up: Data Structures + +Records +======= + + `(require 'record)' + + The Record package provides a facility for user to define their own +record data types. + + - Function: make-record-type TYPE-NAME FIELD-NAMES + Returns a "record-type descriptor", a value representing a new data + type disjoint from all others. The TYPE-NAME argument must be a + string, but is only used for debugging purposes (such as the + printed representation of a record of the new type). The + FIELD-NAMES argument is a list of symbols naming the "fields" of a + record of the new type. It is an error if the list contains any + duplicates. It is unspecified how record-type descriptors are + represented. + + - Function: record-constructor RTD [FIELD-NAMES] + Returns a procedure for constructing new members of the type + represented by RTD. The returned procedure accepts exactly as + many arguments as there are symbols in the given list, + FIELD-NAMES; these are used, in order, as the initial values of + those fields in a new record, which is returned by the constructor + procedure. The values of any fields not named in that list are + unspecified. The FIELD-NAMES argument defaults to the list of + field names in the call to `make-record-type' that created the + type represented by RTD; if the FIELD-NAMES argument is provided, + it is an error if it contains any duplicates or any symbols not in + the default list. + + - Function: record-predicate RTD + Returns a procedure for testing membership in the type represented + by RTD. The returned procedure accepts exactly one argument and + returns a true value if the argument is a member of the indicated + record type; it returns a false value otherwise. + + - Function: record-accessor RTD FIELD-NAME + Returns a procedure for reading the value of a particular field of + a member of the type represented by RTD. The returned procedure + accepts exactly one argument which must be a record of the + appropriate type; it returns the current value of the field named + by the symbol FIELD-NAME in that record. The symbol FIELD-NAME + must be a member of the list of field-names in the call to + `make-record-type' that created the type represented by RTD. + + - Function: record-modifier RTD FIELD-NAME + Returns a procedure for writing the value of a particular field of + a member of the type represented by RTD. The returned procedure + accepts exactly two arguments: first, a record of the appropriate + type, and second, an arbitrary Scheme value; it modifies the field + named by the symbol FIELD-NAME in that record to contain the given + value. The returned value of the modifier procedure is + unspecified. The symbol FIELD-NAME must be a member of the list + of field-names in the call to `make-record-type' that created the + type represented by RTD. + + - Function: record? OBJ + Returns a true value if OBJ is a record of any type and a false + value otherwise. Note that `record?' may be true of any Scheme + value; of course, if it returns true for some particular value, + then `record-type-descriptor' is applicable to that value and + returns an appropriate descriptor. + + - Function: record-type-descriptor RECORD + Returns a record-type descriptor representing the type of the given + record. That is, for example, if the returned descriptor were + passed to `record-predicate', the resulting predicate would return + a true value when passed the given record. Note that it is not + necessarily the case that the returned descriptor is the one that + was passed to `record-constructor' in the call that created the + constructor procedure that created the given record. + + - Function: record-type-name RTD + Returns the type-name associated with the type represented by rtd. + The returned value is `eqv?' to the TYPE-NAME argument given in + the call to `make-record-type' that created the type represented by + RTD. + + - Function: record-type-field-names RTD + Returns a list of the symbols naming the fields in members of the + type represented by RTD. The returned value is `equal?' to the + field-names argument given in the call to `make-record-type' that + created the type represented by RTD. + + +File: slib.info, Node: Base Table, Next: Relational Database, Prev: Records, Up: Data Structures + +Base Table +========== + + A base table implementation using Scheme association lists is +available as the value of the identifier `alist-table' after doing: + + (require 'alist-table) + + Association list base tables are suitable for small databases and +support all Scheme types when temporary and readable/writeable Scheme +types when saved. I hope support for other base table implementations +will be added in the future. + + This rest of this section documents the interface for a base table +implementation from which the *Note Relational Database:: package +constructs a Relational system. It will be of interest primarily to +those wishing to port or write new base-table implementations. + + All of these functions are accessed through a single procedure by +calling that procedure with the symbol name of the operation. A +procedure will be returned if that operation is supported and `#f' +otherwise. For example: + + (require 'alist-table) + (define open-base (alist-table 'make-base)) + make-base => *a procedure* + (define foo (alist-table 'foo)) + foo => #f + + - Function: make-base FILENAME KEY-DIMENSION COLUMN-TYPES + Returns a new, open, low-level database (collection of tables) + associated with FILENAME. This returned database has an empty + table associated with CATALOG-ID. The positive integer + KEY-DIMENSION is the number of keys composed to make a PRIMARY-KEY + for the catalog table. The list of symbols COLUMN-TYPES describes + the types of each column for that table. If the database cannot + be created as specified, `#f' is returned. + + Calling the `close-base' method on this database and possibly other + operations will cause FILENAME to be written to. If FILENAME is + `#f' a temporary, non-disk based database will be created if such + can be supported by the base table implelentation. + + - Function: open-base FILENAME MUTABLE + Returns an open low-level database associated with FILENAME. If + MUTABLE? is `#t', this database will have methods capable of + effecting change to the database. If MUTABLE? is `#f', only + methods for inquiring the database will be available. If the + database cannot be opened as specified `#f' is returned. + + Calling the `close-base' (and possibly other) method on a MUTABLE? + database will cause FILENAME to be written to. + + - Function: write-base LLDB FILENAME + Causes the low-level database LLDB to be written to FILENAME. If + the write is successful, also causes LLDB to henceforth be + associated with FILENAME. Calling the `close-database' (and + possibly other) method on LLDB may cause FILENAME to be written + to. If FILENAME is `#f' this database will be changed to a + temporary, non-disk based database if such can be supported by the + underlying base table implelentation. If the operations completed + successfully, `#t' is returned. Otherwise, `#f' is returned. + + - Function: sync-base LLDB + Causes the file associated with the low-level database LLDB to be + updated to reflect its current state. If the associated filename + is `#f', no action is taken and `#f' is returned. If this + operation completes successfully, `#t' is returned. Otherwise, + `#f' is returned. + + - Function: close-base LLDB + Causes the low-level database LLDB to be written to its associated + file (if any). If the write is successful, subsequent operations + to LLDB will signal an error. If the operations complete + successfully, `#t' is returned. Otherwise, `#f' is returned. + + - Function: make-table LLDB KEY-DIMENSION COLUMN-TYPES + Returns the BASE-ID for a new base table, otherwise returns `#f'. + The base table can then be opened using `(open-table LLDB + BASE-ID)'. The positive integer KEY-DIMENSION is the number of + keys composed to make a PRIMARY-KEY for this table. The list of + symbols COLUMN-TYPES describes the types of each column. + + - Constant: catalog-id + A constant BASE-ID suitable for passing as a parameter to + `open-table'. CATALOG-ID will be used as the base table for the + system catalog. + + - Function: open-table LLDB BASE-ID KEY-DIMENSION COLUMN-TYPES + Returns a HANDLE for an existing base table in the low-level + database LLDB if that table exists and can be opened in the mode + indicated by MUTABLE?, otherwise returns `#f'. + + As with `make-table', the positive integer KEY-DIMENSION is the + number of keys composed to make a PRIMARY-KEY for this table. The + list of symbols COLUMN-TYPES describes the types of each column. + + - Function: kill-table LLDB BASE-ID KEY-DIMENSION COLUMN-TYPES + Returns `#t' if the base table associated with BASE-ID was removed + from the low level database LLDB, and `#f' otherwise. + + - Function: make-keyifier-1 TYPE + Returns a procedure which accepts a single argument which must be + of type TYPE. This returned procedure returns an object suitable + for being a KEY argument in the functions whose descriptions + follow. + + Any 2 arguments of the supported type passed to the returned + function which are not `equal?' must result in returned values + which are not `equal?'. + + - Function: make-list-keyifier KEY-DIMENSION TYPES + The list of symbols TYPES must have at least KEY-DIMENSION + elements. Returns a procedure which accepts a list of length + KEY-DIMENSION and whose types must corresopond to the types named + by TYPES. This returned procedure combines the elements of its + list argument into an object suitable for being a KEY argument in + the functions whose descriptions follow. + + Any 2 lists of supported types (which must at least include + symbols and non-negative integers) passed to the returned function + which are not `equal?' must result in returned values which are not + `equal?'. + + - Function: make-key-extractor KEY-DIMENSION TYPES COLUMN-NUMBER + Returns a procedure which accepts objects produced by application + of the result of `(make-list-keyifier KEY-DIMENSION TYPES)'. This + procedure returns a KEY which is `equal?' to the COLUMN-NUMBERth + element of the list which was passed to create COMBINED-KEY. The + list TYPES must have at least KEY-DIMENSION elements. + + - Function: make-key->list KEY-DIMENSION TYPES + Returns a procedure which accepts objects produced by application + of the result of `(make-list-keyifier KEY-DIMENSION TYPES)'. This + procedure returns a list of KEYs which are elementwise `equal?' to + the list which was passed to create COMBINED-KEY. + +In the following functions, the KEY argument can always be assumed to +be the value returned by a call to a *keyify* routine. + + - Function: for-each-key HANDLE PROCEDURE + Calls PROCEDURE once with each KEY in the table opened in HANDLE + in an unspecified order. An unspecified value is returned. + + - Function: map-key HANDLE PROCEDURE + Returns a list of the values returned by calling PROCEDURE once + with each KEY in the table opened in HANDLE in an unspecified + order. + + - Function: ordered-for-each-key HANDLE PROCEDURE + Calls PROCEDURE once with each KEY in the table opened in HANDLE + in the natural order for the types of the primary key fields of + that table. An unspecified value is returned. + + - Function: present? HANDLE KEY + Returns a non-`#f' value if there is a row associated with KEY in + the table opened in HANDLE and `#f' otherwise. + + - Function: delete HANDLE KEY + Removes the row associated with KEY from the table opened in + HANDLE. An unspecified value is returned. + + - Function: make-getter KEY-DIMENSION TYPES + Returns a procedure which takes arguments HANDLE and KEY. This + procedure returns a list of the non-primary values of the relation + (in the base table opened in HANDLE) whose primary key is KEY if + it exists, and `#f' otherwise. + + - Function: make-putter KEY-DIMENSION TYPES + Returns a procedure which takes arguments HANDLE and KEY and + VALUE-LIST. This procedure associates the primary key KEY with + the values in VALUE-LIST (in the base table opened in HANDLE) and + returns an unspecified value. + + - Function: supported-type? SYMBOL + Returns `#t' if SYMBOL names a type allowed as a column value by + the implementation, and `#f' otherwise. At a minimum, an + implementation must support the types `integer', `symbol', + `string', `boolean', and `base-id'. + + - Function: supported-key-type? SYMBOL + Returns `#t' if SYMBOL names a type allowed as a key value by the + implementation, and `#f' otherwise. At a minimum, an + implementation must support the types `integer', and `symbol'. + +`integer' + Scheme exact integer. + +`symbol' + Scheme symbol. + +`boolean' + `#t' or `#f'. + +`base-id' + Objects suitable for passing as the BASE-ID parameter to + `open-table'. The value of CATALOG-ID must be an acceptable + `base-id'. + + +File: slib.info, Node: Relational Database, Next: Weight-Balanced Trees, Prev: Base Table, Up: Data Structures + +Relational Database +=================== + + `(require 'relational-database)' + + This package implements a database system inspired by the Relational +Model (`E. F. Codd, A Relational Model of Data for Large Shared Data +Banks'). An SLIB relational database implementation can be created +from any *Note Base Table:: implementation. + +* Menu: + +* Motivations:: Database Manifesto +* Creating and Opening Relational Databases:: +* Relational Database Operations:: +* Table Operations:: +* Catalog Representation:: +* Unresolved Issues:: +* Database Utilities:: 'database-utilities + + +File: slib.info, Node: Motivations, Next: Creating and Opening Relational Databases, Prev: Relational Database, Up: Relational Database + +Motivations +----------- + + Most nontrivial programs contain databases: Makefiles, configure +scripts, file backup, calendars, editors, source revision control, CAD +systems, display managers, menu GUIs, games, parsers, debuggers, +profilers, and even error reporting are all rife with databases. Coding +databases is such a common activity in programming that many may not be +aware of how often they do it. + + A database often starts as a dispatch in a program. The author, +perhaps because of the need to make the dispatch configurable, the need +for correlating dispatch in other routines, or because of changes or +growth, devises a data structure to contain the information, a routine +for interpreting that data structure, and perhaps routines for +augmenting and modifying the stored data. The dispatch must be +converted into this form and tested. + + The programmer may need to devise an interactive program for enabling +easy examination and modification of the information contained in this +database. Often, in an attempt to foster modularity and avoid delays in +release, intermediate file formats for the database information are +devised. It often turns out that users prefer modifying these +intermediate files with a text editor to using the interactive program +in order to do operations (such as global changes) not forseen by the +program's author. + + In order to address this need, the concientous software engineer may +even provide a scripting language to allow users to make repetitive +database changes. Users will grumble that they need to read a large +manual and learn yet another programming language (even if it *almost* +has language "xyz" syntax) in order to do simple configuration. + + All of these facilities need to be designed, coded, debugged, +documented, and supported; often causing what was very simple in concept +to become a major developement project. + + This view of databases just outlined is somewhat the reverse of the +view of the originators of the "Relational Model" of database +abstraction. The relational model was devised to unify and allow +interoperation of large multi-user databases running on diverse +platforms. A fairly general purpose "Comprehensive Language" for +database manipulations is mandated (but not specified) as part of the +relational model for databases. + + One aspect of the Relational Model of some importance is that the +"Comprehensive Language" must be expressible in some form which can be +stored in the database. This frees the programmer from having to make +programs data-driven in order to use a database. + + This package includes as one of its basic supported types Scheme +"expression"s. This type allows expressions as defined by the Scheme +standards to be stored in the database. Using `slib:eval' retrieved +expressions can be evaluated (in the top-level environment). Scheme's +`lambda' facilitates closure of environments, modularity, etc. so that +procedures (which could not be stored directly most databases) can +still be effectively retrieved. Since `slib:eval' evaluates +expressions in the top-level environment, built-in and user defined +procedures can be easily accessed by name. + + This package's purpose is to standardize (through a common interface) +database creation and usage in Scheme programs. The relational model's +provision for inclusion of language expressions as data as well as the +description (in tables, of course) of all of its tables assures that +relational databases are powerful enough to assume the roles currently +played by thousands of ad-hoc routines and data formats. + +Such standardization to a relational-like model brings many benefits: + + * Tables, fields, domains, and types can be dealt with by name in + programs. + + * The underlying database implementation can be changed (for + performance or other reasons) by changing a single line of code. + + * The formats of tables can be easily extended or changed without + altering code. + + * Consistency checks are specified as part of the table descriptions. + Changes in checks need only occur in one place. + + * All the configuration information which the developer wishes to + group together is easily grouped, without needing to change + programs aware of only some of these tables. + + * Generalized report generators, interactive entry programs, and + other database utilities can be part of a shared library. The + burden of adding configurability to a program is greatly reduced. + + * Scheme is the "comprehensive language" for these databases. + Scripting for configuration no longer needs to be in a separate + language with additional documentation. + + * Scheme's latent types mesh well with the strict typing and logical + requirements of the relational model. + + * Portable formats allow easy interchange of data. The included + table descriptions help prevent misinterpretation of format. + + +File: slib.info, Node: Creating and Opening Relational Databases, Next: Relational Database Operations, Prev: Motivations, Up: Relational Database + +Creating and Opening Relational Databases +----------------------------------------- + + - Function: make-relational-system BASE-TABLE-IMPLEMENTATION + Returns a procedure implementing a relational database using the + BASE-TABLE-IMPLEMENTATION. + + All of the operations of a base table implementation are accessed + through a procedure defined by `require'ing that implementation. + Similarly, all of the operations of the relational database + implementation are accessed through the procedure returned by + `make-relational-system'. For instance, a new relational database + could be created from the procedure returned by + `make-relational-system' by: + + (require 'alist-table) + (define relational-alist-system + (make-relational-system alist-table)) + (define create-alist-database + (relational-alist-system 'create-database)) + (define my-database + (create-alist-database "mydata.db")) + +What follows are the descriptions of the methods available from +relational system returned by a call to `make-relational-system'. + + - Function: create-database FILENAME + Returns an open, nearly empty relational database associated with + FILENAME. The only tables defined are the system catalog and + domain table. Calling the `close-database' method on this database + and possibly other operations will cause FILENAME to be written + to. If FILENAME is `#f' a temporary, non-disk based database will + be created if such can be supported by the underlying base table + implelentation. If the database cannot be created as specified + `#f' is returned. For the fields and layout of descriptor tables, + *Note Catalog Representation:: + + - Function: open-database FILENAME MUTABLE? + Returns an open relational database associated with FILENAME. If + MUTABLE? is `#t', this database will have methods capable of + effecting change to the database. If MUTABLE? is `#f', only + methods for inquiring the database will be available. Calling the + `close-database' (and possibly other) method on a MUTABLE? + database will cause FILENAME to be written to. If the database + cannot be opened as specified `#f' is returned. + + +File: slib.info, Node: Relational Database Operations, Next: Table Operations, Prev: Creating and Opening Relational Databases, Up: Relational Database + +Relational Database Operations +------------------------------ + +These are the descriptions of the methods available from an open +relational database. A method is retrieved from a database by calling +the database with the symbol name of the operation. For example: + + (define my-database + (create-alist-database "mydata.db")) + (define telephone-table-desc + ((my-database 'create-table) 'telephone-table-desc)) + + - Function: close-database + Causes the relational database to be written to its associated + file (if any). If the write is successful, subsequent operations + to this database will signal an error. If the operations completed + successfully, `#t' is returned. Otherwise, `#f' is returned. + + - Function: write-database FILENAME + Causes the relational database to be written to FILENAME. If the + write is successful, also causes the database to henceforth be + associated with FILENAME. Calling the `close-database' (and + possibly other) method on this database will cause FILENAME to be + written to. If FILENAME is `#f' this database will be changed to + a temporary, non-disk based database if such can be supported by + the underlying base table implelentation. If the operations + completed successfully, `#t' is returned. Otherwise, `#f' is + returned. + + - Function: table-exists? TABLE-NAME + Returns `#t' if TABLE-NAME exists in the system catalog, otherwise + returns `#f'. + + - Function: open-table TABLE-NAME MUTABLE? + Returns a "methods" procedure for an existing relational table in + this database if it exists and can be opened in the mode indicated + by MUTABLE?, otherwise returns `#f'. + +These methods will be present only in databases which are MUTABLE?. + + - Function: delete-table TABLE-NAME + Removes and returns the TABLE-NAME row from the system catalog if + the table or view associated with TABLE-NAME gets removed from the + database, and `#f' otherwise. + + - Function: create-table TABLE-DESC-NAME + Returns a methods procedure for a new (open) relational table for + describing the columns of a new base table in this database, + otherwise returns `#f'. For the fields and layout of descriptor + tables, *Note Catalog Representation::. + + - Function: create-table TABLE-NAME TABLE-DESC-NAME + Returns a methods procedure for a new (open) relational table with + columns as described by TABLE-DESC-NAME, otherwise returns `#f'. + + - Function: create-view ?? + - Function: project-table ?? + - Function: restrict-table ?? + - Function: cart-prod-tables ?? + Not yet implemented. + + +File: slib.info, Node: Table Operations, Next: Catalog Representation, Prev: Relational Database Operations, Up: Relational Database + +Table Operations +---------------- + +These are the descriptions of the methods available from an open +relational table. A method is retrieved from a table by calling the +table with the symbol name of the operation. For example: + + (define telephone-table-desc + ((my-database 'create-table) 'telephone-table-desc)) + (require 'common-list-functions) + (define ndrp (telephone-table-desc 'row:insert)) + (ndrp '(1 #t name #f string)) + (ndrp '(2 #f telephone + (lambda (d) + (and (string? d) (> (string-length d) 2) + (every + (lambda (c) + (memv c '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 + #\+ #\( #\ #\) #\-))) + (string->list d)))) + string)) + +Operations on a single column of a table are retrieved by giving the +column name as the second argument to the methods procedure. For +example: + + (define column-ids ((telephone-table-desc 'get* 'column-number))) + +Some operations described below require primary key arguments. Primary +keys arguments are denoted KEY1 KEY2 .... It is an error to call an +operation for a table which takes primary key arguments with the wrong +number of primary keys for that table. + +The term "row" used below refers to a Scheme list of values (one for +each column) in the order specified in the descriptor (table) for this +table. Missing values appear as `#f'. Primary keys may not be missing. + + - Function: get KEY1 KEY2 ... + Returns the value for the specified column of the row associated + with primary keys KEY1, KEY2 ... if it exists, or `#f' otherwise. + + - Function: get* + Returns a list of the values for the specified column for all rows + in this table. + + - Function: row:retrieve KEY1 KEY2 ... + Returns the row associated with primary keys KEY1, KEY2 ... if it + exists, or `#f' otherwise. + + - Function: row:retrieve* + Returns a list of all rows in this table. + + - Function: row:remove KEY1 KEY2 ... + Removes and returns the row associated with primary keys KEY1, + KEY2 ... if it exists, or `#f' otherwise. + + - Function: row:remove* + Removes and returns a list of all rows in this table. + + - Function: row:delete KEY1 KEY2 ... + Deletes the row associated with primary keys KEY1, KEY2 ... if it + exists. The value returned is unspecified. + + - Function: row:delete* + Deletes all rows in this table. The value returned is + unspecified. The descriptor table and catalog entry for this + table are not affected. + + - Function: row:update ROW + Adds the row, ROW, to this table. If a row for the primary key(s) + specified by ROW already exists in this table, it will be + overwritten. The value returned is unspecified. + + - Function: row:update* ROWS + Adds each row in the list ROWS, to this table. If a row for the + primary key specified by an element of ROWS already exists in this + table, it will be overwritten. The value returned is unspecified. + + - Function: row:insert ROW + Adds the row ROW to this table. If a row for the primary key(s) + specified by ROW already exists in this table an error is + signaled. The value returned is unspecified. + + - Function: row:insert* ROWS + Adds each row in the list ROWS, to this table. If a row for the + primary key specified by an element of ROWS already exists in this + table, an error is signaled. The value returned is unspecified. + + - Function: for-each-row PROC + Calls PROC with each ROW in this table in the natural ordering for + the primary key types. *Real* relational programmers would use + some least-upper-bound join for every row to get them in order; + But we don't have joins yet. + + - Function: close-table + Subsequent operations to this table will signal an error. + + - Constant: column-names + - Constant: column-foreigns + - Constant: column-domains + - Constant: column-types + Return a list of the column names, foreign-key table names, domain + names, or type names respectively for this table. These 4 methods + are different from the others in that the list is returned, rather + than a procedure to obtain the list. + + - Constant: primary-limit + Returns the number of primary keys fields in the relations in this + table. + + +File: slib.info, Node: Catalog Representation, Next: Unresolved Issues, Prev: Table Operations, Up: Relational Database + +Catalog Representation +---------------------- + +Each database (in an implementation) has a "system catalog" which +describes all the user accessible tables in that database (including +itself). + +The system catalog base table has the following fields. `PRI' +indicates a primary key for that table. + + PRI table-name + column-limit the highest column number + coltab-name descriptor table name + bastab-id data base table identifier + user-integrity-rule + view-procedure A scheme thunk which, when called, + produces a handle for the view. coltab + and bastab are specified if and only if + view-procedure is not. + +Descriptors for base tables (not views) are tables (pointed to by +system catalog). Descriptor (base) tables have the fields: + + PRI column-number sequential integers from 1 + primary-key? boolean TRUE for primary key components + column-name + column-integrity-rule + domain-name + +A "primary key" is any column marked as `primary-key?' in the +corresponding descriptor table. All the `primary-key?' columns must +have lower column numbers than any non-`primary-key?' columns. Every +table must have at least one primary key. Primary keys must be +sufficient to distinguish all rows from each other in the table. All of +the system defined tables have a single primary key. + +This package currently supports tables having from 1 to 4 primary keys +if there are non-primary columns, and any (natural) number if *all* +columns are primary keys. If you need more than 4 primary keys, I would +like to hear what you are doing! + +A "domain" is a category describing the allowable values to occur in a +column. It is described by a (base) table with the fields: + + PRI domain-name + foreign-table + domain-integrity-rule + type-id + type-param + +The "type-id" field value is a symbol. This symbol may be used by the +underlying base table implementation in storing that field. + +If the `foreign-table' field is non-`#f' then that field names a table +from the catalog. The values for that domain must match a primary key +of the table referenced by the TYPE-PARAM (or `#f', if allowed). This +package currently does not support composite foreign-keys. + +The types for which support is planned are: + atom + symbol + string [<length>] + number [<base>] + money <currency> + date-time + boolean + + foreign-key <table-name> + expression + virtual <expression> + + +File: slib.info, Node: Unresolved Issues, Next: Database Utilities, Prev: Catalog Representation, Up: Relational Database + +Unresolved Issues +----------------- + + Although `rdms.scm' is not large I found it very difficult to write +(six rewrites). I am not aware of any other examples of a generalized +relational system (although there is little new in CS). I left out +several aspects of the Relational model in order to simplify the job. +The major features lacking (which might be addressed portably) are +views, transaction boundaries, and protection. + + Protection needs a model for specifying priveledges. Given how +operations are accessed from handles it should not be difficult to +restrict table accesses to those allowed for that user. + + The system catalog has a field called `view-procedure'. This should +allow a purely functional implementation of views. This will work but +is unsatisfying for views resulting from a "select"ion (subset of +rows); for whole table operations it will not be possible to reduce the +number of keys scanned over when the selection is specified only by an +opaque procedure. + + Transaction boundaries present the most intriguing area. Transaction +boundaries are actually a feature of the "Comprehensive Language" of the +Relational database and not of the database. Scheme would seem to +provide the opportunity for an extremely clean semantics for transaction +boundaries since the builtin procedures with side effects are small in +number and easily identified. + + These side-effect builtin procedures might all be portably redefined +to versions which properly handled transactions. Compiled library +routines would need to be recompiled as well. Many system extensions +(delete-file, system, etc.) would also need to be redefined. + +There are 2 scope issues that must be resolved for multiprocess +transaction boundaries: + +Process scope + The actions captured by a transaction should be only for the + process which invoked the start of transaction. Although standard + Scheme does not provide process primitives as such, `dynamic-wind' + would provide a workable hook into process switching for many + implementations. + +Shared utilities with state + Some shared utilities have state which should *not* be part of a + transaction. An example would be calling a pseudo-random number + generator. If the success of a transaction depended on the + pseudo-random number and failed, the state of the generator would + be set back. Subsequent calls would keep returning the same + number and keep failing. + + Pseudo-random number generators are not reentrant and so would + require locks in order to operate properly in a multiprocess + environment. Are all examples of utilities whose state should not + part of transactions also non-reentrant? If so, perhaps + suspending transaction capture for the duration of locks would fix + it. + + +File: slib.info, Node: Database Utilities, Prev: Unresolved Issues, Up: Relational Database + +Database Utilities +------------------ + + `(require 'database-utilities)' + +This enhancement wraps a utility layer on `relational-database' which +provides: + * Automatic loading of the appropriate base-table package when + opening a database. + + * Automatic execution of initialization commands stored in database. + + * Transparent execution of database commands stored in `*commands*' + table in database. + +Also included are utilities which provide: + * Data definition from Scheme lists and + + * Report generation + +for any SLIB relational database. + + - Function: create-database FILENAME BASE-TABLE-TYPE + Returns an open, nearly empty enhanced (with `*commands*' table) + relational database (with base-table type BASE-TABLE-TYPE) + associated with FILENAME. + + - Function: open-database FILENAME + - Function: open-database FILENAME BASE-TABLE-TYPE + Returns an open enchanced relational database associated with + FILENAME. The database will be opened with base-table type + BASE-TABLE-TYPE) if supplied. If BASE-TABLE-TYPE is not supplied, + `open-database' will attempt to deduce the correct + base-table-type. If the database can not be opened or if it lacks + the `*commands*' table, `#f' is returned. + + - Function: open-database! FILENAME + - Function: open-database! FILENAME BASE-TABLE-TYPE + Returns *mutable* open enchanced relational database ... + +The table `*commands*' in an "enhanced" relational-database has the +fields (with domains): + PRI name symbol + parameters parameter-list + procedure expression + documentation string + + The `parameters' field is a foreign key (domain `parameter-list') of +the `*catalog-data*' table and should have the value of a table +described by `*parameter-columns*'. This `parameter-list' table +describes the arguments suitable for passing to the associated command. +The intent of this table is to be of a form such that different +user-interfaces (for instance, pull-down menus or plain-text queries) +can operate from the same table. A `parameter-list' table has the +following fields: + PRI index uint + name symbol + arity parameter-arity + domain domain + default expression + documentation string + + The `arity' field can take the values: + +`single' + Requires a single parameter of the specified domain. + +`optional' + A single parameter of the specified domain or zero parameters is + acceptable. + +`boolean' + A single boolean parameter or zero parameters (in which case `#f' + is substituted) is acceptable. + +`nary' + Any number of parameters of the specified domain are acceptable. + The argument passed to the command function is always a list of the + parameters. + +`nary1' + One or more of parameters of the specified domain are acceptable. + The argument passed to the command function is always a list of the + parameters. + + The `domain' field specifies the domain which a parameter or +parameters in the `index'th field must satisfy. + + The `default' field is an expression whose value is either `#f' or a +procedure of no arguments which returns a parameter or parameter list +as appropriate. If the expression's value is `#f' then no default is +appropriate for this parameter. Note that since the `default' +procedure is called every time a default parameter is needed for this +column, "sticky" defaults can be implemented using shared state with +the domain-integrity-rule. + +Invoking Commands +................. + + When an enhanced relational-database is called with a symbol which +matches a NAME in the `*commands*' table, the associated procedure +expression is evaluated and applied to the enhanced +relational-database. A procedure should then be returned which the user +can invoke on (optional) arguments. + + The command `*initialize*' is special. If present in the +`*commands*' table, `open-database' or `open-database!' will return the +value of the `*initialize*' command. Notice that arbitrary code can be +run when the `*initialize*' procedure is automatically applied to the +enhanced relational-database. + + Note also that if you wish to shadow or hide from the user +relational-database methods described in *Note Relational Database +Operations::, this can be done by a dispatch in the closure returned by +the `*initialize*' expression rather than by entries in the +`*commands*' table if it is desired that the underlying methods remain +accessible to code in the `*commands*' table. + + - Function: make-command-server RDB TABLE-NAME + Returns a procedure of 2 arguments, a (symbol) command and a + call-back procedure. When this returned procedure is called, it + looks up COMMAND in table TABLE-NAME and calls the call-back + procedure with arguments: + COMMAND + The COMMAND + + COMMAND-VALUE + The result of evaluating the expression in the PROCEDURE + field of TABLE-NAME and calling it with RDB. + + PARAMETER-NAME + A list of the "official" name of each parameter. Corresponds + to the `name' field of the COMMAND's parameter-table. + + POSITIONS + A list of the positive integer index of each parameter. + Corresponds to the `index' field of the COMMAND's + parameter-table. + + ARITIES + A list of the arities of each parameter. Corresponds to the + `arity' field of the COMMAND's parameter-table. For a + description of `arity' see table above. + + DEFAULTS + A list of the defaults for each parameter. Corresponds to + the `defaults' field of the COMMAND's parameter-table. + + DOMAIN-INTEGRITY-RULES + A list of procedures (one for each parameter) which tests + whether a value for a parameter is acceptable for that + parameter. The procedure should be called with each datum in + the list for `nary' arity parameters. + + ALIASES + A list of lists of `(alias parameter-name)'. There can be + more than one alias per PARAMETER-NAME. + + For information about parameters, *Note Parameter lists::. Here is an +example of setting up a command with arguments and parsing those +arguments from a `getopt' style argument list (*note Getopt::.). + + (require 'database-utilities) + (require 'parameters) + (require 'getopt) + + (define my-rdb (create-database #f 'alist-table)) + + (define-tables my-rdb + '(foo-params + *parameter-columns* + *parameter-columns* + ((1 first-argument single string "hithere" "first argument") + (2 flag boolean boolean #f "a flag"))) + '(foo-pnames + ((name string)) + ((parameter-index uint)) + (("l" 1) + ("a" 2))) + '(my-commands + ((name symbol)) + ((parameters parameter-list) + (parameter-names parameter-name-translation) + (procedure expression) + (documentation string)) + ((foo + foo-params + foo-pnames + (lambda (rdb) (lambda (foo aflag) (print foo aflag))) + "test command arguments")))) + + (define (dbutil:serve-command-line rdb command-table + command argc argv) + (set! argv (if (vector? argv) (vector->list argv) argv)) + ((make-command-server rdb command-table) + command + (lambda (comname comval options positions + arities types defaults dirs aliases) + (apply comval (getopt->arglist argc argv options positions + arities types defaults dirs aliases))))) + + (define (test) + (set! *optind* 1) + (dbutil:serve-command-line + my-rdb 'my-commands 'foo 4 '("dummy" "-l" "foo" "-a"))) + (test) + -| + "foo" #t + + Some commands are defined in all extended relational-databases. The +are called just like *Note Relational Database Operations::. + + - Function: add-domain DOMAIN-ROW + Adds DOMAIN-ROW to the "domains" table if there is no row in the + domains table associated with key `(car DOMAIN-ROW)' and returns + `#t'. Otherwise returns `#f'. + + For the fields and layout of the domain table, *Note Catalog + Representation:: + + - Function: delete-domain DOMAIN-NAME + Removes and returns the DOMAIN-NAME row from the "domains" table. + + - Function: domain-checker DOMAIN + Returns a procedure to check an argument for conformance to domain + DOMAIN. + +Defining Tables +--------------- + + - Procedure: define-tables RDB SPEC-0 ... + Adds tables as specified in SPEC-0 ... to the open + relational-database RDB. Each SPEC has the form: + + (<name> <descriptor-name> <descriptor-name> <rows>) + or + (<name> <primary-key-fields> <other-fields> <rows>) + + where <name> is the table name, <descriptor-name> is the symbol + name of a descriptor table, <primary-key-fields> and + <other-fields> describe the primary keys and other fields + respectively, and <rows> is a list of data rows to be added to the + table. + + <primary-key-fields> and <other-fields> are lists of field + descriptors of the form: + + (<column-name> <domain>) + or + (<column-name> <domain> <column-integrity-rule>) + + where <column-name> is the column name, <domain> is the domain of + the column, and <column-integrity-rule> is an expression whose + value is a procedure of one argument (and returns non-`#f' to + signal an error). + + If <domain> is not a defined domain name and it matches the name of + this table or an already defined (in one of SPEC-0 ...) single key + field table, a foriegn-key domain will be created for it. + + - Procedure: create-report RDB DESTINATION REPORT-NAME TABLE + - Procedure: create-report RDB DESTINATION REPORT-NAME + The symbol REPORT-NAME must be primary key in the table named + `*reports*' in the relational database RDB. DESTINATION is a + port, string, or symbol. If DESTINATION is a: + + port + The table is created as ascii text and written to that port. + + string + The table is created as ascii text and written to the file + named by DESTINATION. + + symbol + DESTINATION is the primary key for a row in the table named + *printers*. + + Each row in the table *reports* has the fields: + + name + The report name. + + default-table + The table to report on if none is specified. + + header, footer + A `format' string. At the beginning and end of each page + respectively, `format' is called with this string and the + (list of) column-names of this table. + + reporter + A `format' string. For each row in the table, `format' is + called with this string and the row. + + minimum-break + The minimum number of lines into which the report lines for a + row can be broken. Use `0' if a row's lines should not be + broken over page boundaries. + + Each row in the table *printers* has the fields: + + name + The printer name. + + print-procedure + The procedure to call to actually print. + + The report is prepared as follows: + + `Format' (*note Format::.) is called with the `header' field + and the (list of) `column-names' of the table. + + `Format' is called with the `reporter' field and (on + successive calls) each record in the natural order for the + table. A count is kept of the number of newlines output by + format. When the number of newlines to be output exceeds the + number of lines per page, the set of lines will be broken if + there are more than `minimum-break' left on this page and the + number of lines for this row is larger or equal to twice + `minimum-break'. + + `Format' is called with the `footer' field and the (list of) + `column-names' of the table. The footer field should not + output a newline. + + A new page is output. + + This entire process repeats until all the rows are output. + +The following example shows a new database with the name of `foo.db' +being created with tables describing processor families and +processor/os/compiler combinations. + +The database command `define-tables' is defined to call `define-tables' +with its arguments. The database is also configured to print `Welcome' +when the database is opened. The database is then closed and reopened. + + (require 'database-utilities) + (define my-rdb (create-database "foo.db" 'alist-table)) + + (define-tables my-rdb + '(*commands* + ((name symbol)) + ((parameters parameter-list) + (procedure expression) + (documentation string)) + ((define-tables + no-parameters + no-parameter-names + (lambda (rdb) (lambda specs (apply define-tables rdb specs))) + "Create or Augment tables from list of specs") + (*initialize* + no-parameters + no-parameter-names + (lambda (rdb) (display "Welcome") (newline) rdb) + "Print Welcome")))) + + ((my-rdb 'define-tables) + '(processor-family + ((family atom)) + ((also-ran processor-family)) + ((m68000 #f) + (m68030 m68000) + (i386 8086) + (8086 #f) + (powerpc #f))) + + '(platform + ((name symbol)) + ((processor processor-family) + (os symbol) + (compiler symbol)) + ((aix powerpc aix -) + (amiga-dice-c m68000 amiga dice-c) + (amiga-aztec m68000 amiga aztec) + (amiga-sas/c-5.10 m68000 amiga sas/c) + (atari-st-gcc m68000 atari gcc) + (atari-st-turbo-c m68000 atari turbo-c) + (borland-c-3.1 8086 ms-dos borland-c) + (djgpp i386 ms-dos gcc) + (linux i386 linux gcc) + (microsoft-c 8086 ms-dos microsoft-c) + (os/2-emx i386 os/2 gcc) + (turbo-c-2 8086 ms-dos turbo-c) + (watcom-9.0 i386 ms-dos watcom)))) + + ((my-rdb 'close-database)) + + (set! my-rdb (open-database "foo.db" 'alist-table)) + -| + Welcome + |