NAME
Db::GTM - Perl extension to interface with GT.M global data
SYNOPSIS
my ( $db ) = new GTMDB( "SPZ" );
$db ->set(1000);
$db ->set( "foo" ,50);
$db ->set( "foo" ,6,1012);
$db ->set( "foo" ,9,$$);
$db ->set( "foo" ,6, "bar" ,5);
$db ->get( "foo" ,6);
$db -> next ( "foo" ,6);
$db ->prev( "foo" ,9);
$db ->prev( "foo" ,6);
$db ->children( "foo" );
$db ->query( "foo" ,6);
$db ->merge( "foo" ,6, undef , "foo" ,12);
$db ->clobber( "foo" ,6, undef , "foo" ,12);
$db -> kill ( "foo" ,6);
$db ->kv( "foo" ,12);
$db ->ks( "foo" ,12);
|
ABSTRACT
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
|
DESCRIPTION
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.
|
SETTING UP A CONNECTION
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
|
STORING AND RETRIEVING DATA
Once a connection is made, you can store data with the set() function
and retrieve it with get().
$status = set( @nodename , $value );
$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 );
$scalar = prev( @node );
To get children ( sub -nodes) of the current node:
$scalar = first( @node );
$scalar = last ( @node );
( @list ) = children( @node );
( @nodename ) = query( @node );
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" );
$nxt = $dblink2 -> next ( "BUBBA GUMP AUTO CO." );
$prv = $dblink2 ->prev( "SMITH AUTO" );
@dlrs = $dblink2 ->children();
@data = $dblink2 ->children( "ACME AUTO" );
@nxt = $dblink2 ->query( "ACME AUTO" );
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" )
|
CLONING AND MERGING NODES
These two functions do bulk data transfers:
$status = merge( @srcnode , undef , @dstnode );
$status = clobber( @srcnode , undef , @dstnode );
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" );
If you wish to merge/clobber from one database link to another, use the functions
$status = >M::merge ( $src , $dst );
$status = >M::clobber ( $src , $dst );
$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:
>M::merge ( $dblink1 , $dblink2 );
>M::merge ( $dblink1 ->node( "vendors" ), $dblink2 );
|
DELETING NODES
$status = kill ( @node );
$status = kv( @node );
$status = ks( @node );
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." );
$dblink2 ->ks( "CRAZY EDDIE USED CARS" );
$dblink2 ->kv( "PAY-AND-SPRAY" );
$dblink2 -> kill ();
|
TRANSACTIONS
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();
$status = $dblink ->txnabort();
$status = $dblink ->txncommit();
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.
|
LOCKING
$status = $dblink -> lock ( @name , $seconds );
$status = $dblink ->unlock( @name );
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);
(Process 2) $db = new GTMDB( "TOPNODE" );
$db -> lock ( "MYNODE" ,0);
Note that you can always get locks to resources that you have previously
locked, or lock a lower level resource.
|
FUNCTION LIST
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 );
$db_obj = >MDB::new ( @name );
@name = $db_obj ->getprefix();
$scalar = $db_obj ->get( @name );
$scalar = $db_obj -> exists ( @name );
$scalar = $db_obj ->haschildren( @name );
$db_obj = $db_obj -> sub ( @name );
$status = $db_obj ->set( @name , $val );
$scalar = $db_obj -> next ( @name );
$scalar = $db_obj ->prev( @name );
$scalar = $db_obj ->first( @name );
$scalar = $db_obj -> last ( @name );
@name = $db_obj ->query( @name );
@list = $db_obj ->children( @name );
$status = $db_obj -> kill ( @name );
$status = $db_obj ->ks( @name );
$status = $db_obj ->kv( @name );
$status = $dblink ->txnstart();
$status = $dblink ->txnabort();
$status = $dblink ->txncommit();
$status = $dblink -> lock ( @name , $seconds );
$status = $dblink ->unlock( @name );
$status = $db_obj ->merge( @srcname [ , undef , @dstname ]);
$status = $db_obj ->clobber( @srcname [ , undef , @dstname ]);
$db_obj ->clobber() and $db_obj ->merge() can take another $db_obj as a source.
$gvn = $db_obj ->list2gvn( @name );
@name = $db_obj ->gvn2list( $GVN );
@name = >M::gvn2list ( $GVN );
$gvn = >M::list2gvn ( @name );
$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 ]);
|
USING GTM AS A TIED HASH OR SCALAR
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.
my ( %spzhash ); tie %spzhash , 'GTMDB' , "SPZ" , "foo" ;
my ( $spzsclr ); tie $spzsclr , 'GTMDB' , "SPZ" , "foo" , "bar" ;
my ( $spzlink ) = new GTMDB( "SPZ" , "foo" );
$spzhash { "bar" } = 6;
$spzsclr = 6;
exists $spzhash { "bar" };
keys %spzhash ;
%spzhash = ();
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;
keys $spzhash { "bar" };
@node = ( "bar" , "baz" );
$spzhash { @node } = 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 )};
|
EXPORT
None by default.
KNOWN BUGS AND WARNINGS
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'
|
SEE ALSO
GT.M Documentation:
MUMPS documentation:
ANSI language standard: 'ISO/IEC 11756:1999'
Book: 'M[UMPS] by Example' , by Ed de Moel
Sanchez tech bulletin TB5-027 ( "GT.M Callins on UNIX: Invoking M from C" )
|
FILES AND ENVIRONMENT VARIABLES
/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.
|
Environment Variables
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
|
AUTHOR
Steven Zeck, <spzeck@ucdavis.edu>
COPYRIGHT AND LICENSE
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.