next up previous contents
Next: 7.3 Searching Up: 7 Writing Qddb Applications Previous: 7.1 Data abstractions

7.2 QTcl Commands

The data abstractions we have just introduced are manipulated by commands that extend the Tcl language. We will call the extended language QTcl. There are only eight new commands: qddb_schema, qddb_search, qddb_keylist, qddb_tuple, qddb_rows, qddb_view, qddb_instance, and qddb_util. Each of these commands has variants that are distinguished by their first parameter. We will deal with each command in order. We don't show full details of the syntax here; you can refer to the man pages for complete information.

7.2.1 Schemas: qddb_schema

The qddb_schema command is used to open, close, and get information from a schema. There are several forms of the qddb_schema command:
tabular801

The qddb_schema open command opens a relation and returns a schema descriptor (an opaque identifier) that may be used in subsequent commands. A typical usage of this command is: set s [qddb_schema open MyRelation] The Tcl variable s will be set to a value such as qddb_schema32. This value is only usable as a parameter to other Qtcl commands. qddb_schema close closes an open schema and discards any internal storage devoted to it: qddb_schema close $s

The qddb_schema leaves command returns a Tcl list containing all leaves under a given attribute in the schema tree. For example, if we have a schema A B ( C D ), then the command qddb_schema leaves $s B returns a Tcl list containing B.C B.D. If no attribute is specified, the root is assumed. In our example, the value returned would be A B.C B.D.

The qddb_schema option command returns the value of a given option for a given attribute in the schema. The options you can specify are: type, verbosename, alias, isexpandable, exclude, format, and separators. The isexpandable option returns yes if the given attribute is expandable and no otherwise.

The qddb_schema path command returns the full pathname of the relation associated with the given schema descriptor. This command is often used by generic applications to find global configuration files and other information stored in the relation directory.

The qddb_schema print command returns a Tcl list that describes all the attributes. This ability is used heavily by generic applications. Each element of the list is a single attribute, represented by the sublist {Attribute Options Children}. The Attribute element is not the dot-separated path name, but just the local attribute name. The Options element is a Tcl list containing both the verbosename option and the isexpandable option. The Children element recursively contains lists of the attribute's children in the same form.

Example:

Suppose we have the following schema in a relation TmpDB:

    A ( B C D )* E (F* G verbosename "Junk")

The following figure shows the output of various qddb_schema commands typed directly into qtclsh. The output of the qddb_schema print command is formatted for clarity.

    % set s [qddb_schema open TmpDB]
    qddb_schema0
    % qddb_schema leaves $s
    A.B A.C A.D E.F E.G 
    % qddb_schema option isexpandable $s A.B 
    no
    % qddb_schema path $s
    /tmp/TmpDB
    % set p [qddb_schema print $s]
    {
        A {{} yes} {
            {B {{} no} {}}
            {C {{} no} {}}
            {D {{} no} {}}
        }
    }
    {
        E {{} no} {
            {F {{} yes} {}}
            {G {Junk no} {}}
        }
    }
    % qddb_schema close $s

7.2.2 Searching and keylists: qddb_search and qddb_keylist

Qtcl provides two commands for searching and then combining search results: qddb_search, which searches the relation for tuples, and qddb_keylist, which performs set operations on the resulting keylists.

The qddb_search command takes the form:

    qddb_search $s ?-prunebyattr Attribute? Type KeyOrRange

By convention, we will always use s to refer to an open schema descriptor. Type is one of word, word_range, number, numeric_range, date_range, and regexp. Each of these specifies a different sort of search. For those searches that need a single key, KeyOrRange is a single word or number. For range searches, KeyOrRange is of the form

    ?lower-bound key? - ?upper-bound key?
If you specify the optional -prunebyattr Attribute, the search only applies to the given leaf attribute, not to entire tuples. The return value of qddb_search is a keylist descriptor, which is an opaque identifier used as input to other commands.

The qddb_keylist command performs unary and binary operations on keylists. You may either choose to retain the original keylists or to free the resources they are occupying. You can also choose to prune the resulting keylist by removing redundant nodes, such as nodes that refer to the same tuple or those that refer to the same tuple and attribute. These choices are controlled by the following options:


tabular828

The two most useful qddb_keylist subcommands are operation and process.

The qddb_keylist operation command performs binary operations. It takes as a parameter one of intersection, union, or exclusion followed by two keylist descriptors. Its options are:


tabular835

