Verdad - Perl module for access to Verdad
use lib "/usr/local/lib"; use Verdad;
$gDBH = sqlConnect(); $id = lookupItemId($gDBH, "item-name"); $item = fetchItem($gDBH, $id); $item = fetchCompleteItem($gDBH, $id);
This package is the lowest-level API to Verdad. It is the same library used by all the basic Verdad tools read and write the database. Because Verdad is a database with unique semantics implemented on top of a SQL database, it is critical that programmers interact with the database via the library, and not via raw SQL queries.
This package is procedural. No objects here. Some day, the design of Verdad might be stable enough, and there might be a programmer working on it that's good at making object-oriented interfaces. For now, however, it's purely procedural. To add insult to injury, Verdad.pm is also rude about which methods it exports into the caller's namespace. Sorry about that, but it seemed like a good idea at the time.
The basic operations that this library enables are searching for items, fetching items (in simple and complete form), and writing items. We'll cover them in that order.
There are lots of other support routines that are documented at the end.
Before you can use most any of the routines below, you need a valid database handle. This is a regular database handle fetched from DBI. You may use it to talk to the database, if you are certain you know what you are doing and why you are doing it. In general, get it once and pass it in to other routines; don't try to use it yourself.
Verdad parses a config file to get various parameters it
needs. The loadConfig
call is done for you automatically by
sqlConnect, if you have not already executed it. You can
call loadConfig
at any time. See the section named
CONFIGURATION for more information on the contents
of this file.
loadConfig()
This routine will load a config file from "/usr/local/etc/verdad.conf". Setting the environment vairable VERDAD_CONF will override this.
sqlConnect()
This routine initializes a connection to the database using
the configuration parameters from the config file. If loadConfig
has never been called before, it calls it first to load the config
file.
Always call this routine, even if you think you want to do something clever with caching connections under mod_perl. It's better to use the Apache::DBI package to do persistent connections to a database.
sqlDisconnect($gDBH)
Finishes a session with the underlying database. You should always call this routine before discarding the reference to $gDBH.
One more thing you need to know to make sense of the following descriptions. For the purposes of this document, a table is a reference to an array which contains references to rows of a table. For instance if you used findItemsTable, you'd get a table back:
$table = findItemsTable(...);
Then if you wanted to print out one line for each row with spaces between each of the bits of data in the rows, you'd do this:
foreach my $row (@$table) { print join(", ", @$row), "\n"; }
The columns from left to right in a table are determined by the routine that returns it; each one documents them seprately.
Verdad is able to cache data on the client side, so that fewer database lookups, and the network roundtrips associated with them, need to happen. There is NO cache expiration/update policy, so you should only use caching in a program that is doing a huge batch of lookups, then exiting. In this case, the database is likely to change little, if at all during the execution of the program, so letting the cache hold items, even if they have changed in the database will not be a problem.
If you want to turn on caching, do this at the top of your program:
useCache(1);
For long-lived processes, like the web user-interface scripts, turning on caching would be disasterous. So don't do that.
This routine searches for items in the Verdad database that match any of the search strings in the array @q. If $exact is true, then only exact matches get returned, otherwise, substring matches are supported too.
The return value is a table with the following columns:
item_id - an integer item_name - a string is_locked - y or n locked_by - a username locked_dt - a SQL date-time
As a (very) special case, if the first entry in the @q array is "-fields", then the second entry in the array is used as the list of fields to return. It should be in the form of a comma separated list, like this:
findItemsTable($dbh, $exact, "-fields", "item_id", @q);
This will return a table with only one column, which is a bit more efficient when fetching a lot of items.
This routine returns an array of item ids. The items are all ones which contain the given tag.
This routine returns a table with the following columns:
item_id
The items are all ones which are currently locked by the given user.
This routine returns a table with the following columns:
item_name
The table includes all items that inherit from the item represented by the given $itemId.
This routine returns a table with the following columns:
item_id
The table includes all items that inherit from the item represented by the given $itemId. Compare this to findNamesByIs, which returns the item names, instead of the item id's.
This routine returns a reference to an array of item names.
The array of item names includes all items for which every tag filter in %tag matches. %tag is simply a list of tag/value pairs. The tag is interpreted as a string. The value is interpreted as a regular expression. The special tag '' acts as a wildcard for all tags, so the following will return all items that have 192.168 anywhere in any tag:
$table = searchTags($gDBH, '', '192.168');
There are two forms in which you can fetch an item. A simple item is simply the tags and values that make up an item, without any inheritance. Using many simple items, and following the rules of inheritance, Verdad.pm can construct a complete item. There are two routines which result in the two different kinds of items.
Here's an example item in text form:
item example is = example-item animals += ( cat dog pig )
Here's the data structure returned by fetchItem:
$item = { '_item-name' => [ '=', 'example-item' ], '_item-id' => [ '=', 365 ], 'is' => [ '=', 'example-item' ], 'animals' => [ '+', 'cat', 'dog', 'pig' ] };
There are three key things to notice here. First, $item is a reference to a hash which holds the tags and values. Second, there are (at least) two tags which start with underscore. These tags are called meta-tags and hold info about the item itself. Finally, the values are held in lists, even for tags with only one value. The first item in a value list of the inheritance policy that applies to that tag.
Here's an example of a pair of items. The second inherits from the first by way of an "append" inheritance relationship.
item parent animals = giraffe
item kid is = parent animals += ( cat dog pig )
After calling fetchCompleteItem on the item named "kid" above, the resulting $item will be:
$item = { '_item-name' => [ [ 'kid' ], [ 'kid' ] ], '_item-id' => [ [ 365 ], [ 'kid' ] ], 'is' => [ [ 'parent' ], [ 'kid' ] ], 'animals' => [ [ 'giraffe', 'cat', 'dog', 'pig' ], [ 'parent', 'kid' ] ] };
The meta tags show up in a complete item just as they do in a simple item. The right hand side is the complete value, which reflects the results of inheritance. It is an array of two items, the final value array and an array of contributors to the final value. Notice that even the "is" tag, which had a single value in the text form is stored in list format. This consistency makes it easier to write code to process these data structures, at the cost of making them somewhat more complex than necessary for the single-value case.
buildCompleteItem takes an item (as fetchItem returns) and builds a complete item from it, just as fetchCompleteItem does. The difference between fetchCompleteItem and buildCompleteItem is that all data returned by fetchCompleteItem comes from the database, where as the base item passed into buildCompleteItem is used. This way you can compute a complete item (for "what if" issues) without actually having to write the item to the database.
An expanded item is a complete item which has had the schema-specific itemExpand method called on it. Typically, this function will add or modify tags to include computed information that you don't want people trying to maintain by hand in the database. With the default schema, which has an empty itemExpand function, fetchCompleteItem and fetchExpandedItem return the same thing.
To write an item, you start a transaction, submit simple items to the library's writeItem routine, then finish the transaction. The transaction takes care of locking and also bundles a related set of item changes together. Item changes from two concurrent transactions are guaranteed to be sequential.
Note: unless the underlying database system supports formal transactions, they are not used. These are atomic transactions, not fully ACIDic transactions. At the present time, Verdad does not use features available in MySQL to support ACIDic transactions.
This routine starts a transaction and gives you a transaction number to use in future writeItem calls. The $why parameter should be a small string explaining why this change is being made. You could compare it to a CVS log message, for instance.
If this is a new item that's being added to the database, pass in $itemID as undef. If you don't know the item-id, you can also pass in undef and writeItem will attempt to find it for you using the item-name stored in the $item.
$item must be a simple item, either an edited version of one returned by fetchItem, or one hand-crafted to be handed to writeItem.
$res is the result code. A true value means the item was written correctly. writeItem can throw these three exceptions:
Item is missing required _item-name tag. Could not create a new item. Permission denied.
It throws them by using die()
, so if you don't want
your program exiting as a result of the exception,
you'll need to catch them with eval:
eval { $res = writeItem(...); }; if ($@) { # error happened; handle it. };
Deletes an item.
Undeletes an item. The item springs into existence again, but it is empty. You need to use writeItem to fill it back up with useful data.
Use this item to set an advisory lock on the item. writeItem does not check locks; it expects the application to do so, if necessary. Thus for "checkout/checkin" applications like the command line interface to Verdad locking support is required. The locks are only "advisory", since there are known race conditions in the system. Verdad locks have proven to be good enough to keep humans from stomping on each other, but that's all.
If $gotLock is false, then $who and $since tell you who has the lock now. If it is true, $who and $since are undefined.
Unlocks an item.
If $who and $since are defined, then the lock is held by $who. If not, the item is available for locking.
The following support routines are used when interacting with Verdad.
Use this to turn an item name into an item id.
Use this to turn an item id into an item name.
This function returns true if the OS is Windows, which helps make certain operations portable.
isMetaTag($tag)
This function returns true if the tag is a "meta" tag, i.e. it starts with an underscore.
isIsTag($tag)
This function currently return true if the $tag is "is" or "include". This abstracts the name of the inheritance-triggering tag in case we want to change it later. Right.
When loadConfig
is called, Verdad loads a configuration
file. Usually, loadConfig
is called by sqlConnect
, but you can call it at any time.
loadConfig uses the environment variable VERDAD_CONF (which defaults to /usr/local/etc/verdad.conf, if it is not set) to find a config file.
If a config file is located, it will be parsed using the Verdad item parser. The following tags are then checked and used to control Verdad's behavior.
Set this to the name of the user which owns CGI processes. The default is "nobody". This is required so that Verdad will know when to substitute the webserver username with the username of the person who logged in to the web interface.
Sets the hostname of the server where the database is located. Defaults to the name of the machine.
Sets the name of the database that Verdad.pm will connect to. Defaults to "verdad".
Sets the password that will be used to connect to the database. Defaults to no password required.
Sets the username that will be used to connect to the database. Defaults to "verdad".
Set this to "None", "Verbose", or "Debug". Any other setting will act like "None". This setting is case-sensitive. The default is "None". If the environment variable "VERDAD_LOG_LEVEL" is set, it overrides the value set in the config file.
If this string is set to your name, it will appear at the bottom of all pages in the web-based UI, so that your users will know who to go to to get help.
Set this to the name of a .pm file in the lib directory which will implement schema checking and item expansion. Defaults to DefaultSchema.pm.
Jeff R. Allen <jra@nella.org>