Copyright (C) 2000-2012 |
GNU Info (slib.info)Database UtilitiesDatabase 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 |