The qddb_keylist process command performs unary operations. Its options are:


tabular841

The qddb_keylist process prune command requires either -prunebyattr or -prunebyrow. The former requires an attribute parameter and will prune the keylist of all nodes that do not refer to the given attribute. The latter requires a list of attributes as a parameter and will prune the keylist of all nodes that are not part of a row matching each listed attribute. Typically, the list of attributes contains each attribute that was searched on to obtain the keylist.

Keylists are hidden, in the sense that you must deal with keylist descriptors. Before you can read a tuple based on a keylist, however, you must convert the keylist into a Tcl list that you can scan through. This list is called a read list, and its elements are read nodes. The command

    qddb_keylist get KeyList

produces a read list from a keylist.

Example:

The following program fragment finds all tuples in the relation MyRelation and prints them out (using commands soon to be introduced) in readable form. Each tuple is printed exactly once.

    set s [qddb_schema open MyRelation]
    set k [qddb_search $s regexp .*] ;# get all tuples
    set k [qddb_keylist process nullop -deldup_sameentry on $k]
    foreach i [qddb_keylist get $k] { ;# one iteration per keylist node
        set t [qddb_tuple read $s $i]
        if {[string length $t] == 0} {continue} ;# deleted or modified
        puts [qddb_tuple get readable $t] ;# output readable qddb_tuple
        qddb_tuple delete $t ;# free storage
    }
    qddb_keylist delete $k ;# free storage
    qddb_schema close $s

7.2.3 Tuples: qddb_tuple

The qddb_tuple command manipulates an individual tuple. This command has many subcommands, distinguished by its first parameter:


tabular854

The first step in manipulating a tuple is to read it from the relation based on a read node. Reading is accomplished by

    qddb_tuple read ReadNode
which returns a tuple descriptor (another opaque identifier). If the tuple is invalid (in particular, if it has been deleted), the tuple descriptor is a null string.

The values of the tuple's attributes are accessed by converting the tuple into one of three formats by the qddb_tuple get command. These three varieties are provided because you may have various purposes in mind when you access the values of a tuple. The formats look like this:

You can modify the data in any of these formats, and then you can construct a new tuple descriptor for the modified data and write out the modified tuple:

    set t [qddb_tuple put Format $s TupleValue]
    qddb_tuple write t
Here, Format is, for example, tclexternal. The intermediate value stored in t is a tuple descriptor.

7.2.4 Rows: qddb_rows

  The qddb_rows command builds, sorts and formats rows. The various subcommands accept keylist descriptors, tuple descriptors, or row descriptors. For example, given a keylist returned by qddb_search, qddb_rows can return a list of the rows matching a query. Given a tuple descriptor returned by qddb_tuple, qddb_rows can return all rows associated with that tuple. There are several forms of the qddb_rows command:


tabular878

All row descriptors returned or manipulated by qddb_rows describe complete rows; that is, all leaf attributes in the schema tree are included. You may elide certain attributes for the purpose of deleting duplicates, but each resultant row will always be complete.

The two most useful forms of the qddb_rows command are qddb_rows all and qddb_rows select. The qddb_rows all command traverses the tuple tree, possibly beginning with a particular instance of an attribute. When complete, a call to qddb_rows all returns the requested rows associated with a particular tuple. If the -sortby and/or the -ascending options are specified, the resultant row descriptors are sorted by the specified attributes, either in ascending (use the -ascending option) or descending order (default). The -print option specifies the attributes you are interested in for printing purposes; -sortby is independent of these attributes. Thus, you can sort by attributes not specified by the -print option, and you can print attributes not specified by the -sortby option.

The qddb_rows select command returns a list of elements for each row describing the results of a search. Each element contains (1) a tuple descriptor, (2) a row descriptor, and (3) possibly a list of attribute values. You must provide: (1) a keylist describing the results of a search, (2) a list of the constraininggif attributes (if any), and (3) the attributes you are interested in seeing. Duplicate rows are removed if the -deldup_rows on option is given. The return value format of qddb_rows select -query off is:

    {qddb_tuple0 qddb_row0 {some values}}
    {qddb_tuple1 qddb_row1 {some other values}}
    {qddb_tuple2 qddb_row2 {still some other values}}

Example:

Suppose you want have a relation Debtors containing a set of people who owe you money. The Schema might look like this:

    Name (First Last)
    Address (City Street State Zip)*
    Phones (Desc Area Number)*
    SS verbosename "Social Security Number"
    Date verbosename "Date loaned" type date
    Amount type real format "%.2f"
    Rate verbosename "Interest Rate" type real format "%.2f"

