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 / AnyDBD.pm < prev    next >
Encoding:
Perl POD Document  |  2002-09-05  |  13.4 KB  |  423 lines

  1. # $Id: AnyDBD.pm,v 1.15 2002/09/04 12:05:03 matt Exp $
  2.  
  3. package DBIx::AnyDBD;
  4. use DBI;
  5. use strict;
  6. use vars qw/$AUTOLOAD $VERSION/;
  7.  
  8. $VERSION = '2.01';
  9.  
  10. sub new {
  11.     my $class = shift;
  12.     my %args = @_;
  13.     my $dbh = DBI->connect(
  14.             $args{dsn}, 
  15.             $args{user}, 
  16.             $args{pass},
  17.             ($args{attr} ||
  18.                 {
  19.                     AutoCommit => 1,
  20.                     PrintError => 0,
  21.                     RaiseError => 1,
  22.                 })
  23.             );
  24.     die "Can't connect: " . DBI->errstr unless $dbh;
  25.     my $package = $args{'package'} || $class;
  26.     my $self = bless { 'package' => $package, dbh => $dbh }, $class;
  27.     $self->rebless;
  28.     $self->_init if $self->can('_init');
  29.     return $self;
  30. }
  31.  
  32. sub connect {
  33.     my $class = shift;
  34.     my ($dsn, $user, $pass, $args, $package) = @_;
  35.     my $dbh = DBI->connect($dsn, $user, $pass, $args);
  36.     return undef unless $dbh;
  37.     $package ||= $class;
  38.     my $self = bless { 'package' => $package, 'dbh' => $dbh }, $class;
  39.     $self->rebless;
  40.     $self->_init if $self->can('_init');
  41.     return $self;
  42. }
  43.  
  44. sub rebless {
  45.     my $self = shift;
  46.     my $driver = ucfirst($self->{dbh}->{Driver}->{Name});
  47.     if ( $driver eq 'Proxy' ) {
  48.     # Looking into the internals of DBD::Proxy is maybe a little questionable
  49.     ($driver) = $self->{dbh}->{proxy_client}->{application} =~ /^DBI:(.+?):/;
  50.     }
  51.     my $class = $self->{'package'};
  52.     my ($odbc, $ado) = ($driver eq 'ODBC', $driver eq 'ADO');
  53.     if ($odbc || $ado) {
  54.         my $name;
  55.         
  56.         if ($odbc) {
  57.             no strict;
  58.             $name = $self->{dbh}->func(17, GetInfo);
  59.         }
  60.         elsif ($ado) {
  61.             $name = $self->{dbh}->{ado_conn}->Properties->Item('DBMS Name')->Value;
  62.         } 
  63.         else {
  64.             die "Can't determine driver name!\n";
  65.         }
  66.         
  67.         if ($name eq 'Microsoft SQL Server') {
  68.             $driver = 'MSSQL';
  69.         }
  70.         elsif ($name eq 'SQL Server') {
  71.             $driver = 'Sybase';
  72.         }
  73.         elsif ($name =~ /Oracle/) {
  74.             $driver = 'Oracle';
  75.         }
  76. #             elsif ($name eq 'ACCESS') {
  77. #                   $driver = 'Access';
  78. #             }
  79. #             elsif ($name eq 'Informix') {
  80. #                   $driver = 'Informix'; # caught by "else" condition below
  81. #             }
  82.         elsif ($name eq 'Adaptive Server Anywhere') {
  83.             $driver = 'ASAny';
  84.         } elsif ($name eq 'ADABAS D') {
  85.             $driver = 'AdabasD';
  86.         }
  87.         else {  # this should catch Access and Informix
  88.             $driver = lc($name);
  89.             $driver =~ s/\b(\w)/uc($1)/eg;
  90.             $driver =~ s/\s+/_/g;
  91.         }
  92.     }
  93.     
  94.     my $dir;
  95.     ($dir = $self->{package}) =~ s/::/\//g;
  96.     load_module("$dir/Default.pm") or die "Cannot find $dir/Default.pm module in \@INC for $self->{package}!";
  97.  
  98.     if (!load_module("$dir/$driver.pm")) {
  99.         # no package for driver - use Default instead
  100.         bless $self, "${class}::Default";
  101.         # make Default -> DBIx::AnyDBD hierarchy
  102.         add_isa("${class}::Default", 'DBIx::AnyDBD');
  103.     }
  104.     else {
  105.         # package OK...
  106.         
  107.         bless $self, "${class}::${driver}";
  108.         
  109.         if ($ado) {
  110.             if (load_module("$dir/ADO.pm")) {
  111.                 if (!load_module("$dir/ODBC.pm")) {
  112.                     add_isa("${class}::${driver}", "${class}::ADO");
  113.                     add_isa("${class}::ADO", "${class}::Default");
  114.                 }
  115.                 else {
  116.                     add_isa("${class}::${driver}", "${class}::ADO");
  117.                     add_isa("${class}::ADO", "${class}::ODBC");
  118.                     add_isa("${class}::ODBC", "${class}::Default");
  119.                 }
  120.                 return;
  121.             }
  122.         }
  123.         
  124.         if ($odbc) {
  125.             if (load_module("$dir/ODBC.pm")) {
  126.                 add_isa("${class}::${driver}", "${class}::ODBC");
  127.                 add_isa("${class}::ODBC", "${class}::Default");
  128.                 return;
  129.             }
  130.         }
  131.         
  132.         # make Default -> DBIx::AnyDBD hierarchy
  133.         add_isa("${class}::Default", 'DBIx::AnyDBD');
  134.         
  135.         # make Driver -> Default hierarchy
  136.         add_isa("${class}::${driver}", "${class}::Default");
  137.     }
  138.     
  139. }
  140.  
  141. sub add_isa {
  142.     my ($class, $newval) = @_;
  143.     no strict 'refs';
  144.     unshift @{"${class}::ISA"}, $newval unless $class->isa($newval);
  145. }
  146.  
  147. sub load_module {
  148.     my $module = shift;
  149.     
  150.     eval {
  151.         require $module;
  152.     };
  153.     if ($@) {
  154.         if ($@ =~ /^Can't locate $module in \@INC/) {
  155.             undef $@;
  156.             return 0;
  157.         }
  158.         else {
  159.             die $@;
  160.         }
  161.     }
  162.     
  163.     return 1;
  164. }
  165.  
  166. sub get_dbh {
  167.     # maybe add code here to check connection status.
  168.     # or maybe add check once every 10 get_dbh's...
  169.     return shift->{dbh};
  170. }
  171.  
  172. sub DESTROY {
  173.     my $self = shift;
  174.     my $dbh;
  175.     if ($dbh = $self->get_dbh) {
  176.         $dbh->disconnect;
  177.     }
  178. }
  179.  
  180. sub AUTOLOAD {
  181.     (my $func = $AUTOLOAD) =~ s/.*:://;
  182.     no strict 'refs';
  183.     # The following is much more elegant but doesn't work!
  184.     # *$func = sub {
  185.     #      unshift @_ => shift->get_dbh;
  186.     #      goto &{$_[0]->can($func)};
  187.     # };    
  188.     *$func = sub {
  189.       my $dbh = shift->get_dbh;
  190.       if (wantarray) {
  191.       my @r = eval { $dbh->$func(@_) };
  192.       return @r unless $@;
  193.       } elsif (defined wantarray) {
  194.       my $r = eval { $dbh->$func(@_) };
  195.       return $r unless $@;
  196.       } else {
  197.       eval { $dbh->$func(@_) };
  198.       return unless $@;
  199.       };
  200.       if ( $@ =~ /(.*) at ${\__FILE__} .*$/ ) {
  201.       # We want croak() to report errors as occouring
  202.       # in the implementation class even though we are related
  203.       package DBIx::AnyDBD::Carp;
  204.       require Carp;
  205.       Carp::croak "$1";
  206.       }
  207.       die; # Unreachable ?
  208.     };
  209.     goto &$func;
  210. }
  211.  
  212. 1;
  213.  
  214. __END__
  215.  
  216. =head1 NAME
  217.  
  218. DBIx::AnyDBD - DBD independant class
  219.  
  220. =head1 DESCRIPTION
  221.  
  222. This class provides application developers with an abstraction class
  223. a level away from DBI, that allows them to write an application that
  224. works on multiple database platforms. The idea isn't to take away the
  225. responsibility for coding different SQL on different platforms, but
  226. to simply provide a platform that uses the right class at the right
  227. time for whatever DB is currently in use.
  228.  
  229. =head1 SYNOPSIS
  230.  
  231.     use DBIx::AnyDBD;
  232.  
  233.     my $db = DBIx::AnyDBD->connect("dbi:Oracle:sid1",
  234.         "user", "pass", {}, "MyClass");
  235.  
  236.     my $foo = $db->foo;
  237.     my $blee = $db->blee;
  238.  
  239. That doesn't really tell you much... Because you have to implement a
  240. bit more than that. Underneath you have to have a module
  241. MyClass::Oracle that has methods foo() and blee in it. If those
  242. methods don't exist in MyClass::Oracle, it will check in MyClass::Default,
  243. allowing you to implement code that doesn't need to be driver
  244. dependant in the same module. The foo() and blee() methods will recieve
  245. the DBIx::AnyDBD instance as thier first parameter, and any parameters
  246. you pass just go as parameters.
  247.  
  248. See the example Default.pm and Sybase.pm classes in the AnyDBD directory
  249. for an example.
  250.  
  251. =head1 Implementation
  252.  
  253. Underneath it's all implemented using the ISA hierarchy, which is modified
  254. when you connect to your database. The inheritance tree ensures that the
  255. right functions get called at the right time. There is also an AUTOLOADer
  256. that steps in if the function doesn't exist and tries to call the function
  257. on the database handle (i.e. in the DBI class). The sub-classing uses
  258. C<ucfirst($dbh->{Driver}->{Name})> (along with some clever fiddling for
  259. ODBC and ADO) to get the super-class, so if you don't know what to name
  260. your class (see the list below first) then check that.
  261.  
  262. =head1 API
  263.  
  264. =head2 new( ... )
  265.  
  266.     dsn => $dsn,
  267.     user => $user,
  268.     pass => $pass,
  269.     attr => $attr,
  270.     package => $package
  271.  
  272. new() is a named parameter call that connects and creates a new db object
  273. for your use. The named parameters are dsn, user, pass, attr and package.
  274. The first 4 are just the parameters passed to DBI->connect, and package
  275. contains the package prefix for your database dependant modules, for example,
  276. if package was "MyPackage", the AUTOLOADer would look for
  277. MyPackage::Oracle::func, and then MyPackage::Default::func. Beware that the
  278. DBD driver will be ucfirst'ed, because lower case package names are reserved
  279. as pragmas in perl. See the known DBD package mappings below.
  280.  
  281. If package parameter is undefined then the package name used to call
  282. the constructor is used.  This will usually be DBIx::AnyDBD.  This, in
  283. itself, is not very useful but is convenient if you subclass
  284. DBIx::AnyDBD.
  285.  
  286. If attr is undefined then the default attributes are:
  287.  
  288.     AutoCommit => 1
  289.     PrintError => 0
  290.     RaiseError => 1
  291.  
  292. So be aware if you don't want your application dying to either eval{} all
  293. db sections and catch the exception, or pass in a different attr parameter.
  294.  
  295. After re-blessing the object into the database specific object, DBIx::AnyDBD
  296. will call the _init() method on the object, if it exists. This allows you
  297. to perform some driver specific post-initialization.
  298.  
  299. =head2 connect($dsn, $user, $pass, $attr, $package)
  300.  
  301. connect() is very similar to DBI->connect, taking exactly the same first
  302. 4 parameters. The 5th parameter is the package prefix, as above.
  303.  
  304. connect() doesn't try and default attributes for you if you don't pass them.
  305.  
  306. After re-blessing the object into the database specific object, DBIx::AnyDBD
  307. will call the _init() method on the object, if it exists. This allows you
  308. to perform some driver specific post-initialization.
  309.  
  310. =head2 $db->get_dbh()
  311.  
  312. This method is mainly for the DB dependant modules to use, it returns the
  313. underlying DBI database handle. There will probably have code added here
  314. to check the db is still connected, so it may be wise to always use this
  315. method rather than trying to retrieve $self->{dbh} directly.
  316.  
  317. =head1 Known DBD Package Mappings
  318.  
  319. The following are the known DBD driver name mappings, including ucfirst'ing
  320. them:
  321.  
  322.     DBD::Oracle => Oracle.pm
  323.     DBD::Sybase => Sybase.pm
  324.     DBD::Pg => Pg.pm
  325.     DBD::mysql => Mysql.pm
  326.     DBD::Informix => Informix.pm
  327.     DBD::AdabasD => AdabasD.pm
  328.  
  329. If you use this on other platforms, let me know what the mappings are.
  330.  
  331. =head2 ODBC
  332.  
  333. ODBC needed special support, so when run with DBD::ODBC, we call GetInfo
  334. to find out what database we're connecting to, and then map to a known package.
  335. The following are the known package mappings for ODBC:
  336.  
  337.     Microsoft SQL Server (7.0 and MSDE) => MSSQL.pm
  338.     Microsoft SQL Server (6.5 and below) => Sybase.pm (sorry!)
  339.     Sybase (ASE and ASA) => Sybase.pm
  340.     Microsoft Access => Access.pm
  341.     Informix => Informix.pm
  342.     Oracle => Oracle.pm
  343.     Adabas D => AdabasD.pm
  344.  
  345. Anything that isn't listed above will get mapped using the following rule:
  346.  
  347.     Get rdbms name using: $dbh->func(17, GetInfo);
  348.     Change whitespace to a single underscore
  349.     Add .pm on the end.
  350.  
  351. So if you need to know what your particular database will map to, simply run
  352. the $dbh->func(17, GetInfo) method to find out.
  353.  
  354. ODBC also inserts C<$package::ODBC.pm> into the hierarchy if it exists, so
  355. the hierarchy will look like:
  356.  
  357.     DBIx::AnyDBD <= ODBC.pm <= Informix.pm
  358.  
  359. (given that the database you're connecting to would be Informix). This is
  360. useful because ODBC provides its own SQL abstraction layer.
  361.  
  362. =head2 ADO
  363.  
  364. ADO uses the same semantics as ODBC for determining the right driver or
  365. module to load. However in extension to that, it inserts an ADO.pm into
  366. the inheritance hierarchy if it exists, so the hierarchy would look like:
  367.  
  368.     DBIx::AnyDBD <= ODBC.pm <= ADO.pm <= Informix.pm
  369.  
  370. I do understand that this is not fundamentally correct, as not all ADO
  371. connections go through ODBC, but if you're doing some of that funky stuff
  372. with ADO (such as queries on MS Index Server) then you're not likely to
  373. need this module!
  374.  
  375. =head1 Controlling error propagation from AUTOLOADed DBI methods
  376.  
  377. Typicially the implementation packages will make calls to DBI methods
  378. as though they were methods of the DBIx::AnyDBD object.  If one of
  379. these methods reports an error in DBI::AnyDBD then the error is caught
  380. and rethrown by DBIx::AnyDBD so that the error is reported as occuring
  381. in the implementation module.  It does this by calling Carp::croak()
  382. with the current package set to DBIx::AnyDBD::Carp.
  383.  
  384. Usually this the the right thing to do but sometimes you may want to
  385. report the error in the line containing the original method call on
  386. the DBIx::AnyDBD object.  In this case you should temporarily set
  387. @DBIx::AnyDBD::Carp::ISA.
  388.  
  389.     my $db = DBIx::AnyDBD->connect("dbi:Oracle:sid1",
  390.         "user", "pass", {}, "MyClass");
  391.  
  392.     my $foo = $db->foo;
  393.     my $blee = $db->blee("too few arguments"); # Error reported here
  394.  
  395.     package MyClass::Oracle;
  396.  
  397.     sub foo {
  398.     shift->prepare("Invalid SQL"); # Error reported here
  399.     }
  400.  
  401.     sub blee {
  402.     local @DBIx::AnyDBD::Carp::ISA = __PACKAGE__;
  403.     shift->selectall_arrayref(BLEE_STATEMENT,{},@_); # Error not reported here
  404.     }
  405.  
  406. =head1 LICENCE
  407.  
  408. This module is free software, and you may distribute it under the same
  409. terms as Perl itself.
  410.  
  411. =head1 SUPPORT
  412.  
  413. Commercial support for this module is available on a pay per incident
  414. basis from Fastnet Software Ltd. Contact matt@sergeant.org for further
  415. details. Alternatively join the DBI-Users mailing list, where I'll help
  416. you out for free!
  417.  
  418. =head1 SEE ALSO
  419.  
  420. Check out the example files in the example/ directory.
  421.  
  422. =cut
  423.