GNU Info

Info Node: (slib.info)Database Utilities

(slib.info)Database Utilities


Next: Database Reports Prev: Unresolved Issues Up: Relational Database
Enter node , (file) or (file)node

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
         defaulter   expression
         expander    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 `defaulter' field is an expression whose value is either `#f' or
a procedure of one argument (the parameter-list) which returns a _list_
of the default value or values as appropriate.  Note that since the
`defaulter' 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.

    TYPES
          A list of the type name of each parameter.  Correspnds to the
          `type-id' field of the contents of the `domain' of the
          COMMAND's parameter-table.

    DEFAULTERS
          A list of the defaulters for each parameter.  Corresponds to
          the `defaulters' 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 'fluid-let)
     (require 'parameters)
     (require 'getopt)
     
     (define my-rdb (create-database #f 'alist-table))
     
     (define-tables my-rdb
       '(foo-params
         *parameter-columns*
         *parameter-columns*
         ((1 single-string single string
             (lambda (pl) '("str")) #f "single string")
          (2 nary-symbols nary symbol
             (lambda (pl) '()) #f "zero or more symbols")
          (3 nary1-symbols nary1 symbol
             (lambda (pl) '(symb)) #f "one or more symbols")
          (4 optional-number optional uint
             (lambda (pl) '()) #f "zero or one number")
          (5 flag boolean boolean
             (lambda (pl) '(#f)) #f "a boolean flag")))
       '(foo-pnames
         ((name string))
         ((parameter-index uint))
         (("s" 1)
          ("single-string" 1)
          ("n" 2)
          ("nary-symbols" 2)
          ("N" 3)
          ("nary1-symbols" 3)
          ("o" 4)
          ("optional-number" 4)
          ("f" 5)
          ("flag" 5)))
       '(my-commands
         ((name symbol))
         ((parameters parameter-list)
          (parameter-names parameter-name-translation)
          (procedure expression)
          (documentation string))
         ((foo
           foo-params
           foo-pnames
           (lambda (rdb) (lambda args (print args)))
           "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 defaulters dirs aliases)
          (apply comval (getopt->arglist
                         argc argv options positions
                         arities types defaulters dirs aliases)))))
     
     (define (cmd . opts)
       (fluid-let ((*optind* 1))
         (printf "%-34s => "
                 (call-with-output-string
                  (lambda (pt) (write (cons 'cmd opts) pt))))
         (set! opts (cons "cmd" opts))
         (force-output)
         (dbutil:serve-command-line
          my-rdb 'my-commands 'foo (length opts) opts)))
     
     (cmd)                              => ("str" () (symb) () #f)
     (cmd "-f")                         => ("str" () (symb) () #t)
     (cmd "--flag")                     => ("str" () (symb) () #t)
     (cmd "-o177")                      => ("str" () (symb) (177) #f)
     (cmd "-o" "177")                   => ("str" () (symb) (177) #f)
     (cmd "--optional" "621")           => ("str" () (symb) (621) #f)
     (cmd "--optional=621")             => ("str" () (symb) (621) #f)
     (cmd "-s" "speciality")            => ("speciality" () (symb) () #f)
     (cmd "-sspeciality")               => ("speciality" () (symb) () #f)
     (cmd "--single" "serendipity")     => ("serendipity" () (symb) () #f)
     (cmd "--single=serendipity")       => ("serendipity" () (symb) () #f)
     (cmd "-n" "gravity" "piety")       => ("str" () (piety gravity) () #f)
     (cmd "-ngravity" "piety")          => ("str" () (piety gravity) () #f)
     (cmd "--nary" "chastity")          => ("str" () (chastity) () #f)
     (cmd "--nary=chastity" "")         => ("str" () ( chastity) () #f)
     (cmd "-N" "calamity")              => ("str" () (calamity) () #f)
     (cmd "-Ncalamity")                 => ("str" () (calamity) () #f)
     (cmd "--nary1" "surety")           => ("str" () (surety) () #f)
     (cmd "--nary1=surety")             => ("str" () (surety) () #f)
     (cmd "-N" "levity" "fealty")       => ("str" () (fealty levity) () #f)
     (cmd "-Nlevity" "fealty")          => ("str" () (fealty levity) () #f)
     (cmd "--nary1" "surety" "brevity") => ("str" () (brevity surety) () #f)
     (cmd "--nary1=surety" "brevity")   => ("str" () (brevity surety) () #f)
     (cmd "-?")
     -|
     Usage: cmd [OPTION ARGUMENT ...] ...
     
       -f, --flag
       -o, --optional[=]<number>
       -n, --nary[=]<symbols> ...
       -N, --nary1[=]<symbols> ...
       -s, --single[=]<string>
     
     ERROR: getopt->parameter-list "unrecognized option" "-?"

  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.  Currently, these fields are
        * domain-name

        * foreign-table

        * domain-integrity-rule

        * type-id

        * type-param

     The following example adds 3 domains to the `build' database.
     `Optstring' is either a string or `#f'.  `filename' is a string
     and `build-whats' is a symbol.

          (for-each (build 'add-domain)
                    '((optstring #f
                                 (lambda (x) (or (not x) (string? x)))
                                 string
                                 #f)
                      (filename #f #f string #f)
                      (build-whats #f #f symbol #f)))

 - 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 (which returns `#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.

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

Listing Tables
..............

 - Procedure: list-table-definition rdb table-name
     If symbol TABLE-NAME exists in the open relational-database RDB,
     then returns a list of the table-name, its primary key names and
     domains, its other key names and domains, and the table's records
     (as lists).  Otherwise, returns #f.

     The list returned by `list-table-definition', when passed as an
     argument to `define-tables', will recreate the table.


automatically generated by info2www version 1.2.2.9