Now suppose that you want to add interest to all records with a Date older than 30 days. The following segment of code accomplishes this:

    #!/usr/local/qddb/bin/qtclsh
    # Monthly update procedure for Debtors
    set s [qddb_schema open Debtors]
    set k [qddb_search $s -prunebyattr Date date_range - {today - 30 days}]
    set triples [qddb_rows select -attrs Date -suppress on \
        -print {Date Amount Rate} -deldup_rows on $k]
    qddb_keylist delete $k ;# finished with the keylist
    foreach i $triples {
        set t [lindex $i 0] ;# tuple descriptor
        while {[qddb_tuple lock $t] == 0} {
            puts "Waiting for tuple to be released"
            exec sleep 1
        }
        set v [qddb_view define $t {}]
        qddb_view set $v [lindex $i 1] ;# row descriptor
        set cur [qddb_instance getval $v Amount]
        set rate [qddb_instance getval $v Rate]
        qddb_instance setval $v Amount [expr $cur * $rate + $cur]
        qddb_tuple write $t
        qddb_tuple delete $t ;# deletes row, tuple, and view
    }
    qddb_schema close $s

First, we (1) open the schema, (2) search for a date range meaning ``older than 30 days ago,'' then (3) produce all unique rows. In this particular case, we know that no tuple will produce more than one row. Next, we iterate through the list returned by qddb_rows select, lock each record, then update, write and remove each record from memory. Finally, we close the schema, implicitly deleting all storage associated with it (tuples, rows, and views).

7.2.5 Views: qddb_view

 

The qddb_view command manipulates views within a Qddb tuple. You can define views and and set them to particular rows (described by a row descriptor returned by qddb_rows) in a tuple. You can also create new row descriptors describing the current row in a view. There are a few subcommands of the qddb_view command:


tabular892

Since the view always describes one complete row in a tuple, manipulating the view implicitly manipulates the row it describes. We can bind the individual columns of the row to Tcl variables when we define the view, or we can bind no columns and choose to manipulate the view only with the Qtcl command qddb_instance. When you provide qddb_view define an empty list of Tcl variable names, you bind no columns to Tcl variables. For example, qddb_view define $tuple {} will return an opaque identifier referring to the new view that is bound to no Tcl variables.

Example:

You have a relation ``Library'' (like the one presented in Chapter 2) and you want to write a Qtcl procedure to build a new complete view. By convention, we typically use a Tcl array with attribute names as indices. The code for such a routine might look like this:

    proc new_view {schema_desc tuple_desc array_name} {
        set l [qddb_schema leaves $schema_desc]
        foreach i $l {
            lappend attrs [list $i ${array_name}($i)]
        }
        return [qddb_view define $tuple_desc $attrs]
    }

We first get the list of all leaves in the schema with qddb_schema leaves. Next, we iterate through the list, appending an (attribute,variable) element at each step. Finally, we define the view with qddb_view define and return the result (an opaque identifier to the view.) This opaque identifier can then be used with qddb_view and qddb_instance Qtcl commands.

7.2.6 Instances: qddb_instance

The qddb_instance command manipulates defined rows. There are many subcommands of qddb_instance:


tabular902

Example:

Suppose you have a relation ``Family'' with the following schema:

    Name ( First Last )
    Address ( Street City State Zip )*

You want to write a two Qtcl procedures: (1) to add a new Address to a given tuple, and (2) to delete the current instance of Address. The procedures might look like these:

    proc add_address {view street city state zip} {
        qddb_instance switch $view Address [qddb_instance new $view Address]
        qddb_instance setval $view Address.Street $street
        qddb_instance setval $view Address.City $city
        qddb_instance setval $view Address.State $state
        qddb_instance setval $view Address.Zip $zip
    }
    proc del_address {view} {
        qddb_instance remove $view Address \
            [qddb_instance current $view Address]
    }

The add_address procedure accepts a view descriptor view and a set of values, one for each subattribute. The current instance of Address is switched to the new instance created by qddb_instance new. Next, the value of each attribute in the new instance is set to the corresponding parameter. The del_address procedure accepts a view descriptor and deletes the current instance of Address from the tuple.


next up previous contents
Next: 7.3 Searching Up: 7 Writing Qddb Applications Previous: 7.1 Data abstractions

Herrin Software Development, Inc.