diff options
Diffstat (limited to 'Master/tlpkg/tlperl0/lib/DBI/ProxyServer.pm')
-rwxr-xr-x | Master/tlpkg/tlperl0/lib/DBI/ProxyServer.pm | 890 |
1 files changed, 890 insertions, 0 deletions
diff --git a/Master/tlpkg/tlperl0/lib/DBI/ProxyServer.pm b/Master/tlpkg/tlperl0/lib/DBI/ProxyServer.pm new file mode 100755 index 00000000000..44581f19674 --- /dev/null +++ b/Master/tlpkg/tlperl0/lib/DBI/ProxyServer.pm @@ -0,0 +1,890 @@ +# $Header: /home/timbo/dbi/lib/DBI/RCS/ProxyServer.pm,v 11.9 2003/05/14 11:08:17 timbo Exp $ +# -*- perl -*- +# +# DBI::ProxyServer - a proxy server for DBI drivers +# +# Copyright (c) 1997 Jochen Wiedmann +# +# The DBD::Proxy module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. In particular permission +# is granted to Tim Bunce for distributing this as a part of the DBI. +# +# +# Author: Jochen Wiedmann +# Am Eisteich 9 +# 72555 Metzingen +# Germany +# +# Email: joe@ispsoft.de +# Phone: +49 7123 14881 +# +# +############################################################################## + + +require 5.004; +use strict; + +use RPC::PlServer 0.2001; +# require DBI; # deferred till AcceptVersion() to aid threading +require Config; + + +package DBI::ProxyServer; + + + +############################################################################ +# +# Constants +# +############################################################################ + +use vars qw($VERSION @ISA); + +$VERSION = "0.3005"; +@ISA = qw(RPC::PlServer DBI); + + +# Most of the options below are set to default values, we note them here +# just for the sake of documentation. +my %DEFAULT_SERVER_OPTIONS; +{ + my $o = \%DEFAULT_SERVER_OPTIONS; + $o->{'chroot'} = undef, # To be used in the initfile, + # after loading the required + # DBI drivers. + $o->{'clients'} = + [ { 'mask' => '.*', + 'accept' => 1, + 'cipher' => undef + } + ]; + $o->{'configfile'} = '/etc/dbiproxy.conf' if -f '/etc/dbiproxy.conf'; + $o->{'debug'} = 0; + $o->{'facility'} = 'daemon'; + $o->{'group'} = undef; + $o->{'localaddr'} = undef; # Bind to any local IP number + $o->{'localport'} = undef; # Must set port number on the + # command line. + $o->{'logfile'} = undef; # Use syslog or EventLog. + + # XXX don't restrict methods that can be called (trust users once connected) + $o->{'XXX_methods'} = { + 'DBI::ProxyServer' => { + 'Version' => 1, + 'NewHandle' => 1, + 'CallMethod' => 1, + 'DestroyHandle' => 1 + }, + 'DBI::ProxyServer::db' => { + 'prepare' => 1, + 'commit' => 1, + 'rollback' => 1, + 'STORE' => 1, + 'FETCH' => 1, + 'func' => 1, + 'quote' => 1, + 'type_info_all' => 1, + 'table_info' => 1, + 'disconnect' => 1, + }, + 'DBI::ProxyServer::st' => { + 'execute' => 1, + 'STORE' => 1, + 'FETCH' => 1, + 'func' => 1, + 'fetch' => 1, + 'finish' => 1 + } + }; + if ($Config::Config{'usethreads'} eq 'define') { + $o->{'mode'} = 'threads'; + } elsif ($Config::Config{'d_fork'} eq 'define') { + $o->{'mode'} = 'fork'; + } else { + $o->{'mode'} = 'single'; + } + # No pidfile by default, configuration must provide one if needed + $o->{'pidfile'} = 'none'; + $o->{'user'} = undef; +}; + + +############################################################################ +# +# Name: Version +# +# Purpose: Return version string +# +# Inputs: $class - This class +# +# Result: Version string; suitable for printing by "--version" +# +############################################################################ + +sub Version { + my $version = $DBI::ProxyServer::VERSION; + "DBI::ProxyServer $version, Copyright (C) 1998, Jochen Wiedmann"; +} + + +############################################################################ +# +# Name: AcceptApplication +# +# Purpose: Verify DBI DSN +# +# Inputs: $self - This instance +# $dsn - DBI dsn +# +# Returns: TRUE for a valid DSN, FALSE otherwise +# +############################################################################ + +sub AcceptApplication { + my $self = shift; my $dsn = shift; + $dsn =~ /^dbi:\w+:/i; +} + + +############################################################################ +# +# Name: AcceptVersion +# +# Purpose: Verify requested DBI version +# +# Inputs: $self - Instance +# $version - DBI version being requested +# +# Returns: TRUE for ok, FALSE otherwise +# +############################################################################ + +sub AcceptVersion { + my $self = shift; my $version = shift; + require DBI; + DBI::ProxyServer->init_rootclass(); + $DBI::VERSION >= $version; +} + + +############################################################################ +# +# Name: AcceptUser +# +# Purpose: Verify user and password by connecting to the client and +# creating a database connection +# +# Inputs: $self - Instance +# $user - User name +# $password - Password +# +############################################################################ + +sub AcceptUser { + my $self = shift; my $user = shift; my $password = shift; + return 0 if (!$self->SUPER::AcceptUser($user, $password)); + my $dsn = $self->{'application'}; + $self->Debug("Connecting to $dsn as $user"); + local $ENV{DBI_AUTOPROXY} = ''; # :-) + $self->{'dbh'} = eval { + DBI::ProxyServer->connect($dsn, $user, $password, + { 'PrintError' => 0, + 'Warn' => 0, + 'RaiseError' => 1, + 'HandleError' => sub { + my $err = $_[1]->err; + my $state = $_[1]->state || ''; + $_[0] .= " [err=$err,state=$state]"; + return 0; + } }) + }; + if ($@) { + $self->Error("Error while connecting to $dsn as $user: $@"); + return 0; + } + [1, $self->StoreHandle($self->{'dbh'}) ]; +} + + +sub CallMethod { + my $server = shift; + my $dbh = $server->{'dbh'}; + # We could store the private_server attribute permanently in + # $dbh. However, we'd have a reference loop in that case and + # I would be concerned about garbage collection. :-( + $dbh->{'private_server'} = $server; + $server->Debug("CallMethod: => " . do { local $^W; join(",", @_)}); + my @result = eval { $server->SUPER::CallMethod(@_) }; + my $msg = $@; + undef $dbh->{'private_server'}; + if ($msg) { + $server->Debug("CallMethod died with: $@"); + die $msg; + } else { + $server->Debug("CallMethod: <= " . do { local $^W; join(",", @result) }); + } + @result; +} + + +sub main { + my $server = DBI::ProxyServer->new(\%DEFAULT_SERVER_OPTIONS, \@_); + $server->Bind(); +} + + +############################################################################ +# +# The DBI part of the proxyserver is implemented as a DBI subclass. +# Thus we can reuse some of the DBI methods and overwrite only +# those that need additional handling. +# +############################################################################ + +package DBI::ProxyServer::dr; + +@DBI::ProxyServer::dr::ISA = qw(DBI::dr); + + +package DBI::ProxyServer::db; + +@DBI::ProxyServer::db::ISA = qw(DBI::db); + +sub prepare { + my($dbh, $statement, $attr, $params, $proto_ver) = @_; + my $server = $dbh->{'private_server'}; + if (my $client = $server->{'client'}) { + if ($client->{'sql'}) { + if ($statement =~ /^\s*(\S+)/) { + my $st = $1; + if (!($statement = $client->{'sql'}->{$st})) { + die "Unknown SQL query: $st"; + } + } else { + die "Cannot parse restricted SQL statement: $statement"; + } + } + } + my $sth = $dbh->SUPER::prepare($statement, $attr); + my $handle = $server->StoreHandle($sth); + + if ( $proto_ver and $proto_ver > 1 ) { + $sth->{private_proxyserver_described} = 0; + return $handle; + + } else { + # The difference between the usual prepare and ours is that we implement + # a combined prepare/execute. The DBD::Proxy driver doesn't call us for + # prepare. Only if an execute happens, then we are called with method + # "prepare". Further execute's are called as "execute". + my @result = $sth->execute($params); + my ($NAME, $TYPE); + my $NUM_OF_FIELDS = $sth->{NUM_OF_FIELDS}; + if ($NUM_OF_FIELDS) { # is a SELECT + $NAME = $sth->{NAME}; + $TYPE = $sth->{TYPE}; + } + ($handle, $NUM_OF_FIELDS, $sth->{'NUM_OF_PARAMS'}, + $NAME, $TYPE, @result); + } +} + +sub table_info { + my $dbh = shift; + my $sth = $dbh->SUPER::table_info(); + my $numFields = $sth->{'NUM_OF_FIELDS'}; + my $names = $sth->{'NAME'}; + my $types = $sth->{'TYPE'}; + + # We wouldn't need to send all the rows at this point, instead we could + # make use of $rsth->fetch() on the client as usual. + # The problem is that some drivers (namely DBD::ExampleP, DBD::mysql and + # DBD::mSQL) are returning foreign sth's here, thus an instance of + # DBI::st and not DBI::ProxyServer::st. We could fix this by permitting + # the client to execute method DBI::st, but I don't like this. + my @rows; + while (my ($row) = $sth->fetch()) { + last unless defined $row; + push(@rows, [@$row]); + } + ($numFields, $names, $types, @rows); +} + + +package DBI::ProxyServer::st; + +@DBI::ProxyServer::st::ISA = qw(DBI::st); + +sub execute { + my $sth = shift; my $params = shift; my $proto_ver = shift; + my @outParams; + if ($params) { + for (my $i = 0; $i < @$params;) { + my $param = $params->[$i++]; + if (!ref($param)) { + $sth->bind_param($i, $param); + } + else { + if (!ref(@$param[0])) {#It's not a reference + $sth->bind_param($i, @$param); + } + else { + $sth->bind_param_inout($i, @$param); + my $ref = shift @$param; + push(@outParams, $ref); + } + } + } + } + my $rows = $sth->SUPER::execute(); + if ( $proto_ver and $proto_ver > 1 and not $sth->{private_proxyserver_described} ) { + my ($NAME, $TYPE); + my $NUM_OF_FIELDS = $sth->{NUM_OF_FIELDS}; + if ($NUM_OF_FIELDS) { # is a SELECT + $NAME = $sth->{NAME}; + $TYPE = $sth->{TYPE}; + } + $sth->{private_proxyserver_described} = 1; + # First execution, we ship back description. + return ($rows, $NUM_OF_FIELDS, $sth->{'NUM_OF_PARAMS'}, $NAME, $TYPE, @outParams); + } + ($rows, @outParams); +} + +sub fetch { + my $sth = shift; my $numRows = shift || 1; + my($ref, @rows); + while ($numRows-- && ($ref = $sth->SUPER::fetch())) { + push(@rows, [@$ref]); + } + @rows; +} + + +1; + + +__END__ + +=head1 NAME + +DBI::ProxyServer - a server for the DBD::Proxy driver + +=head1 SYNOPSIS + + use DBI::ProxyServer; + DBI::ProxyServer::main(@ARGV); + +=head1 DESCRIPTION + +DBI::Proxy Server is a module for implementing a proxy for the DBI proxy +driver, DBD::Proxy. It allows access to databases over the network if the +DBMS does not offer networked operations. But the proxy server might be +useful for you, even if you have a DBMS with integrated network +functionality: It can be used as a DBI proxy in a firewalled environment. + +DBI::ProxyServer runs as a daemon on the machine with the DBMS or on the +firewall. The client connects to the agent using the DBI driver DBD::Proxy, +thus in the exactly same way than using DBD::mysql, DBD::mSQL or any other +DBI driver. + +The agent is implemented as a RPC::PlServer application. Thus you have +access to all the possibilities of this module, in particular encryption +and a similar configuration file. DBI::ProxyServer adds the possibility of +query restrictions: You can define a set of queries that a client may +execute and restrict access to those. (Requires a DBI driver that supports +parameter binding.) See L</CONFIGURATION FILE>. + +The provided driver script, L<dbiproxy>, may either be used as it is or +used as the basis for a local version modified to meet your needs. + +=head1 OPTIONS + +When calling the DBI::ProxyServer::main() function, you supply an +array of options. These options are parsed by the Getopt::Long module. +The ProxyServer inherits all of RPC::PlServer's and hence Net::Daemon's +options and option handling, in particular the ability to read +options from either the command line or a config file. See +L<RPC::PlServer>. See L<Net::Daemon>. Available options include + +=over 4 + +=item I<chroot> (B<--chroot=dir>) + +(UNIX only) After doing a bind(), change root directory to the given +directory by doing a chroot(). This is useful for security, but it +restricts the environment a lot. For example, you need to load DBI +drivers in the config file or you have to create hard links to Unix +sockets, if your drivers are using them. For example, with MySQL, a +config file might contain the following lines: + + my $rootdir = '/var/dbiproxy'; + my $unixsockdir = '/tmp'; + my $unixsockfile = 'mysql.sock'; + foreach $dir ($rootdir, "$rootdir$unixsockdir") { + mkdir 0755, $dir; + } + link("$unixsockdir/$unixsockfile", + "$rootdir$unixsockdir/$unixsockfile"); + require DBD::mysql; + + { + 'chroot' => $rootdir, + ... + } + +If you don't know chroot(), think of an FTP server where you can see a +certain directory tree only after logging in. See also the --group and +--user options. + +=item I<clients> + +An array ref with a list of clients. Clients are hash refs, the attributes +I<accept> (0 for denying access and 1 for permitting) and I<mask>, a Perl +regular expression for the clients IP number or its host name. + +=item I<configfile> (B<--configfile=file>) + +Config files are assumed to return a single hash ref that overrides the +arguments of the new method. However, command line arguments in turn take +precedence over the config file. See the L<"CONFIGURATION FILE"> section +below for details on the config file. + +=item I<debug> (B<--debug>) + +Turn debugging mode on. Mainly this asserts that logging messages of +level "debug" are created. + +=item I<facility> (B<--facility=mode>) + +(UNIX only) Facility to use for L<Sys::Syslog>. The default is +B<daemon>. + +=item I<group> (B<--group=gid>) + +After doing a bind(), change the real and effective GID to the given. +This is useful, if you want your server to bind to a privileged port +(<1024), but don't want the server to execute as root. See also +the --user option. + +GID's can be passed as group names or numeric values. + +=item I<localaddr> (B<--localaddr=ip>) + +By default a daemon is listening to any IP number that a machine +has. This attribute allows to restrict the server to the given +IP number. + +=item I<localport> (B<--localport=port>) + +This attribute sets the port on which the daemon is listening. It +must be given somehow, as there's no default. + +=item I<logfile> (B<--logfile=file>) + +Be default logging messages will be written to the syslog (Unix) or +to the event log (Windows NT). On other operating systems you need to +specify a log file. The special value "STDERR" forces logging to +stderr. See L<Net::Daemon::Log> for details. + +=item I<mode> (B<--mode=modename>) + +The server can run in three different modes, depending on the environment. + +If you are running Perl 5.005 and did compile it for threads, then the +server will create a new thread for each connection. The thread will +execute the server's Run() method and then terminate. This mode is the +default, you can force it with "--mode=threads". + +If threads are not available, but you have a working fork(), then the +server will behave similar by creating a new process for each connection. +This mode will be used automatically in the absence of threads or if +you use the "--mode=fork" option. + +Finally there's a single-connection mode: If the server has accepted a +connection, he will enter the Run() method. No other connections are +accepted until the Run() method returns (if the client disconnects). +This operation mode is useful if you have neither threads nor fork(), +for example on the Macintosh. For debugging purposes you can force this +mode with "--mode=single". + +=item I<pidfile> (B<--pidfile=file>) + +(UNIX only) If this option is present, a PID file will be created at the +given location. Default is to not create a pidfile. + +=item I<user> (B<--user=uid>) + +After doing a bind(), change the real and effective UID to the given. +This is useful, if you want your server to bind to a privileged port +(<1024), but don't want the server to execute as root. See also +the --group and the --chroot options. + +UID's can be passed as group names or numeric values. + +=item I<version> (B<--version>) + +Supresses startup of the server; instead the version string will +be printed and the program exits immediately. + +=back + +=head1 SHUTDOWN + +DBI::ProxyServer is built on L<RPC::PlServer> which is, in turn, built on L<Net::Daemon>. + +You should refer to L<Net::Daemon> for how to shutdown the server, except that +you can't because it's not currently documented there (as of v0.43). +The bottom-line is that it seems that there's no support for graceful shutdown. + +=head1 CONFIGURATION FILE + +The configuration file is just that of I<RPC::PlServer> or I<Net::Daemon> +with some additional attributes in the client list. + +The config file is a Perl script. At the top of the file you may include +arbitraty Perl source, for example load drivers at the start (useful +to enhance performance), prepare a chroot environment and so on. + +The important thing is that you finally return a hash ref of option +name/value pairs. The possible options are listed above. + +All possibilities of Net::Daemon and RPC::PlServer apply, in particular + +=over 4 + +=item Host and/or User dependent access control + +=item Host and/or User dependent encryption + +=item Changing UID and/or GID after binding to the port + +=item Running in a chroot() environment + +=back + +Additionally the server offers you query restrictions. Suggest the +following client list: + + 'clients' => [ + { 'mask' => '^admin\.company\.com$', + 'accept' => 1, + 'users' => [ 'root', 'wwwrun' ], + }, + { + 'mask' => '^admin\.company\.com$', + 'accept' => 1, + 'users' => [ 'root', 'wwwrun' ], + 'sql' => { + 'select' => 'SELECT * FROM foo', + 'insert' => 'INSERT INTO foo VALUES (?, ?, ?)' + } + } + +then only the users root and wwwrun may connect from admin.company.com, +executing arbitrary queries, but only wwwrun may connect from other +hosts and is restricted to + + $sth->prepare("select"); + +or + + $sth->prepare("insert"); + +which in fact are "SELECT * FROM foo" or "INSERT INTO foo VALUES (?, ?, ?)". + + +=head1 Proxyserver Configuration file (bigger example) + +This section tells you how to restrict a DBI-Proxy: Not every user from +every workstation shall be able to execute every query. + +There is a perl program "dbiproxy" which runs on a machine which is able +to connect to all the databases we wish to reach. All Perl-DBD-drivers must +be installed on this machine. You can also reach databases for which drivers +are not available on the machine where you run the programm querying the +database, e.g. ask MS-Access-database from Linux. + +Create a configuration file "proxy_oracle.cfg" at the dbproxy-server: + + { + # This shall run in a shell or a DOS-window + # facility => 'daemon', + pidfile => 'your_dbiproxy.pid', + logfile => 1, + debug => 0, + mode => 'single', + localport => '12400', + + # Access control, the first match in this list wins! + # So the order is important + clients => [ + # hint to organize: + # the most specialized rules for single machines/users are 1st + # then the denying rules + # the the rules about whole networks + + # rule: internal_webserver + # desc: to get statistical information + { + # this IP-address only is meant + mask => '^10\.95\.81\.243$', + # accept (not defer) connections like this + accept => 1, + # only users from this list + # are allowed to log on + users => [ 'informationdesk' ], + # only this statistical query is allowed + # to get results for a web-query + sql => { + alive => 'select count(*) from dual', + statistic_area => 'select count(*) from e01admin.e01e203 where geb_bezei like ?', + } + }, + + # rule: internal_bad_guy_1 + { + mask => '^10\.95\.81\.1$', + accept => 0, + }, + + # rule: employee_workplace + # desc: get detailled informations + { + # any IP-address is meant here + mask => '^10\.95\.81\.(\d+)$', + # accept (not defer) connections like this + accept => 1, + # only users from this list + # are allowed to log on + users => [ 'informationdesk', 'lippmann' ], + # all these queries are allowed: + sql => { + search_city => 'select ort_nr, plz, ort from e01admin.e01e200 where plz like ?', + search_area => 'select gebiettyp, geb_bezei from e01admin.e01e203 where geb_bezei like ? or geb_bezei like ?', + } + }, + + # rule: internal_bad_guy_2 + # This does NOT work, because rule "employee_workplace" hits + # with its ip-address-mask of the whole network + { + # don't accept connection from this ip-address + mask => '^10\.95\.81\.5$', + accept => 0, + } + ] + } + +Start the proxyserver like this: + + rem well-set Oracle_home needed for Oracle + set ORACLE_HOME=d:\oracle\ora81 + dbiproxy --configfile proxy_oracle.cfg + + +=head2 Testing the connection from a remote machine + +Call a programm "dbish" from your commandline. I take the machine from rule "internal_webserver" + + dbish "dbi:Proxy:hostname=oracle.zdf;port=12400;dsn=dbi:Oracle:e01" informationdesk xxx + +There will be a shell-prompt: + + informationdesk@dbi...> alive + + Current statement buffer (enter '/'...): + alive + + informationdesk@dbi...> / + COUNT(*) + '1' + [1 rows of 1 fields returned] + + +=head2 Testing the connection with a perl-script + +Create a perl-script like this: + + # file: oratest.pl + # call me like this: perl oratest.pl user password + + use strict; + use DBI; + + my $user = shift || die "Usage: $0 user password"; + my $pass = shift || die "Usage: $0 user password"; + my $config = { + dsn_at_proxy => "dbi:Oracle:e01", + proxy => "hostname=oechsle.zdf;port=12400", + }; + my $dsn = sprintf "dbi:Proxy:%s;dsn=%s", + $config->{proxy}, + $config->{dsn_at_proxy}; + + my $dbh = DBI->connect( $dsn, $user, $pass ) + || die "connect did not work: $DBI::errstr"; + + my $sql = "search_city"; + printf "%s\n%s\n%s\n", "="x40, $sql, "="x40; + my $cur = $dbh->prepare($sql); + $cur->bind_param(1,'905%'); + &show_result ($cur); + + my $sql = "search_area"; + printf "%s\n%s\n%s\n", "="x40, $sql, "="x40; + my $cur = $dbh->prepare($sql); + $cur->bind_param(1,'Pfarr%'); + $cur->bind_param(2,'Bronnamberg%'); + &show_result ($cur); + + my $sql = "statistic_area"; + printf "%s\n%s\n%s\n", "="x40, $sql, "="x40; + my $cur = $dbh->prepare($sql); + $cur->bind_param(1,'Pfarr%'); + &show_result ($cur); + + $dbh->disconnect; + exit; + + + sub show_result { + my $cur = shift; + unless ($cur->execute()) { + print "Could not execute\n"; + return; + } + + my $rownum = 0; + while (my @row = $cur->fetchrow_array()) { + printf "Row is: %s\n", join(", ",@row); + if ($rownum++ > 5) { + print "... and so on\n"; + last; + } + } + $cur->finish; + } + +The result + + C:\>perl oratest.pl informationdesk xxx + ======================================== + search_city + ======================================== + Row is: 3322, 9050, Chemnitz + Row is: 3678, 9051, Chemnitz + Row is: 10447, 9051, Chemnitz + Row is: 12128, 9051, Chemnitz + Row is: 10954, 90513, Zirndorf + Row is: 5808, 90513, Zirndorf + Row is: 5715, 90513, Zirndorf + ... and so on + ======================================== + search_area + ======================================== + Row is: 101, Bronnamberg + Row is: 400, Pfarramt Zirndorf + Row is: 400, Pfarramt Rosstal + Row is: 400, Pfarramt Oberasbach + Row is: 401, Pfarramt Zirndorf + Row is: 401, Pfarramt Rosstal + ======================================== + statistic_area + ======================================== + DBD::Proxy::st execute failed: Server returned error: Failed to execute method CallMethod: Unknown SQL query: statistic_area at E:/Perl/site/lib/DBI/ProxyServer.pm line 258. + Could not execute + + +=head2 How the configuration works + +The most important section to control access to your dbi-proxy is "client=>" +in the file "proxy_oracle.cfg": + +Controlling which person at which machine is allowed to access + +=over 4 + +=item * "mask" is a perl regular expression against the plain ip-address of the machine which wishes to connect _or_ the reverse-lookup from a nameserver. + +=item * "accept" tells the dbiproxy-server wether ip-adresse like in "mask" are allowed to connect or not (0/1) + +=item * "users" is a reference to a list of usernames which must be matched, this is NOT a regular expression. + +=back + +Controlling which SQL-statements are allowed + +You can put every SQL-statement you like in simply ommiting "sql => ...", but the more important thing is to restrict the connection so that only allowed queries are possible. + +If you include an sql-section in your config-file like this: + + sql => { + alive => 'select count(*) from dual', + statistic_area => 'select count(*) from e01admin.e01e203 where geb_bezei like ?', + } + +The user is allowed to put two queries against the dbi-proxy. The queries are _not_ "select count(*)...", the queries are "alive" and "statistic_area"! These keywords are replaced by the real query. So you can run a query for "alive": + + my $sql = "alive"; + my $cur = $dbh->prepare($sql); + ... + +The flexibility is that you can put parameters in the where-part of the query so the query are not static. Simply replace a value in the where-part of the query through a question mark and bind it as a parameter to the query. + + my $sql = "statistic_area"; + my $cur = $dbh->prepare($sql); + $cur->bind_param(1,'905%'); + # A second parameter would be called like this: + # $cur->bind_param(2,'98%'); + +The result is this query: + + select count(*) from e01admin.e01e203 + where geb_bezei like '905%' + +Don't try to put parameters into the sql-query like this: + + # Does not work like you think. + # Only the first word of the query is parsed, + # so it's changed to "statistic_area", the rest is omitted. + # You _have_ to work with $cur->bind_param. + my $sql = "statistic_area 905%"; + my $cur = $dbh->prepare($sql); + ... + + +=head2 Problems + +=over 4 + +=item * I don't know how to restrict users to special databases. + +=item * I don't know how to pass query-parameters via dbish + +=back + + +=head1 AUTHOR + + Copyright (c) 1997 Jochen Wiedmann + Am Eisteich 9 + 72555 Metzingen + Germany + + Email: joe@ispsoft.de + Phone: +49 7123 14881 + +The DBI::ProxyServer module is free software; you can redistribute it +and/or modify it under the same terms as Perl itself. In particular +permission is granted to Tim Bunce for distributing this as a part of +the DBI. + + +=head1 SEE ALSO + +L<dbiproxy>, L<DBD::Proxy>, L<DBI>, L<RPC::PlServer>, +L<RPC::PlClient>, L<Net::Daemon>, L<Net::Daemon::Log>, +L<Sys::Syslog>, L<Win32::EventLog>, L<syslog> |