Db::GTM - Perl extension to interface with GT.M global data
use Db::GTM; my($db) = new GTMDB("SPZ"); # Create connection to node ["SPZ"] # Basic database node storage/retrieval $db->set(1000); # sets node ["SPZ"] = 1000 $db->set("foo",50); # sets node ["SPZ","foo"] = 50 $db->set("foo",6,1012); # sets node ["SPZ","foo",6] = 1012 $db->set("foo",9,$$); # sets node ["SPZ","foo",9] = PID $db->set("foo",6,"bar",5); # sets node ["SPZ","foo",6,"bar"] = 5 $db->get("foo",6); # should be 1012 # Iterating through the database $db->next("foo",6); # should be scalar 9 $db->prev("foo",9); # should be scalar 6 $db->prev("foo",6); # should be undefined $db->children("foo"); # should be list: (6,9) $db->query("foo",6); # should be list: ("foo",6,"bar"); # Copying nodes $db->merge("foo",6,undef,"foo",12); # copies ["SPZ","foo",6] and subscripts into ["SPZ","foo",12] $db->clobber("foo",6,undef,"foo",12); # copies [foo,6] over to [foo,12] # makes ["SPZ","foo",12] a clone of ["SPZ","foo",6] # Deleting nodes $db->kill("foo",6); # Kills node ["SPZ","foo",6] # and ["SPZ","foo",6,"bar"] $db->kv("foo",12); # Kills node ["SPZ","foo",12] only $db->ks("foo",12); # Kills node ["SPZ","foo",12,"bar"] # and any other subscripts...
This module attempts to allow access to the GT.M globals database from PERL. GT.M is a fast and flexible hierarchical database system. This module documents Db::GTM module version $VERSION
This module provides access to a GT.M database by presenting it as a PERL module. Since GT.M is a hierarchical database (also known as an Object-Oriented database), each node of the database is treated as a list. Instead of having a simple key like 'JoeSmith' to reference your data, you can have a list; like ('Joe Smith','Address','Street Number'). Each connection to the database ties it to one primary node (A single GT.M global) and will invisibly attempt to limit subsequent stores/retrieves to this main node. You probably won't notice this limitation if you use the module normally. This allows you to treat a single GT.M datastore as if it were multiple databases.
Create a connection to GT.M with the new() function. Pass new() the name of the main node ("Global") you wish to link to. You may link to a subscript if you wish. Example: my $dblink1 = new GTMDB("Clients"); my $dblink2 = new GTMDB("Clients","auto dealerships"); In the first instance, all subsequent database access will be invisibly restricted to the GTM database node "Clients". In the second, it will be further restricted to the sub-node: "auto dealerships". The following two statements would return identical results: $addr = $dblink1->get("auto dealerships","ACME AUTO","Address"); $addr = $dblink2->get("ACME AUTO","Address"); WARNING: There are special restrictions for the first element of the main node. It MUST start with an upper or lowercase letter, the rest of the element MUST contain only alphanumeric characters, and the full name of the 1st element SHOULD be 1-8 characters long. On failure, new() will return 'undef' and print an error to STDERR
Once a connection is made, you can store data with the set() function and retrieve it with get(). $status = set(@nodename,$value); # Returns nonzero on failure $value = get(@nodename); Example: $dblink2->set("ACME AUTO","Address","123 Any Street"); my $addr = $dblink2->get("ACME AUTO","Address"); To find out what sibling nodes are adjacent to a given one: $scalar = next(@node); # next node element at same depth $scalar = prev(@node); # previous node element at same depth To get children (sub-nodes) of the current node: $scalar = first(@node); # first child node $scalar = last(@node); # last child node (@list) = children(@node); # all node elements below given one (@nodename) = query(@node); # next node element at any depth You do not have to specify valid data nodes for these operations. You can ask for the node that would have been next/prior to a nonexistant node. Example: $dblink2->set("ACME AUTO",103309); $dblink2->set("ACME AUTO","Address","123 Any Street"); $dblink2->set("SMITH AUTO",103306); $dblink2->set("SMITH AUTO","Address","456 Other Street"); $nxt = $dblink2->next("ACME AUTO"); # Should be "SMITH AUTO" $nxt = $dblink2->next("BUBBA GUMP AUTO CO."); # Should be "SMITH AUTO" $prv = $dblink2->prev("SMITH AUTO"); # Should be "ACME AUTO" @dlrs = $dblink2->children(); # Should be ("ACME AUTO","SMITH AUTO") @data = $dblink2->children("ACME AUTO"); # Should be ("Address") @nxt = $dblink2->query("ACME AUTO"); # Should be ("ACME AUTO","Address") COLLATING ORDER NOTE: Data nodes are not stored randomly, as in a hash. They are stored in the MUMPS standard collating order. All numeric nodes come first, sorted from lowest to highest. Then all string nodes are sorted in ASCII order. If the following set operations were performed: $dblink2->set(5.5, "foo"); $dblink2->set(-1000, "bar"); $dblink2->set(12.362,"baz"); $dblink2->set(1, "boo"); $dblink2->set(3, "Foo"); $dblink2->set("x", "Bar"); $dblink2->set("1A", "Baz"); $dblink2->set("SPZ", "Boo"); $dblink2->children() would return the list: (-1000,1,3,5.5,12.362,"1A","SPZ","x")
These two functions do bulk data transfers: $status = merge(@srcnode,undef,@dstnode); # Returns nonzero on failure $status = clobber(@srcnode,undef,@dstnode); # Returns nonzero on failure The first function merges the contents of the source node into the destination node (including all sub-nodes). Sub-nodes in the destination will be overwritten if they conflict with sub-nodes in the source. Nodes that are unique to the destination will be left alone. In clobber(), nodes that are unique to the destination will be destroyed. Since both node names are lists, put an 'undef' between them to separate them. Example: $dblink1->set("vendors","ACME Sprocket Co.","Address","999 Main Street"); $dblink1->merge("auto dealerships",undef,"vendors"); $dblink1->get("vendors","ACME AUTO"); # Should be 103309 If you wish to merge/clobber from one database link to another, use the functions $status = >M::merge($src,$dst); # Returns nonzero on failure $status = >M::clobber($src,$dst); # Returns nonzero on failure $src or $dst can be either a database object ($dblink1) or a MUMPS-Global name. The node() function returns the MUMPS-Global name of a child node of a database link. Example: # Copies everything in dblink1 into dblink2 >M::merge($dblink1,$dblink2); # Copies everything in dblink1's sub-node "vendors" into dblink2 >M::merge($dblink1->node("vendors"),$dblink2);
$status = kill(@node); # Destroys node and all sub-nodes $status = kv(@node); # Destroys specified node only $status = ks(@node); # Destroys sub-nodes only All return nonzero on failure. Example: $dblink2->set("BUBBA GUMP AUTO CO.",103009); $dblink2->set("BUBBA GUMP AUTO CO.","Address","789 Someother Ave."); $dblink2->set("CRAZY EDDIE USED CARS",103009); $dblink2->set("CRAZY EDDIE USED CARS","Address","999 Emporium Ave."); $dblink2->set("PAY-AND-SPRAY",103009); $dblink2->set("PAY-AND-SPRAY","Address","400 Sunset Bvd"); $dblink2->kill("BUBBA GUMP AUTO CO."); # destroys "Address" sub-node too $dblink2->ks("CRAZY EDDIE USED CARS"); # destroys ONLY the 'Address' record $dblink2->kv("PAY-AND-SPRAY"); # leaves 'Address' record intact $dblink2->kill(); # destroys the "auto dealerships" node and all subnodes
GT.M and Db::GTM support the concept of "transactions". All database changes made during a transaction are linked, meaning that either they are all processed successfully or none of them are. If there is a fatal error or system crash in the middle of the transaction set, none of the set/kill operations will take effect. $status = $dblink->txnstart(); # Begin a transaction $status = $dblink->txnabort(); # Abort a transaction, make no changes $status = $dblink->txncommit(); # Save all set/kills made during txn Example: $status = $dblink->txnstart(); $dblink->set($acctno,"CHECKING","BALANCE",($oldChkBal - $transferAmt)); $dblink->set($acctno,"SAVINGS","BALANCE", ($oldSavBal + $transferAmt)); $status = $dblink->txncommit(); From the time you initiate txnstart(), all sets/kills are queued until you do either txncommit() or txnabort(). Note that if you have multiple GTMDB objects and only do a txnstart() with one of them, then the others will behave normally (their writes will take effect immediately). Also note that until a txncommit() is performed, everyone who views the data that has been set since a txnstart() will see the OLD data, not the stuff that is in the process of being written.
$status = $dblink->lock(@name,$seconds); # Lock a database node $status = $dblink->unlock(@name); # Unlock something locked prior seconds: the last parameter to lock() is the number of seconds to wait to get a lock before giving up. This is important as GT.M reserves the use of signals for itself and using SIGALRM may cause problems. Specifying a seconds count of 0 will make the locking attempt fail immediately if another lock exists. Specifying a seconds count of -1 will make the locking attempt wait forever for a conflicting lock to be released. This can lead to deadlock, so use with caution. If you specify a global name, you MUST specify a seconds count. Bad things will happen to you if you don't. In order to work gracefully with other processes that are attempting to update data in the GTM datastore, you can request locks on database nodes. Locks are advisory (meaning that it's possible to write to a "locked" node if you don't bother to ask for your lock first). Locks are automatically released when your process exits. Lock on a higher-level resources conflict with lower-level ones. Examples: (Process 1) $db = new GTMDB("TOPNODE"); $db->lock("MYNODE","A",0); # Lock (TOPNODE.MYNODE.A) (Process 2) $db = new GTMDB("TOPNODE"); $db->lock("MYNODE",0); # Lock (TOPNODE.MYNODE) # Fails because process 1 has a conflicting lock Note that you can always get locks to resources that you have previously locked, or lock a lower level resource.
Conventions: When a function takes '@name' as a parameter, or returns it as output, @name is a list that makes up the name of a database node. Any node can store data as well as have child nodes. See the examples above for explicit usage. If unspecified, functions that operate on @name will operate on the main node linked to during new() When a function returns '$status', anything nonzero indicates failure $db_obj = new GTMDB(@name); # Returns a link to a database node $db_obj = >MDB::new(@name); # Same @name = $db_obj->getprefix(); # Returns the name of the main node $scalar = $db_obj->get(@name); # Returns data stored at a node $scalar = $db_obj->exists(@name); # True if node has data or sub-nodes $scalar = $db_obj->haschildren(@name); # True if node has sub-nodes $db_obj = $db_obj->sub(@name); # Returns a link to a sub-node $status = $db_obj->set(@name,$val); # Sets data at node to $value $scalar = $db_obj->next(@name); # Returns next node at same level $scalar = $db_obj->prev(@name); # Returns previous node at same level $scalar = $db_obj->first(@name); # Returns first child node $scalar = $db_obj->last(@name); # Returns last child node @name = $db_obj->query(@name); # Returns next data node at any depth @list = $db_obj->children(@name); # Returns all immediate child nodes $status = $db_obj->kill(@name); # Destroys node and all subnodes $status = $db_obj->ks(@name); # Destroys all sub-nodes only $status = $db_obj->kv(@name); # Destroys current node only $status = $dblink->txnstart(); # Begin a transaction $status = $dblink->txnabort(); # Abort a transaction, make no changes $status = $dblink->txncommit(); # Save all set/kills made during txn $status = $dblink->lock(@name,$seconds); # Lock a database node $status = $dblink->unlock(@name); # Unlock locked database node $status = $db_obj->merge(@srcname [ ,undef,@dstname ]); # Copies nodes in @srcname into @dstname, overwriting collisions # if unspecified, @dstname is assumed to be the main node $status = $db_obj->clobber(@srcname [ ,undef,@dstname ]); # Makes @dstname an exact clone of @srcname $db_obj->clobber() and $db_obj->merge() can take another $db_obj as a source. $gvn = $db_obj->list2gvn(@name); # Returns the MUMPS-Global name that is the combination of the main # node name + the specified one. @name = $db_obj->gvn2list($GVN); # Converts a MUMPS-Global name to list @name = >M::gvn2list($GVN); # Converts a MUMPS-Global name to list $gvn = >M::list2gvn(@name); # Converts a list into a MUMPS Global # The functions >M::merge / >M::clobber can be used to copy the # contents of one DB object into another. They take either database # objects or GVNs (such as those returned by list2gvn()) as arguments $status = >M::merge ([ $gvn OR $src_db_obj ],[ $GVN OR $dst_db_obj ]); $status = >M::clobber([ $gvn OR $src_db_obj ],[ $GVN OR $dst_db_obj ]);
Although PERL's native support of hierarchical databases is somewhat limited, you can use this module to tie a node of a GTM database to a hash or scalar value. # Ties the PERL hash variable %spzhash to the GTM node ["SPZ","foo"] my(%spzhash); tie %spzhash, 'GTMDB', "SPZ", "foo"; # Ties the PERL scalar variable $spzsclar to the node ["SPZ","foo","bar"] my($spzsclr); tie $spzsclr, 'GTMDB', "SPZ", "foo", "bar"; my($spzlink) = new GTMDB("SPZ","foo"); # Sets ["SPZ","foo","bar"] to 6 $spzhash{"bar"} = 6; # Equivalent to $spzlink->set("bar",6); $spzsclr = 6; # Also equivalent... exists $spzhash{"bar"}; # True if "bar" has data or child nodes keys %spzhash; # A list of all child-nodes of ["SPZ","foo"] %spzhash = (); # Equivalent to $spzlink->kill(); As a special note, the 'keys' and 'each' keywords will always return the keys presorted in the mumps collating order. If you want to use 'tie' to store a simple flat-file hash this works fine. Multidimensional support, or accessing child nodes is trickier: $spzhash{"bar","baz"} = 6; # Equivalent to $spzlink->set("bar","baz",6); keys $spzhash{"bar"}; # Syntax error @node = ("bar","baz"); # Danger Will Robinson... $spzhash{@node} = 6; # Does $spzlink->set(2,6); If you wanted to get child nodes of ["SPZ","foo","bar"] this way, do: my %spzbarhash; tie %spzbarhash, 'GTMDB', "SPZ", "foo", "bar"; keys $spzbarhash; untie %spzbarhash; To convert a list to a scalar for purposes of setting a hash variable: $spzhash{join($;,@node)}; # $; is a PERL special variable
None by default.
The FIRST name in a node should always start with a letter, can contain only letters and numbers, and should ideally be 1-8 characters long. Names longer than 8 characters are unsupported by the MUMPS ANSI standard, but should still work in GTM. Node elements cannot be empty. ["SPZ","","bar"] is an invalid node name. Using double-quotes in a nodename probably won't work right now. This should be fixed eventually. ["SPZ","\"","bar"] won't work. Using the NULL (0x00) or FS (0x1c) character in a nodename will probably cause problems or fail. GTM v4.4-003 adjusts the terminal settings, and traps SIGINT (generated by CTRL+C) during initialization. Since these would be unexpected, this package compensates by saving & restoring the signal handler, and restoring the terminal settings when it exits. If the program is killed by SIGKILL or SIGSTOP, it will be unable to do this. You can restore the terminal settings by using the UNIX command '$ stty sane'
GT.M Documentation: http://sourceforge.net/projects/sanchez-gtm/ MUMPS documentation: ANSI language standard: 'ISO/IEC 11756:1999' Book: 'M[UMPS] by Example', by Ed de Moel Website: http://freem.vmth.ucdavis.edu/ Sanchez tech bulletin TB5-027 ("GT.M Callins on UNIX: Invoking M from C")
/usr/local/gtm/xc/calltab.ci This is the default location for the file that tells GT.M how to handle call-ins. If it is located somewhere else, it is currently necessary to specify it's location with the env. variable 'GTMCI' extapi.m This is the MUMPS side of the interface between PERL and GT.M. This collection of M routines should be stored somewhere GT.M can find it when the program runs.
GTMCI = location of the 'calltab.ci' file gtmgbldir = location of the globals file gtmroutines = folder locations of the routines and routine object files gtm_dist = folder containing the GT.M distribution and executable
Steven Zeck, <spzeck@ucdavis.edu>
Copyright 2005 by Steven Zeck
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Db::GTM, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Db::GTM
CPAN shell
perl -MCPAN -e shell install Db::GTM
For more information on module installation, please visit the detailed CPAN module installation guide.