diff options
Diffstat (limited to 'Master/tlpkg/tlperl/lib/Win32/ODBC.pm')
-rw-r--r-- | Master/tlpkg/tlperl/lib/Win32/ODBC.pm | 1493 |
1 files changed, 1493 insertions, 0 deletions
diff --git a/Master/tlpkg/tlperl/lib/Win32/ODBC.pm b/Master/tlpkg/tlperl/lib/Win32/ODBC.pm new file mode 100644 index 00000000000..a51616388ea --- /dev/null +++ b/Master/tlpkg/tlperl/lib/Win32/ODBC.pm @@ -0,0 +1,1493 @@ +package Win32::ODBC; + +$VERSION = '0.032'; + +# Win32::ODBC.pm +# +==========================================================+ +# | | +# | ODBC.PM package | +# | --------------- | +# | | +# | Copyright (c) 1996, 1997 Dave Roth. All rights reserved. | +# | This program is free software; you can redistribute | +# | it and/or modify it under the same terms as Perl itself. | +# | | +# +==========================================================+ +# +# +# based on original code by Dan DeMaggio (dmag@umich.edu) +# +# Use under GNU General Public License or Larry Wall's "Artistic License" +# +# Check the README.TXT file that comes with this package for details about +# it's history. +# + +require Exporter; +require DynaLoader; + +$ODBCPackage = "Win32::ODBC"; +$ODBCPackage::Version = 970208; +$::ODBC = $ODBCPackage; +$CacheConnection = 0; + + # Reserve ODBC in the main namespace for US! +*ODBC::=\%Win32::ODBC::; + + +@ISA= qw( Exporter DynaLoader ); + # Items to export into callers namespace by default. Note: do not export + # names by default without a very good reason. Use EXPORT_OK instead. + # Do not simply export all your public functions/methods/constants. +@EXPORT = qw( + ODBC_ADD_DSN + ODBC_REMOVE_DSN + ODBC_CONFIG_DSN + ODBC_ADD_SYS_DSN + ODBC_REMOVE_SYS_DSN + ODBC_CONFIG_SYS_DSN + + SQL_DONT_CLOSE + SQL_DROP + SQL_CLOSE + SQL_UNBIND + SQL_RESET_PARAMS + + SQL_FETCH_NEXT + SQL_FETCH_FIRST + SQL_FETCH_LAST + SQL_FETCH_PRIOR + SQL_FETCH_ABSOLUTE + SQL_FETCH_RELATIVE + SQL_FETCH_BOOKMARK + + SQL_COLUMN_COUNT + SQL_COLUMN_NAME + SQL_COLUMN_TYPE + SQL_COLUMN_LENGTH + SQL_COLUMN_PRECISION + SQL_COLUMN_SCALE + SQL_COLUMN_DISPLAY_SIZE + SQL_COLUMN_NULLABLE + SQL_COLUMN_UNSIGNED + SQL_COLUMN_MONEY + SQL_COLUMN_UPDATABLE + SQL_COLUMN_AUTO_INCREMENT + SQL_COLUMN_CASE_SENSITIVE + SQL_COLUMN_SEARCHABLE + SQL_COLUMN_TYPE_NAME + SQL_COLUMN_TABLE_NAME + SQL_COLUMN_OWNER_NAME + SQL_COLUMN_QUALIFIER_NAME + SQL_COLUMN_LABEL + SQL_COLATT_OPT_MAX + SQL_COLUMN_DRIVER_START + SQL_COLATT_OPT_MIN + SQL_ATTR_READONLY + SQL_ATTR_WRITE + SQL_ATTR_READWRITE_UNKNOWN + SQL_UNSEARCHABLE + SQL_LIKE_ONLY + SQL_ALL_EXCEPT_LIKE + SQL_SEARCHABLE + ); + #The above are included for backward compatibility + + +sub new +{ + my ($n, $self); + my ($type) = shift; + my ($DSN) = shift; + my (@Results) = @_; + + if (ref $DSN){ + @Results = ODBCClone($DSN->{'connection'}); + }else{ + @Results = ODBCConnect($DSN, @Results); + } + @Results = processError(-1, @Results); + if (! scalar(@Results)){ + return undef; + } + $self = bless {}; + $self->{'connection'} = $Results[0]; + $ErrConn = $Results[0]; + $ErrText = $Results[1]; + $ErrNum = 0; + $self->{'DSN'} = $DSN; + $self; +} + +#### +# Close this ODBC session (or all sessions) +#### +sub Close +{ + my ($self, $Result) = shift; + $Result = DESTROY($self); + $self->{'connection'} = -1; + return $Result; +} + +#### +# Auto-Kill an instance of this module +#### +sub DESTROY +{ + my ($self) = shift; + my (@Results) = (0); + if($self->{'connection'} > -1){ + @Results = ODBCDisconnect($self->{'connection'}); + @Results = processError($self, @Results); + if ($Results[0]){ + undef $self->{'DSN'}; + undef @{$self->{'fnames'}}; + undef %{$self->{'field'}}; + undef %{$self->{'connection'}}; + } + } + return $Results[0]; +} + + +sub sql{ + return (Sql(@_)); +} + +#### +# Submit an SQL Execute statement for processing +#### +sub Sql{ + my ($self, $Sql, @Results) = @_; + @Results = ODBCExecute($self->{'connection'}, $Sql); + return updateResults($self, @Results); +} + +#### +# Retrieve data from a particular field +#### +sub Data{ + + # Change by JOC 06-APR-96 + # Altered by Dave Roth <dave@roth.net> 96.05.07 + my($self) = shift; + my(@Fields) = @_; + my(@Results, $Results, $Field); + + if ($self->{'Dirty'}){ + GetData($self); + $self->{'Dirty'} = 0; + } + @Fields = @{$self->{'fnames'}} if (! scalar(@Fields)); + foreach $Field (@Fields) { + if (wantarray) { + push(@Results, data($self, $Field)); + } else { + $Results .= data($self, $Field); + } + } + return wantarray ? @Results : $Results; +} + +sub DataHash{ + my($self, @Results) = @_; + my(%Results, $Element); + + if ($self->{'Dirty'}){ + GetData($self); + $self->{'Dirty'} = 0; + } + @Results = @{$self->{'fnames'}} if (! scalar(@Results)); + foreach $Element (@Results) { + $Results{$Element} = data($self, $Element); + } + + return %Results; +} + +#### +# Retrieve data from the data buffer +#### +sub data +{ $_[0]->{'data'}->{$_[1]}; } + + +sub fetchrow{ + return (FetchRow(@_)); +} +#### +# Put a row from an ODBC data set into data buffer +#### +sub FetchRow{ + my ($self, @Results) = @_; + my ($item, $num, $sqlcode); + # Added by JOC 06-APR-96 + # $num = 0; + $num = 0; + undef $self->{'data'}; + + + @Results = ODBCFetch($self->{'connection'}, @Results); + if (! (@Results = processError($self, @Results))){ + #### + # There should be an innocuous error "No records remain" + # This indicates no more records in the dataset + #### + return undef; + } + # Set the Dirty bit so we will go and extract data via the + # ODBCGetData function. Otherwise use the cache. + $self->{'Dirty'} = 1; + + # Return the array of field Results. + return @Results; +} + +sub GetData{ + my($self) = @_; + my @Results; + my $num = 0; + + @Results = ODBCGetData($self->{'connection'}); + if (!(@Results = processError($self, @Results))){ + return undef; + } + #### + # This is a special case. Do not call processResults + #### + ClearError(); + foreach (@Results){ + s/ +$// if defined $_; # HACK + $self->{'data'}->{ ${$self->{'fnames'}}[$num] } = $_; + $num++; + } + # return is a hack to interface with a assoc array. + return wantarray? (1, 1): 1; +} + +#### +# See if any more ODBC Results Sets +# Added by Brian Dunfordshore <Brian_Dunfordshore@bridge.com> +# 96.07.10 +#### +sub MoreResults{ + my ($self) = @_; + + my(@Results) = ODBCMoreResults($self->{'connection'}); + return (processError($self, @Results))[0]; +} + +#### +# Retrieve the catalog from the current DSN +# NOTE: All Field names are uppercase!!! +#### +sub Catalog{ + my ($self) = shift; + my ($Qualifier, $Owner, $Name, $Type) = @_; + my (@Results) = ODBCTableList($self->{'connection'}, $Qualifier, $Owner, $Name, $Type); + + # If there was an error return 0 else 1 + return (updateResults($self, @Results) != 1); +} + +#### +# Return an array of names from the catalog for the current DSN +# TableList($Qualifier, $Owner, $Name, $Type) +# Return: (array of names of tables) +# NOTE: All Field names are uppercase!!! +#### +sub TableList{ + my ($self) = shift; + my (@Results) = @_; + if (! scalar(@Results)){ + @Results = ("", "", "%", "TABLE"); + } + + if (! Catalog($self, @Results)){ + return undef; + } + undef @Results; + while (FetchRow($self)){ + push(@Results, Data($self, "TABLE_NAME")); + } + return sort(@Results); +} + + +sub fieldnames{ + return (FieldNames(@_)); +} +#### +# Return an array of fieldnames extracted from the current dataset +#### +sub FieldNames { $self = shift; return @{$self->{'fnames'}}; } + + +#### +# Closes this connection. This is used mostly for testing. You should +# probably use Close(). +#### +sub ShutDown{ + my($self) = @_; + print "\nClosing connection $self->{'connection'}..."; + $self->Close(); + print "\nDone\n"; +} + +#### +# Return this connection number +#### +sub Connection{ + my($self) = @_; + return $self->{'connection'}; +} + +#### +# Returns the current connections that are in use. +#### +sub GetConnections{ + return ODBCGetConnections(); +} + +#### +# Set the Max Buffer Size for this connection. This determines just how much +# ram can be allocated when a fetch() is performed that requires a HUGE amount +# of memory. The default max is 10k and the absolute max is 100k. +# This will probably never be used but I put it in because I noticed a fetch() +# of a MEMO field in an Access table was something like 4Gig. Maybe I did +# something wrong, but after checking several times I decided to impliment +# this limit thingie. +#### +sub SetMaxBufSize{ + my($self, $Size) = @_; + my(@Results) = ODBCSetMaxBufSize($self->{'connection'}, $Size); + return (processError($self, @Results))[0]; +} + +#### +# Returns the Max Buffer Size for this connection. See SetMaxBufSize(). +#### +sub GetMaxBufSize{ + my($self) = @_; + my(@Results) = ODBCGetMaxBufSize($self->{'connection'}); + return (processError($self, @Results))[0]; +} + + +#### +# Returns the DSN for this connection as an associative array. +#### +sub GetDSN{ + my($self, $DSN) = @_; + if(! ref($self)){ + $DSN = $self; + $self = 0; + } + if (! $DSN){ + $self = $self->{'connection'}; + } + my(@Results) = ODBCGetDSN($self, $DSN); + return (processError($self, @Results)); +} + +#### +# Returns an associative array of $XXX{'DSN'}=Description +#### +sub DataSources{ + my($self, $DSN) = @_; + if(! ref $self){ + $DSN = $self; + $self = 0; + } + my(@Results) = ODBCDataSources($DSN); + return (processError($self, @Results)); +} + +#### +# Returns an associative array of $XXX{'Driver Name'}=Driver Attributes +#### +sub Drivers{ + my($self) = @_; + if(! ref $self){ + $self = 0; + } + my(@Results) = ODBCDrivers(); + return (processError($self, @Results)); +} + +#### +# Returns the number of Rows that were affected by the previous SQL command. +#### +sub RowCount{ + my($self, $Connection) = @_; + if (! ref($self)){ + $Connection = $self; + $self = 0; + } + if (! $Connection){$Connection = $self->{'connection'};} + my(@Results) = ODBCRowCount($Connection); + return (processError($self, @Results))[0]; +} + +#### +# Returns the Statement Close Type -- how does ODBC Close a statment. +# Types: +# SQL_DROP +# SQL_CLOSE +# SQL_UNBIND +# SQL_RESET_PARAMS +#### +sub GetStmtCloseType{ + my($self, $Connection) = @_; + if (! ref($self)){ + $Connection = $self; + $self = 0; + } + if (! $Connection){$Connection = $self->{'connection'};} + my(@Results) = ODBCGetStmtCloseType($Connection); + return (processError($self, @Results)); +} + +#### +# Sets the Statement Close Type -- how does ODBC Close a statment. +# Types: +# SQL_DROP +# SQL_CLOSE +# SQL_UNBIND +# SQL_RESET_PARAMS +# Returns the newly set value. +#### +sub SetStmtCloseType{ + my($self, $Type, $Connection) = @_; + if (! ref($self)){ + $Connection = $Type; + $Type = $self; + $self = 0; + } + if (! $Connection){$Connection = $self->{'connection'};} + my(@Results) = ODBCSetStmtCloseType($Connection, $Type); + return (processError($self, @Results))[0]; +} + +sub ColAttributes{ + my($self, $Type, @Field) = @_; + my(%Results, @Results, $Results, $Attrib, $Connection, $Temp); + if (! ref($self)){ + $Type = $Field; + $Field = $self; + $self = 0; + } + $Connection = $self->{'connection'}; + if (! scalar(@Field)){ @Field = $self->fieldnames;} + foreach $Temp (@Field){ + @Results = ODBCColAttributes($Connection, $Temp, $Type); + ($Attrib) = processError($self, @Results); + if (wantarray){ + $Results{$Temp} = $Attrib; + }else{ + $Results .= "$Temp"; + } + } + return wantarray? %Results:$Results; +} + +sub GetInfo{ + my($self, $Type) = @_; + my($Connection, @Results); + if(! ref $self){ + $Type = $self; + $self = 0; + $Connection = 0; + }else{ + $Connection = $self->{'connection'}; + } + @Results = ODBCGetInfo($Connection, $Type); + return (processError($self, @Results))[0]; +} + +sub GetConnectOption{ + my($self, $Type) = @_; + my(@Results); + if(! ref $self){ + $Type = $self; + $self = 0; + } + @Results = ODBCGetConnectOption($self->{'connection'}, $Type); + return (processError($self, @Results))[0]; +} + +sub SetConnectOption{ + my($self, $Type, $Value) = @_; + if(! ref $self){ + $Value = $Type; + $Type = $self; + $self = 0; + } + my(@Results) = ODBCSetConnectOption($self->{'connection'}, $Type, $Value); + return (processError($self, @Results))[0]; +} + + +sub Transact{ + my($self, $Type) = @_; + my(@Results); + if(! ref $self){ + $Type = $self; + $self = 0; + } + @Results = ODBCTransact($self->{'connection'}, $Type); + return (processError($self, @Results))[0]; +} + + +sub SetPos{ + my($self, @Results) = @_; + @Results = ODBCSetPos($self->{'connection'}, @Results); + $self->{'Dirty'} = 1; + return (processError($self, @Results))[0]; +} + +sub ConfigDSN{ + my($self) = shift @_; + my($Type, $Connection); + if(! ref $self){ + $Type = $self; + $Connection = 0; + $self = 0; + }else{ + $Type = shift @_; + $Connection = $self->{'connection'}; + } + my($Driver, @Attributes) = @_; + @Results = ODBCConfigDSN($Connection, $Type, $Driver, @Attributes); + return (processError($self, @Results))[0]; +} + + +sub Version{ + my($self, @Packages) = @_; + my($Temp, @Results); + if (! ref($self)){ + push(@Packages, $self); + } + my($ExtName, $ExtVersion) = Info(); + if (! scalar(@Packages)){ + @Packages = ("ODBC.PM", "ODBC.PLL"); + } + foreach $Temp (@Packages){ + if ($Temp =~ /pll/i){ + push(@Results, "ODBC.PM:$Win32::ODBC::Version"); + }elsif ($Temp =~ /pm/i){ + push(@Results, "ODBC.PLL:$ExtVersion"); + } + } + return @Results; +} + + +sub SetStmtOption{ + my($self, $Option, $Value) = @_; + if(! ref $self){ + $Value = $Option; + $Option = $self; + $self = 0; + } + my(@Results) = ODBCSetStmtOption($self->{'connection'}, $Option, $Value); + return (processError($self, @Results))[0]; +} + +sub GetStmtOption{ + my($self, $Type) = @_; + if(! ref $self){ + $Type = $self; + $self = 0; + } + my(@Results) = ODBCGetStmtOption($self->{'connection'}, $Type); + return (processError($self, @Results))[0]; +} + +sub GetFunctions{ + my($self, @Results)=@_; + @Results = ODBCGetFunctions($self->{'connection'}, @Results); + return (processError($self, @Results)); +} + +sub DropCursor{ + my($self) = @_; + my(@Results) = ODBCDropCursor($self->{'connection'}); + return (processError($self, @Results))[0]; +} + +sub SetCursorName{ + my($self, $Name) = @_; + my(@Results) = ODBCSetCursorName($self->{'connection'}, $Name); + return (processError($self, @Results))[0]; +} + +sub GetCursorName{ + my($self) = @_; + my(@Results) = ODBCGetCursorName($self->{'connection'}); + return (processError($self, @Results))[0]; +} + +sub GetSQLState{ + my($self) = @_; + my(@Results) = ODBCGetSQLState($self->{'connection'}); + return (processError($self, @Results))[0]; +} + + +# ----------- R e s u l t P r o c e s s i n g F u n c t i o n s ---------- +#### +# Generic processing of data into associative arrays +#### +sub updateResults{ + my ($self, $Error, @Results) = @_; + + undef %{$self->{'field'}}; + + ClearError($self); + if ($Error){ + SetError($self, $Results[0], $Results[1]); + return ($Error); + } + + @{$self->{'fnames'}} = @Results; + + foreach (0..$#{$self->{'fnames'}}){ + s/ +$//; + $self->{'field'}->{${$self->{'fnames'}}[$_]} = $_; + } + return undef; +} + +# ---------------------------------------------------------------------------- +# ----------------- D e b u g g i n g F u n c t i o n s -------------------- + +sub Debug{ + my($self, $iDebug, $File) = @_; + my(@Results); + if (! ref($self)){ + if (defined $self){ + $File = $iDebug; + $iDebug = $self; + } + $Connection = 0; + $self = 0; + }else{ + $Connection = $self->{'connection'}; + } + push(@Results, ($Connection, $iDebug)); + push(@Results, $File) if ($File ne ""); + @Results = ODBCDebug(@Results); + return (processError($self, @Results))[0]; +} + +#### +# Prints out the current dataset (used mostly for testing) +#### +sub DumpData { + my($self) = @_; my($f, $goo); + + # Changed by JOC 06-Apr-96 + # print "\nDumping Data for connection: $conn->{'connection'}\n"; + print "\nDumping Data for connection: $self->{'connection'}\n"; + print "Error: \""; + print $self->Error(); + print "\"\n"; + if (! $self->Error()){ + foreach $f ($self->FieldNames){ + print $f . " "; + $goo .= "-" x length($f); + $goo .= " "; + } + print "\n$goo\n"; + while ($self->FetchRow()){ + foreach $f ($self->FieldNames){ + print $self->Data($f) . " "; + } + print "\n"; + } + } +} + +sub DumpError{ + my($self) = @_; + my($ErrNum, $ErrText, $ErrConn); + my($Temp); + + print "\n---------- Error Report: ----------\n"; + if (ref $self){ + ($ErrNum, $ErrText, $ErrConn) = $self->Error(); + ($Temp = $self->GetDSN()) =~ s/.*DSN=(.*?);.*/$1/i; + print "Errors for \"$Temp\" on connection " . $self->{'connection'} . ":\n"; + }else{ + ($ErrNum, $ErrText, $ErrConn) = Error(); + print "Errors for the package:\n"; + } + + print "Connection Number: $ErrConn\nError number: $ErrNum\nError message: \"$ErrText\"\n"; + print "-----------------------------------\n"; + +} + +#### +# Submit an SQL statement and print data about it (used mostly for testing) +#### +sub Run{ + my($self, $Sql) = @_; + + print "\nExcecuting connection $self->{'connection'}\nsql statement: \"$Sql\"\n"; + $self->Sql($Sql); + print "Error: \""; + print $self->error; + print "\"\n"; + print "--------------------\n\n"; +} + +# ---------------------------------------------------------------------------- +# ----------- E r r o r P r o c e s s i n g F u n c t i o n s ------------ + +#### +# Process Errors returned from a call to ODBCxxxx(). +# It is assumed that the Win32::ODBC function returned the following structure: +# ($ErrorNumber, $ResultsText, ...) +# $ErrorNumber....0 = No Error +# >0 = Error Number +# $ResultsText.....if no error then this is the first Results element. +# if error then this is the error text. +#### +sub processError{ + my($self, $Error, @Results) = @_; + if ($Error){ + SetError($self, $Results[0], $Results[1]); + undef @Results; + } + return @Results; +} + +#### +# Return the last recorded error message +#### +sub error{ + return (Error(@_)); +} + +sub Error{ + my($self) = @_; + if(ref($self)){ + if($self->{'ErrNum'}){ + my($State) = ODBCGetSQLState($self->{'connection'}); + return (wantarray)? ($self->{'ErrNum'}, $self->{'ErrText'}, $self->{'connection'}, $State) :"[$self->{'ErrNum'}] [$self->{'connection'}] [$State] \"$self->{'ErrText'}\""; + } + }elsif ($ErrNum){ + return (wantarray)? ($ErrNum, $ErrText, $ErrConn):"[$ErrNum] [$ErrConn] \"$ErrText\""; + } + return undef +} + +#### +# SetError: +# Assume that if $self is not a reference then it is just a placeholder +# and should be ignored. +#### +sub SetError{ + my($self, $Num, $Text, $Conn) = @_; + if (ref $self){ + $self->{'ErrNum'} = $Num; + $self->{'ErrText'} = $Text; + $Conn = $self->{'connection'} if ! $Conn; + } + $ErrNum = $Num; + $ErrText = $Text; + + #### + # Test Section Begin + #### +# $! = ($Num, $Text); + #### + # Test Section End + #### + + $ErrConn = $Conn; +} + +sub ClearError{ + my($self, $Num, $Text) = @_; + if (ref $self){ + undef $self->{'ErrNum'}; + undef $self->{'ErrText'}; + }else{ + undef $ErrConn; + undef $ErrNum; + undef $ErrText; + } + ODBCCleanError(); + return 1; +} + + +sub GetError{ + my($self, $Connection) = @_; + my(@Results); + if (! ref($self)){ + $Connection = $self; + $self = 0; + }else{ + if (! defined($Connection)){ + $Connection = $self->{'connection'}; + } + } + + @Results = ODBCGetError($Connection); + return @Results; +} + + + + +# ---------------------------------------------------------------------------- +# ------------------ A U T O L O A D F U N C T I O N ----------------------- + +sub AUTOLOAD { + # This AUTOLOAD is used to 'autoload' constants from the constant() + # XS function. If a constant is not found then control is passed + # to the AUTOLOAD in AutoLoader. + + my($constname); + ($constname = $AUTOLOAD) =~ s/.*:://; + #reset $! to zero to reset any current errors. + local $! = 0; + $val = constant($constname); + + if ($! != 0) { + if ($! =~ /Invalid/) { + $AutoLoader::AUTOLOAD = $AUTOLOAD; + goto &AutoLoader::AUTOLOAD; + } + else { + + # Added by JOC 06-APR-96 + # $pack = 0; + $pack = 0; + ($pack,$file,$line) = caller; + print "Your vendor has not defined Win32::ODBC macro $constname, used in $file at line $line."; + } + } + eval "sub $AUTOLOAD { $val }"; + goto &$AUTOLOAD; +} + + + # -------------------------------------------------------------- + # + # + # Make sure that we shutdown ODBC and free memory even if we are + # using perlis.dll on Win32 platform! +END{ +# ODBCShutDown() unless $CacheConnection; +} + + +bootstrap Win32::ODBC; + +# Preloaded methods go here. + +# Autoload methods go after __END__, and are processed by the autosplit program. + +1; +__END__ + +=head1 NAME + +Win32::ODBC - ODBC Extension for Win32 + +=head1 SYNOPSIS + +To use this module, include the following statement at the top of your +script: + + use Win32::ODBC; + +Next, create a data connection to your DSN: + + $Data = new Win32::ODBC("MyDSN"); + +B<NOTE>: I<MyDSN> can be either the I<DSN> as defined in the ODBC +Administrator, I<or> it can be an honest-to-God I<DSN Connect String>. + + Example: "DSN=My Database;UID=Brown Cow;PWD=Moo;" + +You should check to see if C<$Data> is indeed defined, otherwise there +has been an error. + +You can now send SQL queries and retrieve info to your heart's +content! See the description of the methods provided by this module +below and also the file F<TEST.PL> as referred to in L<INSTALLATION +NOTES> to see how it all works. + +Finally, B<MAKE SURE> that you close your connection when you are +finished: + + $Data->Close(); + +=head1 DESCRIPTION + +=head2 Background + +This is a hack of Dan DeMaggio's <dmag@umich.edu> F<NTXS.C> ODBC +implementation. I have recoded and restructured most of it including +most of the F<ODBC.PM> package, but its very core is still based on +Dan's code (thanks Dan!). + +The history of this extension is found in the file F<HISTORY.TXT> that +comes with the original archive (see L<INSTALLATION NOTES> below). + +=head2 Benefits + +And what are the benefits of this module? + +=over + +=item * + +The number of ODBC connections is limited by memory and ODBC itself +(have as many as you want!). + +=item * + +The working limit for the size of a field is 10,240 bytes, but you can +increase that limit (if needed) to a max of 2,147,483,647 bytes. (You +can always recompile to increase the max limit.) + +=item * + +You can open a connection by either specifing a DSN or a connection +string! + +=item * + +You can open and close the connections in any order! + +=item * + +Other things that I can not think of right now... :) + +=back + +=head1 CONSTANTS + +This package defines a number of constants. You may refer to each of +these constants using the notation C<ODBC::xxxxx>, where C<xxxxx> is +the constant. + +Example: + + print ODBC::SQL_SQL_COLUMN_NAME, "\n"; + +=head1 SPECIAL NOTATION + +For the method documentation that follows, an B<*> following the +method parameters indicates that that method is new or has been +modified for this version. + +=head1 CONSTRUCTOR + +=over + +=item new ( ODBC_OBJECT | DSN [, (OPTION1, VALUE1), (OPTION2, VALUE2) ...] ) +* + +Creates a new ODBC connection based on C<DSN>, or, if you specify an +already existing ODBC object, then a new ODBC object will be created +but using the ODBC Connection specified by C<ODBC_OBJECT>. (The new +object will be a new I<hstmt> using the I<hdbc> connection in +C<ODBC_OBJECT>.) + +C<DSN> is I<Data Source Name> or a proper C<ODBCDriverConnect> string. + +You can specify SQL Connect Options that are implemented before the +actual connection to the DSN takes place. These option/values are the +same as specified in C<GetConnectOption>/C<SetConnectOption> (see +below) and are defined in the ODBC API specs. + +Returns a handle to the database on success, or I<undef> on failure. + +=back + +=head1 METHODS + +=over + +=item Catalog ( QUALIFIER, OWNER, NAME, TYPE ) + +Tells ODBC to create a data set that contains table information about +the DSN. Use C<Fetch> and C<Data> or C<DataHash> to retrieve the data. +The returned format is: + + [Qualifier] [Owner] [Name] [Type] + +Returns I<true> on error. + +=item ColAttributes ( ATTRIBUTE [, FIELD_NAMES ] ) + +Returns the attribute C<ATTRIBUTE> on each of the fields in the list +C<FIELD_NAMES> in the current record set. If C<FIELD_NAMES> is empty, +then all fields are assumed. The attributes are returned as an +associative array. + +=item ConfigDSN ( OPTION, DRIVER, ATTRIBUTE1 [, ATTRIBUTE2, ATTRIBUTE3, ... +] ) + +Configures a DSN. C<OPTION> takes on one of the following values: + + ODBC_ADD_DSN.......Adds a new DSN. + ODBC_MODIFY_DSN....Modifies an existing DSN. + ODBC_REMOVE_DSN....Removes an existing DSN. + + ODBC_ADD_SYS_DSN.......Adds a new System DSN. + ODBC_MODIFY_SYS_DSN....Modifies an existing System DSN. + ODBC_REMOVE_SYS_DSN....Removes an existing System DSN. + +You must specify the driver C<DRIVER> (which can be retrieved by using +C<DataSources> or C<Drivers>). + +C<ATTRIBUTE1> B<should> be I<"DSN=xxx"> where I<xxx> is the name of +the DSN. Other attributes can be any DSN attribute such as: + + "UID=Cow" + "PWD=Moo" + "Description=My little bitty Data Source Name" + +Returns I<true> on success, I<false> on failure. + +B<NOTE 1>: If you use C<ODBC_ADD_DSN>, then you must include at least +I<"DSN=xxx"> and the location of the database. + +Example: For MS Access databases, you must specify the +I<DatabaseQualifier>: + + "DBQ=c:\\...\\MyDatabase.mdb" + +B<NOTE 2>: If you use C<ODBC_MODIFY_DSN>, then you need only specify +the I<"DNS=xxx"> attribute. Any other attribute you include will be +changed to what you specify. + +B<NOTE 3>: If you use C<ODBC_REMOVE_DSN>, then you need only specify +the I<"DSN=xxx"> attribute. + +=item Connection () + +Returns the connection number associated with the ODBC connection. + +=item Close () + +Closes the ODBC connection. No return value. + +=item Data ( [ FIELD_NAME ] ) + +Returns the contents of column name C<FIELD_NAME> or the current row +(if nothing is specified). + +=item DataHash ( [ FIELD1, FIELD2, ... ] ) + +Returns the contents for C<FIELD1, FIELD2, ...> or the entire row (if +nothing is specified) as an associative array consisting of: + + {Field Name} => Field Data + +=item DataSources () + +Returns an associative array of Data Sources and ODBC remarks about them. +They are returned in the form of: + + $ArrayName{'DSN'}=Driver + +where I<DSN> is the Data Source Name and ODBC Driver used. + +=item Debug ( [ 1 | 0 ] ) + +Sets the debug option to on or off. If nothing is specified, then +nothing is changed. + +Returns the debugging value (I<1> or I<0>). + +=item Drivers () + +Returns an associative array of ODBC Drivers and their attributes. +They are returned in the form of: + + $ArrayName{'DRIVER'}=Attrib1;Attrib2;Attrib3;... + +where I<DRIVER> is the ODBC Driver Name and I<AttribX> are the +driver-defined attributes. + +=item DropCursor ( [ CLOSE_TYPE ] ) + +Drops the cursor associated with the ODBC object. This forces the +cursor to be deallocated. This overrides C<SetStmtCloseType>, but the +ODBC object does not lose the C<StmtCloseType> setting. C<CLOSE_TYPE> +can be any valid C<SmtpCloseType> and will perform a close on the stmt +using the specified close type. + +Returns I<true> on success, I<false> on failure. + +=item DumpData () + +Dumps to the screen the fieldnames and all records of the current data +set. Used primarily for debugging. No return value. + +=item Error () + +Returns the last encountered error. The returned value is context +dependent: + +If called in a I<scalar> context, then a I<3-element array> is +returned: + + ( ERROR_NUMBER, ERROR_TEXT, CONNECTION_NUMBER ) + +If called in a I<string> context, then a I<string> is returned: + + "[ERROR_NUMBER] [CONNECTION_NUMBER] [ERROR_TEXT]" + +If debugging is on then two more variables are returned: + + ( ..., FUNCTION, LEVEL ) + +where C<FUNCTION> is the name of the function in which the error +occurred, and C<LEVEL> represents extra information about the error +(usually the location of the error). + +=item FetchRow ( [ ROW [, TYPE ] ] ) + +Retrieves the next record from the keyset. When C<ROW> and/or C<TYPE> +are specified, the call is made using C<SQLExtendedFetch> instead of +C<SQLFetch>. + +B<NOTE 1>: If you are unaware of C<SQLExtendedFetch> and its +implications, stay with just regular C<FetchRow> with no parameters. + +B<NOTE 2>: The ODBC API explicitly warns against mixing calls to +C<SQLFetch> and C<SQLExtendedFetch>; use one or the other but not +both. + +If I<ROW> is specified, it moves the keyset B<RELATIVE> C<ROW> number +of rows. + +If I<ROW> is specified and C<TYPE> is B<not>, then the type used is +B<RELATIVE>. + +Returns I<true> when another record is available to read, and I<false> +when there are no more records. + +=item FieldNames () + +Returns an array of fieldnames found in the current data set. There is +no guarantee on order. + +=item GetConnections () + +Returns an array of connection numbers showing what connections are +currently open. + +=item GetConnectOption ( OPTION ) + +Returns the value of the specified connect option C<OPTION>. Refer to +ODBC documentation for more information on the options and values. + +Returns a string or scalar depending upon the option specified. + +=item GetCursorName () + +Returns the name of the current cursor as a string or I<undef>. + +=item GetData () + +Retrieves the current row from the dataset. This is not generally +used by users; it is used internally. + +Returns an array of field data where the first element is either +I<false> (if successful) and I<true> (if B<not> successful). + +=item getDSN ( [ DSN ] ) + +Returns an associative array indicating the configuration for the +specified DSN. + +If no DSN is specified then the current connection is used. + +The returned associative array consists of: + + keys=DSN keyword; values=Keyword value. $Data{$Keyword}=Value + +=item GetFunctions ( [ FUNCTION1, FUNCTION2, ... ] ) + +Returns an associative array indicating the ability of the ODBC Driver +to support the specified functions. If no functions are specified, +then a 100 element associative array is returned containing all +possible functions and their values. + +C<FUNCTION> must be in the form of an ODBC API constant like +C<SQL_API_SQLTRANSACT>. + +The returned array will contain the results like: + + $Results{SQL_API_SQLTRANSACT}=Value + +Example: + + $Results = $O->GetFunctions( + $O->SQL_API_SQLTRANSACT, + SQL_API_SQLSETCONNECTOPTION + ); + $ConnectOption = $Results{SQL_API_SQLSETCONNECTOPTION}; + $Transact = $Results{SQL_API_SQLTRANSACT}; + +=item GetInfo ( OPTION ) + +Returns a string indicating the value of the particular +option specified. + +=item GetMaxBufSize () + +Returns the current allocated limit for I<MaxBufSize>. For more info, +see C<SetMaxBufSize>. + +=item GetSQLState () * + +Returns a string indicating the SQL state as reported by ODBC. The SQL +state is a code that the ODBC Manager or ODBC Driver returns after the +execution of a SQL function. This is helpful for debugging purposes. + +=item GetStmtCloseType ( [ CONNECTION ] ) + +Returns a string indicating the type of closure that will be used +everytime the I<hstmt> is freed. See C<SetStmtCloseType> for details. + +By default, the connection of the current object will be used. If +C<CONNECTION> is a valid connection number, then it will be used. + +=item GetStmtOption ( OPTION ) + +Returns the value of the specified statement option C<OPTION>. Refer +to ODBC documentation for more information on the options and values. + +Returns a string or scalar depending upon the option specified. + +=item MoreResults () + +This will report whether there is data yet to be retrieved from the +query. This can happen if the query was a multiple select. + +Example: + + "SELECT * FROM [foo] SELECT * FROM [bar]" + +B<NOTE>: Not all drivers support this. + +Returns I<1> if there is more data, I<undef> otherwise. + +=item RowCount ( CONNECTION ) + +For I<UPDATE>, I<INSERT> and I<DELETE> statements, the returned value +is the number of rows affected by the request or I<-1> if the number +of affected rows is not available. + +B<NOTE 1>: This function is not supported by all ODBC drivers! Some +drivers do support this but not for all statements (e.g., it is +supported for I<UPDATE>, I<INSERT> and I<DELETE> commands but not for +the I<SELECT> command). + +B<NOTE 2>: Many data sources cannot return the number of rows in a +result set before fetching them; for maximum interoperability, +applications should not rely on this behavior. + +Returns the number of affected rows, or I<-1> if not supported by the +driver in the current context. + +=item Run ( SQL ) + +Executes the SQL command B<SQL> and dumps to the screen info about +it. Used primarily for debugging. + +No return value. + +=item SetConnectOption ( OPTION ) * + +Sets the value of the specified connect option B<OPTION>. Refer to +ODBC documentation for more information on the options and values. + +Returns I<true> on success, I<false> otherwise. + +=item SetCursorName ( NAME ) * + +Sets the name of the current cursor. + +Returns I<true> on success, I<false> otherwise. + +=item SetPos ( ROW [, OPTION, LOCK ] ) * + +Moves the cursor to the row C<ROW> within the current keyset (B<not> +the current data/result set). + +Returns I<true> on success, I<false> otherwise. + +=item SetMaxBufSize ( SIZE ) + +This sets the I<MaxBufSize> for a particular connection. This will +most likely never be needed but... + +The amount of memory that is allocated to retrieve the field data of a +record is dynamic and changes when it need to be larger. I found that +a memo field in an MS Access database ended up requesting 4 Gig of +space. This was a bit much so there is an imposed limit (2,147,483,647 +bytes) that can be allocated for data retrieval. + +Since it is possible that someone has a database with field data +greater than 10,240, you can use this function to increase the limit +up to a ceiling of 2,147,483,647 (recompile if you need more). + +Returns the max number of bytes. + +=item SetStmtCloseType ( TYPE [, CONNECTION ] ) + +Sets a particular I<hstmt> close type for the connection. This is the +same as C<ODBCFreeStmt(hstmt, TYPE)>. By default, the connection of +the current object will be used. If C<CONNECTION> is a valid +connection number, then it will be used. + +C<TYPE> may be one of: + + SQL_CLOSE + SQL_DROP + SQL_UNBIND + SQL_RESET_PARAMS + +Returns a string indicating the newly set type. + +=item SetStmtOption ( OPTION ) * + +Sets the value of the specified statement option C<OPTION>. Refer to +ODBC documentation for more information on the options and values. + +Returns I<true> on success, I<false> otherwise. + +=item ShutDown () + +Closes the ODBC connection and dumps to the screen info about +it. Used primarily for debugging. + +No return value. + +=item Sql ( SQL_STRING ) + +Executes the SQL command C<SQL_STRING> on the current connection. + +Returns I<?> on success, or an error number on failure. + +=item TableList ( QUALIFIER, OWNER, NAME, TYPE ) + +Returns the catalog of tables that are available in the DSN. For an +unknown parameter, just specify the empty string I<"">. + +Returns an array of table names. + +=item Transact ( TYPE ) * + +Forces the ODBC connection to perform a I<rollback> or I<commit> +transaction. + +C<TYPE> may be one of: + + SQL_COMMIT + SQL_ROLLBACK + +B<NOTE>: This only works with ODBC drivers that support transactions. +Your driver supports it if I<true> is returned from: + + $O->GetFunctions($O->SQL_API_SQLTRANSACT)[1] + +(See C<GetFunctions> for more details.) + +Returns I<true> on success, I<false> otherwise. + +=item Version ( PACKAGES ) + +Returns an array of version numbers for the requested packages +(F<ODBC.pm> or F<ODBC.PLL>). If the list C<PACKAGES> is empty, then +all version numbers are returned. + +=back + +=head1 LIMITATIONS + +What known problems does this thing have? + +=over + +=item * + +If the account under which the process runs does not have write +permission on the default directory (for the process, not the ODBC +DSN), you will probably get a runtime error during a +C<SQLConnection>. I don't think that this is a problem with the code, +but more like a problem with ODBC. This happens because some ODBC +drivers need to write a temporary file. I noticed this using the MS +Jet Engine (Access Driver). + +=item * + +This module has been neither optimized for speed nor optimized for +memory consumption. + +=back + +=head1 INSTALLATION NOTES + +If you wish to use this module with a build of Perl other than +ActivePerl, you may wish to fetch the original source distribution for +this module at: + + ftp://ftp.roth.net:/pub/ntperl/ODBC/970208/Bin/Win32_ODBC_Build_CORE.zip + +or one of the other archives at that same location. See the included +README for hints on installing this module manually, what to do if you +get a I<parse exception>, and a pointer to a test script for this +module. + +=head1 OTHER DOCUMENTATION + +Find a FAQ for Win32::ODBC at: + + http://www.roth.net/odbc/odbcfaq.htm + +=head1 AUTHOR + +Dave Roth <rothd@roth.net> + +=head1 CREDITS + +Based on original code by Dan DeMaggio <dmag@umich.edu> + +=head1 DISCLAIMER + +I do not guarantee B<ANYTHING> with this package. If you use it you +are doing so B<AT YOUR OWN RISK>! I may or may not support this +depending on my time schedule. + +=head1 HISTORY + +Last Modified 1999.09.25. + +=head1 COPYRIGHT + +Copyright (c) 1996-1998 Dave Roth. All rights reserved. + +Courtesy of Roth Consulting: http://www.roth.net/consult/ + +Use under GNU General Public License. Details can be found at: +http://www.gnu.org/copyleft/gpl.html + +=cut |