home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / DBI.pm < prev    next >
Encoding:
Perl POD Document  |  2002-06-13  |  14.0 KB  |  381 lines

  1. package Apache::DBI;
  2.  
  3. use Apache ();
  4. use DBI ();
  5. use strict;
  6.  
  7. # $Id: DBI.pm,v 1.45 2001/01/12 18:59:00 mergl Exp $
  8.  
  9. require_version DBI 1.00;
  10.  
  11. $Apache::DBI::VERSION = '0.88';
  12.  
  13. # 1: report about new connect
  14. # 2: full debug output
  15. $Apache::DBI::DEBUG = 0;
  16. #DBI->trace(2);
  17.  
  18.  
  19. my %Connected;    # cache for database handles
  20. my @ChildConnect; # connections to be established when a new httpd child is created
  21. my %Rollback;     # keeps track of pushed PerlCleanupHandler which can do a rollback after the request has finished
  22. my %PingTimeOut;  # stores the timeout values per data_source, a negative value de-activates ping, default = 0
  23. my %LastPingTime; # keeps track of last ping per data_source
  24. my $Idx;          # key of %Connected and %Rollback.
  25.  
  26.  
  27. # supposed to be called in a startup script.
  28. # stores the data_source of all connections, which are supposed to be created upon
  29. # server startup, and creates a PerlChildInitHandler, which initiates the connections.
  30.  
  31. sub connect_on_init { 
  32.     # provide a handler which creates all connections during server startup
  33.     if(!@ChildConnect and Apache->can('push_handlers')) {
  34.         Apache->push_handlers(PerlChildInitHandler => \&childinit);
  35.     }
  36.     # store connections
  37.     push @ChildConnect, [@_];
  38. }
  39.  
  40.  
  41. # supposed to be called in a startup script.
  42. # stores the timeout per data_source for the ping function.
  43. # use a DSN without attribute settings specified within !
  44.  
  45. sub setPingTimeOut { 
  46.     my $class       = shift;
  47.     my $data_source = shift;
  48.     my $timeout     = shift;
  49.     # sanity check
  50.     if ($data_source =~ /dbi:\w+:.*/ and $timeout =~ /\-*\d+/) {
  51.         $PingTimeOut{$data_source} = $timeout;
  52.     }
  53. }
  54.  
  55.  
  56. # the connect method called from DBI::connect
  57.  
  58. sub connect {
  59.  
  60.     my $class = shift;
  61.     unshift @_, $class if ref $class;
  62.     my $drh    = shift;
  63.     my @args   = map { defined $_ ? $_ : "" } @_;
  64.     my $dsn    = "dbi:$drh->{Name}:$args[0]";
  65.     my $prefix = "$$ Apache::DBI            ";
  66.  
  67.     $Idx = join $;, $args[0], $args[1], $args[2];
  68.  
  69.     # the hash-reference differs between calls even in the same
  70.     # process, so de-reference the hash-reference 
  71.     if (3 == $#args and ref $args[3] eq "HASH") {
  72.        my ($key, $val);
  73.        while (($key,$val) = each %{$args[3]}) {
  74.            $Idx .= "$;$key=$val";
  75.        }
  76.     } elsif (3 == $#args) {
  77.         pop @args;
  78.     }
  79.  
  80.     # don't cache connections created during server initialization; they
  81.     # won't be useful after ChildInit, since multiple processes trying to
  82.     # work over the same database connection simultaneously will receive
  83.     # unpredictable query results.
  84.     if ($Apache::ServerStarting == 1) {
  85.         print STDERR "$prefix skipping connection during server startup, read the docu !!\n" if $Apache::DBI::DEBUG > 1;
  86.         return $drh->connect(@args);
  87.     }
  88.  
  89.     # this PerlCleanupHandler is supposed to initiate a rollback after the script has finished if AutoCommit is off.
  90.     my $needCleanup = ($Idx =~ /AutoCommit[^\d]+0/) ? 1 : 0;
  91.     if(!$Rollback{$Idx} and $needCleanup and Apache->can('push_handlers')) {
  92.         print STDERR "$prefix push PerlCleanupHandler \n" if $Apache::DBI::DEBUG > 1;
  93.         Apache->push_handlers("PerlCleanupHandler", \&cleanup);
  94.         # make sure, that the rollback is called only once for every 
  95.         # request, even if the script calls connect more than once
  96.         $Rollback{$Idx} = 1;
  97.     }
  98.  
  99.     # do we need to ping the database ?
  100.     $PingTimeOut{$dsn}  = 0 unless $PingTimeOut{$dsn};
  101.     $LastPingTime{$dsn} = 0 unless $LastPingTime{$dsn};
  102.     my $now = time;
  103.     my $needping = (($PingTimeOut{$dsn} == 0 or $PingTimeOut{$dsn} > 0) and $now - $LastPingTime{$dsn} > $PingTimeOut{$dsn}) ? 1 : 0;
  104.     print STDERR "$prefix need ping: ", $needping == 1 ? "yes" : "no", "\n" if $Apache::DBI::DEBUG > 1;
  105.     $LastPingTime{$dsn} = $now;
  106.  
  107.     # check first if there is already a database-handle cached
  108.     # if this is the case, possibly verify the database-handle 
  109.     # using the ping-method. Use eval for checking the connection 
  110.     # handle in order to avoid problems (dying inside ping) when 
  111.     # RaiseError being on and the handle is invalid.
  112.     if ($Connected{$Idx} and (!$needping or eval{$Connected{$Idx}->ping})) {
  113.         print STDERR "$prefix already connected to '$Idx'\n" if $Apache::DBI::DEBUG > 1;
  114.         return (bless $Connected{$Idx}, 'Apache::DBI::db');
  115.     }
  116.  
  117.     # either there is no database handle-cached or it is not valid,
  118.     # so get a new database-handle and store it in the cache
  119.     delete $Connected{$Idx};
  120.     $Connected{$Idx} = $drh->connect(@args);
  121.     return undef if !$Connected{$Idx};
  122.  
  123.     # return the new database handle
  124.     print STDERR "$prefix new connect to '$Idx'\n" if $Apache::DBI::DEBUG;
  125.     return (bless $Connected{$Idx}, 'Apache::DBI::db');
  126. }
  127.  
  128.  
  129. # The PerlChildInitHandler creates all connections during server startup.
  130. # Note: this handler runs in every child server, but not in the main server.
  131.  
  132. sub childinit {
  133.     my $prefix = "$$ Apache::DBI            ";
  134.     print STDERR "$prefix PerlChildInitHandler \n" if $Apache::DBI::DEBUG > 1;
  135.     if (@ChildConnect) {
  136.         for my $aref (@ChildConnect) {
  137.             shift @$aref;
  138.             DBI->connect(@$aref);
  139.             $LastPingTime{@$aref[0]} = time;
  140.         }
  141.     }
  142.     1;
  143. }
  144.  
  145.  
  146. # The PerlCleanupHandler is supposed to initiate a rollback after the script has finished if AutoCommit is off.
  147. # Note: the PerlCleanupHandler runs after the response has been sent to the client
  148.  
  149. sub cleanup {
  150.     my $prefix = "$$ Apache::DBI            ";
  151.     print STDERR "$prefix PerlCleanupHandler \n" if $Apache::DBI::DEBUG > 1;
  152.     my $dbh = $Connected{$Idx};
  153.     if ($Rollback{$Idx} and $dbh and $dbh->{Active} and !$dbh->{AutoCommit} and eval {$dbh->rollback}) {
  154.         print STDERR "$prefix PerlCleanupHandler rollback for $Idx \n" if $Apache::DBI::DEBUG > 1;
  155.     }
  156.     delete $Rollback{$Idx};
  157.     1;
  158. }
  159.  
  160.  
  161. # This function can be called from other handlers to perform tasks on all cached database handles.
  162.  
  163. sub all_handlers {
  164.   return \%Connected;
  165. }
  166.  
  167.  
  168. # patch from Tim Bunce: Apache::DBI will not return a DBD ref cursor
  169.  
  170. @Apache::DBI::st::ISA = ('DBI::st');
  171.  
  172.  
  173. # overload disconnect
  174.  
  175. { package Apache::DBI::db;
  176.   no strict;
  177.   @ISA=qw(DBI::db);
  178.   use strict;
  179.   sub disconnect {
  180.       my $prefix = "$$ Apache::DBI            ";
  181.       print STDERR "$prefix disconnect (overloaded) \n" if $Apache::DBI::DEBUG > 1;
  182.       1;
  183.   };
  184. }
  185.  
  186.  
  187. # prepare menu item for Apache::Status
  188.  
  189. Apache::Status->menu_item(
  190.  
  191.     'DBI' => 'DBI connections',
  192.     sub {
  193.         my($r, $q) = @_;
  194.         my(@s) = qw(<TABLE><TR><TD>Datasource</TD><TD>Username</TD></TR>);
  195.         for (keys %Connected) {
  196.             push @s, '<TR><TD>', join('</TD><TD>', (split($;, $_))[0,1]), "</TD></TR>\n";
  197.         }
  198.         push @s, '</TABLE>';
  199.         return \@s;
  200.    }
  201.  
  202. ) if ($INC{'Apache.pm'} and Apache->module('Apache::Status'));
  203.  
  204.  
  205. 1;
  206.  
  207. __END__
  208.  
  209.  
  210. =head1 NAME
  211.  
  212. Apache::DBI - Initiate a persistent database connection
  213.  
  214.  
  215. =head1 SYNOPSIS
  216.  
  217.  # Configuration in httpd.conf or startup.pl:
  218.  
  219.  PerlModule Apache::DBI  # this comes before all other modules using DBI
  220.  
  221. Do NOT change anything in your scripts. The usage of this module is
  222. absolutely transparent !
  223.  
  224.  
  225. =head1 DESCRIPTION
  226.  
  227. This module initiates a persistent database connection.
  228.  
  229. The database access uses Perl's DBI. For supported DBI drivers see:
  230.  
  231.  http://www.symbolstone.org/technology/perl/DBI/
  232.  
  233. When loading the DBI module (do not confuse this with the Apache::DBI module)
  234. it looks if the environment variable GATEWAY_INTERFACE starts with 'CGI-Perl'
  235. and if the module Apache::DBI has been loaded. In this case every connect
  236. request will be forwarded to the Apache::DBI module. This looks if a database
  237. handle from a previous connect request is already stored and if this handle is
  238. still valid using the ping method. If these two conditions are fulfilled it
  239. just returns the database handle. The parameters defining the connection have
  240. to be exactly the same, including the connect attributes ! If there is no
  241. appropriate database handle or if the ping method fails, a new connection is
  242. established and the handle is stored for later re-use. There is no need to
  243. remove the disconnect statements from your code. They won't do anything because
  244. the Apache::DBI module overloads the disconnect method.
  245.  
  246. The Apache::DBI module still has a limitation: it keeps database connections
  247. persistent on a per process basis. The problem is, if a user accesses several
  248. times a database, the http requests will be handled very likely by different
  249. servers. Every server needs to do its own connect. It would be nice, if all
  250. servers could share the database handles. Currently this is not possible,
  251. because of the distinct name-space of every process. Also it is not possible
  252. to create a database handle upon startup of the httpd and then inheriting this
  253. handle to every subsequent server. This will cause clashes when the handle is
  254. used by two processes at the same time.
  255.  
  256. With this limitation in mind, there are scenarios, where the usage of
  257. Apache::DBI is depreciated. Think about a heavy loaded Web-site where every
  258. user connects to the database with a unique userid. Every server would create
  259. many database handles each of which spawning a new backend process. In a short
  260. time this would kill the web server.
  261.  
  262. Another problem are timeouts: some databases disconnect the client after a
  263. certain time of inactivity. The module tries to validate the database handle
  264. using the ping-method of the DBI-module. This method returns true as default.
  265. If the database handle is not valid and the driver has no implementation for
  266. the ping method, you will get an error when accessing the database. As a
  267. work-around you can try to replace the ping method by any database command,
  268. which is cheap and safe or you can deactivate the usage of the ping method
  269. (see CONFIGURATION below).
  270.  
  271. Here is generalized ping method, which can be added to the driver module:
  272.  
  273. {   package DBD::xxx::db; # ====== DATABASE ======
  274.     use strict;
  275.  
  276.     sub ping {
  277.         my($dbh) = @_;
  278.         my $ret = 0;
  279.         eval {
  280.             local $SIG{__DIE__}  = sub { return (0); };
  281.             local $SIG{__WARN__} = sub { return (0); };
  282.             # adapt the select statement to your database:
  283.             $ret = $dbh->do('select 1');
  284.         };
  285.         return ($@) ? 0 : $ret;
  286.     }
  287. }
  288.  
  289. Transactions: a standard DBI script will automatically perform a rollback
  290. whenever the script exits. In the case of persistent database connections,
  291. the database handle will not be destroyed and hence no automatic rollback
  292. occurs. At a first glance it seems even to be possible, to handle a transaction
  293. over multiple requests. But this should be avoided, because different
  294. requests are handled by different servers and a server does not know the state
  295. of a specific transaction which has been started by another server. In general
  296. it is good practice to perform an explicit commit or rollback at the end of
  297. every script. In order to avoid inconsistencies in the database in case
  298. AutoCommit is off and the script finishes without an explicit rollback, the
  299. Apache::DBI module uses a PerlCleanupHandler to issue a rollback at the
  300. end of every request. Note, that this CleanupHandler will only be used, if
  301. the initial data_source sets AutoCommit = 0. It will not be used, if AutoCommit
  302. will be turned off, after the connect has been done.
  303.  
  304. This module plugs in a menu item for Apache::Status. The menu lists the
  305. current database connections. It should be considered incomplete because of
  306. the limitations explained above. It shows the current database connections
  307. for one specific server, the one which happens to serve the current request.
  308. Other servers might have other database connections. The Apache::Status module
  309. has to be loaded before the Apache::DBI module !
  310.  
  311.  
  312. =head1 CONFIGURATION
  313.  
  314. The module should be loaded upon startup of the Apache daemon.
  315. Add the following line to your httpd.conf or startup.pl:
  316.  
  317.  PerlModule Apache::DBI
  318.  
  319. It is important, to load this module before any other modules using DBI !
  320.  
  321. A common usage is to load the module in a startup file via the PerlRequire
  322. directive. See eg/startup.pl for an example.
  323.  
  324. There are two configurations which are server-specific and which can be done
  325. upon server startup:
  326.  
  327.  Apache::DBI->connect_on_init($data_source, $username, $auth, \%attr)
  328.  
  329. This can be used as a simple way to have apache servers establish connections
  330. on process startup.
  331.  
  332.  Apache::DBI->setPingTimeOut($data_source, $timeout)
  333.  
  334. This configures the usage of the ping method, to validate a connection.
  335. Setting the timeout to 0 will always validate the database connection
  336. using the ping method (default). Setting the timeout < 0 will de-activate
  337. the validation of the database handle. This can be used for drivers, which
  338. do not implement the ping-method. Setting the timeout > 0 will ping the
  339. database only if the last access was more than timeout seconds before.
  340.  
  341. For the menu item 'DBI connections' you need to call Apache::Status BEFORE
  342. Apache::DBI ! For an example of the configuration order see startup.pl.
  343.  
  344. To enable debugging the variable $Apache::DBI::DEBUG must be set. This
  345. can either be done in startup.pl or in the user script. Setting the variable
  346. to 1, just reports about a new connect. Setting the variable to 2 enables full
  347. debug output.
  348.  
  349.  
  350. =head1 PREREQUISITES
  351.  
  352. Note that this module needs mod_perl-1.08 or higher, apache_1.3.0 or higher
  353. and that mod_perl needs to be configured with the appropriate call-back hooks:
  354.  
  355.   PERL_CHILD_INIT=1 PERL_STACKED_HANDLERS=1.
  356.  
  357.  
  358. =head1 SEE ALSO
  359.  
  360. L<Apache>, L<mod_perl>, L<DBI>
  361.  
  362.  
  363. =head1 AUTHORS
  364.  
  365. =item *
  366. mod_perl by Doug MacEachern <modperl@apache.org>
  367.  
  368. =item *
  369. DBI by Tim Bunce <dbi-users@isc.org>
  370.  
  371. =item *
  372. Apache::AuthenDBI by Edmund Mergl <E.Mergl@bawue.de>
  373.  
  374.  
  375. =head1 COPYRIGHT
  376.  
  377. The Apache::DBI module is free software; you can redistribute it and/or
  378. modify it under the same terms as Perl itself.
  379.  
  380. =cut
  381.