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.
The qddb_schema
command
is used to open, close, and get information from a schema.
There are several forms of the qddb_schema
command:
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.
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
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.
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:
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:
The qddb_keylist process
command performs unary operations.
Its options are:
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.
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
The qddb_tuple
command manipulates an individual tuple.
This command has many subcommands, distinguished by its first parameter:
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 ReadNodewhich 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:
{ {%1.1.1.1 data in the attribute} {%1.2.1.1 more data} }
{ $NUMBER$ = "319"; a ( b = "some data for attribute a.b" b = "some data for attribute a.b" c = "some data for attribute a.c" c = "some data for attribute a.c" ) a ( d = "some data for attribute a.d" ) }
{ a.b,1.1 "some data for attribute a.b" a.c,2.1 "some data for attribute a.c" }
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 tHere,
Format
is, for example, tclexternal
.
The intermediate value stored in t
is a tuple descriptor.
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:
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.
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.
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 constraining
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}}
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).
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:
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.
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.
The qddb_instance
command manipulates defined rows.
There are many subcommands of qddb_instance
:
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.