--- PApp-SQL/SQL.pm 2000/10/21 19:00:53 1.1 +++ PApp-SQL/SQL.pm 2002/11/02 03:33:49 1.28 @@ -1,49 +1,99 @@ =head1 NAME -PApp::SQL - absolutely easy yet fast and powerful sql access +PApp::SQL - absolutely easy yet fast and powerful sql access. =head1 SYNOPSIS use PApp::SQL; - # to be written + + my $st = sql_exec $DBH, "select ... where a = ?", $a; + + local $DBH = ; + my $st = sql_exec \my($bind_a, $bind_b), "select a,b ..."; + my $st = sql_insertid + sql_exec "insert into ... values (?, ?)", $v1, $v2; + my $a = sql_fetch "select a from ..."; + sql_fetch \my($a, $b), "select a,b ..."; + + sql_exists "table where name like 'a%'" + or die "a* required but not existent"; + + my $db = new PApp::SQL::Database "", "DBI:mysql:test", "user", "pass"; + local $PApp::SQL::DBH = $db->checked_dbh; # does 'ping' + + sql_exec $db->dbh, "select ..."; =head1 DESCRIPTION This module provides you with easy-to-use functions to execute sql commands (using DBI). Despite being easy to use, they are also quite -efficient and allow you to write faster programs in less lines of code. - -=over 4 +efficient and allow you to write faster programs in less lines of code. It +should work with anything from perl-5.004_01 onwards, but I only support +5.005+. UTF8 handling (the C family of functions) will only be +effective with perl version 5.006 and beyond. + +If the descriptions here seem terse or if you always wanted to know +what PApp is then have a look at the PApp module which uses this module +extensively but also provides you with a lot more gimmicks to play around +with to help you create cool applications ;) =cut package PApp::SQL; -use DBI; - -#use PApp::Exception; # not yet used +use DBI (); BEGIN { - use base Exporter; + use base qw(Exporter DynaLoader); - $VERSION = 0.1; + $VERSION = 0.141; @EXPORT = qw( - sql_exec sql_fetch sql_fetchall sql_exists sql_insertid $sql_exec + sql_exec sql_fetch sql_fetchall sql_exists sql_insertid $sql_exec + sql_uexec sql_ufetch sql_ufetchall sql_uexists ); @EXPORT_OK = qw( connect_cached ); - require XSLoader; - XSLoader::load PApp::SQL, $VERSION; + bootstrap PApp::SQL $VERSION; } our $sql_exec; # last result of sql_exec's execute call our $DBH; # the default database handle -our $database; # the current SQL::Database object, if applicable +our $Database; # the current SQL::Database object, if applicable our %dbcache; +=head2 GLOBAL VARIABLES + +=over 4 + +=item $sql_exec + +Since the C family of functions return a statement handle there +must be another way to test the return value of the C call. This +global variable contains the result of the most recent call to C +done by this module. + +=item $PApp::SQL::DBH + +The default database handle used by this module if no C<$DBH> was +specified as argument. See C for a discussion. + +=item $PApp::SQL::Database + +The current default C-object. Future versions might +automatically fall back on this database and create database handles from +it if neccessary. At the moment this is not used by this module but might +be nice as a placeholder for the database object that corresponds to +$PApp::SQL::DBH. + +=back + +=head2 FUNCTIONS + +=over 4 + =item $dbh = connect_cached $id, $dsn, $user, $pass, $flags, $connect (not exported by by default) @@ -52,11 +102,18 @@ flags from C<$flags>. These are just the same arguments as given to Cconnect>. -The database handle will be cached under the unique id C<$id>. If the same -id is requested later, the cached handle will be checked (using ping), and -the connection will be re-established if necessary (be sure to prefix your -application or module name to the id to make it "more" unique. Things like -__PACKAGE__ . __LINE__ work fine as well). +The database handle will be cached under the unique id +C<$id|$dsn|$user|$pass>. If the same id is requested later, the +cached handle will be checked (using ping), and the connection will +be re-established if necessary (be sure to prefix your application or +module name to the id to make it "more" unique. Things like __PACKAGE__ . +__LINE__ work fine as well). + +The reason C<$id> is necessary is that you might specify special connect +arguments or special flags, or you might want to configure your $DBH +differently than maybe other applications requesting the same database +connection. If none of this is necessary for your application you can +leave C<$id> empty (i.e. ""). If specified, C<$connect> is a callback (e.g. a coderef) that will be called each time a new connection is being established, with the new @@ -67,6 +124,9 @@ # try your luck opening the papp database without access info $dbh = connect_cached __FILE__, "DBI:mysql:papp"; +Mysql-specific behaviour: The default setting of +C is TRUE, you can overwrite this, though. + =cut sub connect_cached { @@ -74,14 +134,19 @@ # the following line is duplicated in PApp::SQL::Database::new $id = "$id\0$dsn\0$user\0$pass"; unless ($dbcache{$id} && $dbcache{$id}->ping) { - #warn "connecting to ($dsn|$user|$pass|$flags)\n";#d# - # first, nuke our cache (sooory ;) + # first, nuke our statement cache (sooory ;) cachesize cachesize 0; + + # then make mysql behave more standardly by default + $dsn =~ /^[Dd][Bb][Ii]:mysql:/ + and $dsn !~ /;mysql_client_found_rows/ + and $dsn .= ";mysql_client_found_rows=1"; + # then connect anew $dbcache{$id} = eval { DBI->connect($dsn, $user, $pass, $flags) } || eval { DBI->connect($dsn, $user, $pass, $flags) } - || die $DBI::errstr; + || die "unable to connect to database $dsn: $DBI::errstr\n"; $connect->($dbcache{$id}) if $connect; } $dbcache{$id}; @@ -89,6 +154,8 @@ =item $sth = sql_exec [dbh,] [bind-vals...,] "sql-statement", [arguments...] +=item $sth = sql_uexec + C is the most important and most-used function in this module. Runs the given sql command with the given parameters and returns the @@ -97,18 +164,32 @@ called only once for each distinct sql call (please keep in mind that the returned statement will always be the same, so, if you call C with the same dbh and sql-statement twice (e.g. in a subroutine you -called), the statement handle for the first call mustn't be used. +called), the statement handle for the first call mustn't not be in use +anymore, as the subsequent call will re-use the handle. The database handle (the first argument) is optional. If it is missing, -C first tries to use the variable C<$DBH> in the current (= -calling) package and, if that fails, it tries to use database handle in -C<$PApp::SQL::DBH>, which you can set before calling these functions. +it tries to use database handle in C<$PApp::SQL::DBH>, which you can set +before calling these functions. NOTICE: future and former versions of +PApp::SQL might also look up the global variable C<$DBH> in the callers +package. + +=begin comment + +If it is missing, C first tries to use the variable C<$DBH> +in the current (= calling) package and, if that fails, it tries to use +database handle in C<$PApp::SQL::DBH>, which you can set before calling +these functions. + +=end comment The actual return value from the C<$sth->execute> call is stored in the package-global (and exported) variable C<$sql_exec>. If any error occurs C will throw an exception. +C is similar to C but upgrades all input arguments to +utf8 before calling the C method. + Examples: # easy one @@ -127,7 +208,9 @@ =item sql_fetch -Execute a sql-statement and fetch the first row of results. Depending on +=item sql_ufetch + +Execute an sql-statement and fetch the first row of results. Depending on the caller context the row will be returned as a list (array context), or just the first columns. In table form: @@ -148,8 +231,15 @@ ... and it's still quite fast unless you fetch large amounts of data. +C is similar to C but upgrades all input values to +utf8 and forces all result values to utf8 (this does I include result +parameters, only return values. Using bind variables in cinjunction with +sql_u* functions results in undefined behaviour). + =item sql_fetchall +=item sql_ufetchall + Similarly to C, but all result rows will be fetched (this is of course inefficient for large results!). The context is ignored (only list context makes sense), but the result still depends on the number of @@ -170,14 +260,23 @@ my ($name, $age, $place) = @$_; } -=item sql_exists " where ...", args... +C is similar to C but upgrades all input +values to utf8 and forces all result values to utf8 (see the caveats in +the description of C, though). + +=item sql_exists " where ...", args... + +=item sql_uexists Check wether the result of the sql-statement "select xxx from $first_argument" would be empty or not (that is, imagine the string -"select from" were prepended to your statement (it isn't)). Should work +"select * from" were prepended to your statement (it isn't)). Should work with every database but can be quite slow, except on mysql, where this should be quite fast. +C is similar to C but upgrades all parameters to +utf8. + Examples: print "user 7 exists!\n" @@ -188,16 +287,40 @@ =cut -# uncodumented, since unportable (only works with DBH even!). yet it is exported (aaargh!) -sub sql_insertid { - $DBH->{mysql_insertid}; +=item $lastid = sql_insertid $sth + +Returns the last automatically created key value. It must be executed +directly after executing the insert statement that created it. This is +what is actually returned for various databases. If your database is +missing, please send me an e-mail on how to implement this ;) + + mysql: first C column set to NULL + postgres: C column (is there a way to get the last SERIAL?) + sybase: C column of the last insert (slow) + informix: C or C column of the last insert + +Except for sybase, this does not require a server access. + +=cut + +sub sql_insertid($) { + my $sth = shift or die "sql_insertid requires a statement handle"; + my $dbh = $sth->{Database}; + my $driver = $dbh->{Driver}{Name}; + + $driver eq "mysql" and return $sth->{mysql_insertid}; + $driver eq "Pg" and return $sth->{pg_oid_status}; + $driver eq "Sybase" and return sql_fetch($dbh, 'SELECT @@IDENTITY'); + $driver eq "Informix" and return $sth->{ix_sqlerrd}[1]; + + die "sql_insertid does not spport the dbd driver '$driver', please see PApp::SQL::sql_insertid"; } =item [old-size] = cachesize [new-size] Returns (and possibly changes) the LRU cache size used by C. The default is somewhere around 50 (= the 50 last recently used statements -will be cached). It shouldn't be too large, since a simple linear listed +will be cached). It shouldn't be too large, since a simple linear list is used for the cache at the moment (which, for small (<100) cache sizes is actually quite fast). @@ -212,14 +335,16 @@ =item reinitialize [not exported] -Clears any internal caches (statement cache, database handle cache). +Clears any internal caches (statement cache, database handle +cache). Should be called after C and other accidents that invalidate +database handles. =cut sub reinitialize { cachesize cachesize 0; for (values %dbcache) { - eval { $_->disconnect }; + eval { $_->{InactiveDestroy} = 1 }; } undef %dbcache; } @@ -228,13 +353,16 @@ =cut +reinitialize; + package PApp::SQL::Database; =head2 THE DATABASE CLASS -Again (sigh) the problem of persistency. What do you do when you have to serialize on object -that contains (or should contain) a database handle? Short answer: you don't. Long answer: -you can embed the necessary information to recreate the dbh when needed. +Again (sigh) the problem of persistency. What do you do when you have +to serialize on object that contains (or should contain) a database +handle? Short answer: you don't. Long answer: you can embed the necessary +information to recreate the dbh when needed. The C class does that, in a relatively efficient fashion: the overhead is currently a single method call per access (you @@ -284,29 +412,37 @@ Return the DSN (L) fo the database object (e.g. for error messages). +=item $db->login + +Return the login name. + +=item $db->password + +Return the password (emphasizing the fact that the password is stored plaintext ;) + =cut sub dsn($) { my $self = shift; - $self->[1][1]; + (split /\x00/, $self->[0])[1]; +} + +sub login($) { + my $self = shift; + (split /\x00/, $self->[0])[2]; +} + +sub password($) { + my $self = shift; + (split /\x00/, $self->[0])[3]; } =back =cut -reinitialize; - 1; -=head1 BUGS - -As of this writing, sql_fetch and sql_fetchall are not very well tested -(they were just re-written in C). - -sql_exists could be faster (it is written very ugly to not change the -current package). - =head1 SEE ALSO L.