![]() |
Grundlagen CGI-Programmierung mit Perlvon Prof. Jürgen Plate |

Die Anweisung
#!/usr/bin/perl use DBI;bindet das DBI-Modul in ein Perl-Programm ein. Die Schnittstelle zur Datenbank ist damit jedoch noch nicht komplett. Es fehlt noch der spezielle Treiber welcher von DBI verwendet wird, hier DBD::mysql.
Anmerkung: Es wird vorausgesetzt, daß der MySQL-Server installiert ist. Zusätzlich sind zwei Perl-Module nötig: das allgemeine Datenbankinterface DBI und der spezielle DBD::mysql-Treiber vom CPAN. Die Installation vom CPAN ist recht einfach:
perl -MCPAN -e shell cpan> install DBD::mysql cpan> install DBIBevor es losgeht, muß eine Datenbank angelegt werden. Es versteht sich von selbst, daß man sich vorher Gedanken darüber gemacht hat, wie die Datenbank aussehen soll, z. B. welche Tabellen sie enthalten muß und welche Felder mit welchem Datentyp die Tabellen enthalten. Aber das ist ein anderes Thema. Für unser Beispiel wird eine ganz einfach Datenbank mit nur einer Tabelle verwendet: Der Autoverleih "Bring M. Backalive" unterhält einen Fuhrpark von diversen Fahrzeugen, deren Daten gespeichert werden sollen. Damit es übersichtlich bleibt, lassen wir Typ, Baujahr, Leistung, Kilometerstand, und vieles andere weg - uns reicht das polizeiliche Kennzeichen und die Anzahl der Sitzplätze.
Dazu packen wir noch eine laufende Fahrzeugnummer (Integer) in die Tabelle (als Primärindex, siehe unten). Das Kennzeichen wird als Text-String und die Anzahl der Sitze als Integer definiert. Der Autoverleih legt die Daten aller Fahrzeuge in der Tabelle "fahrzeuge" in der Datenbank ab. Jede Zeile der Tabelle entspricht genau einem Fahrzeug des Fuhrparks:
ID INT kennz VARCHAR(20) sitze INT
Das Programm braucht eine logische Datenbank namens "bring_m_backalive". Das Anlegen kann direkt mit dem mysql-Client erfolgen (CREATE DATABASE bring_m_backalive;). Oder Sie installieren sich das Tool PHPMyAdmin, mit dem man alle Datenbankoperationen über den Browser erledigen kann.
#!/usr/bin/perl
use warnings;
use strict;
my $USER = "root";
my $PASS = "secret";
use DBI;
my @dsn = ("DBI:mysql:database=bring_m_backalive;" .
"host=localhost", $USER, $PASS);
# Datenbank andocken
my $dbh = DBI->connect(@dsn,
{ PrintError => 0,
AutoCommit => 1,
}
) or die $DBI::errstr;
# Alte Tabelle loeschen
$dbh->do("DROP TABLE fahrzeuge");
# Neu anlegen
$dbh->do("CREATE TABLE fahrzeuge (
ID INTEGER AUTO_INCREMENT,
kennz VARCHAR(20),
sitze INTEGER,
PRIMARY KEY (ID))"
) or die $dbh->errstr();
$dbh->disconnect();
Oben definieren wir in "$USER" und "$PASS" Benutzerkennung und Passwort,
unter denen die MySQL-Installation Zugriffe erlaubt. Im Beispiel spezifiziert
@dsn (Data Source Name) die Konfigurationsparameter für die verwendete
Datenbankinstallation. Der erste Parameter
"DBI:mysql:database=bring_m_backalive;host=localhost"legt den "DBD::mysql"-Treiber fest; der Name der Datenbank lautet "bring_m_backalive" der Datenbankserver läuft auf "localhost". In der Regel kann man den Prarameter noch abkürzen und
"DBI:mysql:bring_m_backalive;localhost"schreiben - man muß nur die Reihenfolge einhalten. Gegebenenfalls kann noch die Portnummer anhängen (wieder durch ":" getrennt), sofern diese vom Standardport abweicht. Die weiteren Parameter "$USER" und "$PASS" übernehmen die Benutzerkennung und das Passwort.
Die connect-Funktion der DBI-Klasse verbindet das Skript mit der laufenden Datenbank. Sie nimmt die eben definierten Konfigurationsparameter in "@dsn" und eine Referenz auf einen Hash mit weiteren Steuerungsparametern entgegen. Die Option "PrintError" wird auf 0 (False) gesetzt, damit die DBI-Methoden Fehlermeldungen unterdrücken. Die von connect() zurückgelieferte Referenz auf ein DBI-Objekt erlaubt es nun, mit der Datenbank zu kommunizieren.
Die do()-Methode setzt SQL-Befehle ab, die von der Datenbankengine ausgefüht werden. Nach dem Löschen einer etwa vorhandenen Tabelle wird eine leere Tabelle "fahrzeuge" mit allen Felddefinitionen angelegt. Den mehrzeiligen String zwischen den Anführungszeichen gibt Perl an die Methode weiter. Die Oder-Bedingung fängt eventuell von do() gelieferte Fehler ab.
Mit errstr() des Datenbank-Handles $dbh erhält man den Text des letzten Fehlers zurück, so daß das Konstrukt oben einfach mit die() und der Fehlermeldung der Datenbank abbricht, falls etwas schief geht. Bei CGI-Programmen landen solche Fehlermeldungen im Error-Log des Webservers.
"ID" in der Tabelle ist die laufende Nummer des Fahrzeugs. Das Attribut "AUTO_INCREMENT" ist eine MySQL-spezifische SQL-Erweiterung und gibt an, dass die Datenbank den numerischen Wert dieses Feldes selbst erzeugen soll: Beginnend mit "1" wird jeder neue Tabelleneintrag um eins erhöht. Das Attribut "PRIMARY KEY" (Primärschlüssel) bestimmt, über welche Spalte die Datenbank die Tabelle primär indiziert. Da die Seriennummer "ID" numerisch und für jede Zeile eindeutig ist, bietet sie sich als Primärschlüssel an. Es ist übrigens in der Regel sinnvoll bei jeder Tabelle einen solchen Primary Key anzulegen, über den sich Datensätze eindeutig refernziert lassen.
Ein erzeugtes Handle wird durch die Methode disconnect() wieder geschlossen.
Wer will, kan sich auch eine Funktion für das Öffnen der Datenbank schreiben, in der alle unveränderlichen Angaben enthalten sind. Die folgenden Funktion öffnet eine Datenbank auf einem angegebenen Host und liefert ein Datenbankhandle $dbh zurück:
sub open_dbase
{
my %Datenbank =
( dbname => 'fahrzeuge',
user => $ENV{'LOGNAME'},
# ggf. hier einen festen User eintragen
pass => 'Geheim',
host => 'localhost',
driver => 'mysql',
@_
);
# alternativ kann das Passwort auch von Datei eingelesen werden
# (guenstig, wenn mehrere Programme auf die DB zugreifen
my $dsn = "DBI:$Datenbank{'driver'}:$Datenbank{'dbname'}:$Datenbank{'host'}";
my $dbh = DBI->connect($dsn, $Datenbank{'user'}, $Datenbank{'pass'});
print STDERR "Error: $DBI::errstr\n" unless $dbh;
return $dbh;
}
Einen kleinen Perl-Trick stellt die Zeile "@_" im Hash %Datenbank dar. Man
kann so über die Parameterzeile beim Aufruf die voreingestellten Werte überschreiben,
z.B.:
$dbh = open_dbase( dbname => 'lager' ); $dbh = open_dbase( dbname => 'lager', pw => 'secret' );
Will man sehen, was beim Datenbankzugtiff geschieht, kann noch die Zeile
$dbh->trace(LEVEL);vor dem "return" eingefügt werden. Als Trace-LEVEL reicht normalerweise der Wert 1. Je höher man geht, desto mehr Informationen erhät man.
Sind alle Tabellen angelegt, kann die Datenbank mit Informationen gefüllt werden. Zuvor sollen aber noch zwei nützliche Hilfsdienste vorgestellt werden. Eine Liste der Tabelle bekommt man mit der Zeile:
my @tables = $dbh->func('_ListTables');
Das entspricht dem SQL-Befehle SHOW TABLES.
Eine Beschreibung einer Tabelle $table bekommt man mit:
my $sth = $dbh->prepare("DESC $table");
$sth->execute();
die "$DBI::errstr\n" if ($DBI::err);
... wie unten gezeigt auslesen
$dbh->do("INSERT INTO fahrzeuge (kennz, sitze) VALUES ('M-AX 007','8') ")
or die $dbh->errstr();
Das SQL-Kommando "INSERT INTO ..." spezifiziert die Spaltennamen in der ersten Klammer und
weist ihnen die Werte der zweiten Klammer zu. SQL-Zeichenketten müssen in einfache
Anführungszeichen eingeschlossen sein. Enthält der String einfache Quotes, müssen diese
mit "\'" ausmaskiert werden. Werden Variableninhalte verwendet, wie beim folgenden
Beispiel, müssen die Strings mit Quotes versehen werden:
my $sitze = 8;
my $kennz = "M-AX 007";
my $kennz_q = $dbh->quote($kennz);
$dbh->do("INSERT INTO fahrzeuge (kennz, sitze) VALUES ($kennz_q, $sitze)")
or die $dbh->errstr();
Dafür stellt DBI eine datenbankspezifische Methode bereit: $dbh->quote.
Beispielsweise gibt der Aufruf $dbh->quote("Da gibt's was auf die Ohren!")
als Ergebnis 'Da gibt\'s was auf die Ohren!' zurück.
Will man die geschilderten Ouotierungs-Probleme bei der do()-Methode vermeiden, verwendet man stattdessen eine Kombination aus prepare() und execute(). Grundsätzlich entspricht die Kombination dem do():
my $sth = $dbh->prepare("INSERT INTO fahrzeuge (kennz, sitze) VALUES ('M-AX 007','8')")
or die $dbh->errstr();
$sth->execute() or die $dbh->errstr();
Damit ist aber noch nicht viel gewonnen. Erst wenn man prepare() einen Rumpf des
"INSERT"-Befehls mitgibt, in dem alle Werte durch Fragezeichen ersetzt sind, kann man
die aktuellen Werte der execute()-Methode mitgeben:
my $sth = $dbh->prepare("INSERT INTO fahrzeuge (kennz, sitze) VALUES (?,?)")
or die $dbh->errstr();
$sth->execute($kennz, $sitze) or die $dbh->errstr();
use warnings;
use strict;
use DBI;
my $USER = "root";
my $PASSWORD = "";
my $dbh = DBI->connect("DBI:mysql:database=bring_m_backalive;" .
"host=localhost",$USER, $PASSWORD,
{PrintError => 0,
AutoCommit => 1}
);
my @fuhrpark = (
[ "M-AX 007", 8 ],
[ "M-N 1718", 4 ],
[ "M-OP 471", 4 ],
[ "M-DD 313", 2 ],
[ "M-IX 707", 2 ],
);
my $sth = $dbh->prepare("INSERT INTO fahrzeuge (kennz, sitze)
VALUES (?,?)") or die $dbh->errstr();
for (@fuhrpark)
{
my($kennz, $sitze) = @$_;
$sth->execute($kennz, $sitze) or die $dbh->errstr();
}
$dbh->disconnect();
Auch das SQL-"DELETE"-Kommando kann mit DBI über die "do"-Methode abgesetzt werden.
Die "WHERE"-Klausel gibt an, welche Zeilen gelöscht werden sollen. Folgendes Programmstück
löscht alle Zweisitzer:
$dbh->do("DELETE FROM fahrzeuge WHERE sitze = '2'")
or die $dbh->errstr();
Das "DELETE"-Kommando gibt die Anzahl der gelöschten Datensätze zurück. Besitzt Der Verleih
keine klassischen Roadster, läuft der Methodenaufruf auf die die()-Anweisung. Ganz korrekt
wäre es allerdings, den Rückgabewert von do() einer Variablen zuzuweisen und sie mit
defined() daraufhin zu prüfen, ob do() den Wert "undef" zurückgeliefert hat.
Der SQL-"UPDATE"-Befehl verändert Spaltenwerte bestimmter Datensätze. Er ist immer dann anzuwenden, wenn ein Datensatz schon existiert und sich nur einzelne Ausprägungen ändern sollen. Ändert sich beispielsweise das Kennzeichen der Limousine M-AX 007 nach MA-X 008, erledigt das folgende Programmstück die Korrektur:
$dbh->do("UPDATE fahrzeuge SET kennz = 'MA-X 008' WHERE kennz = 'M-AX 4712'")
or die $dbh->errstr();
Der Annahme folgend, dass der Wagen M-AX 007 auf jeden Fall in der Datenbank ist, geht auch
hier die "or"-Bedingung durch. Sie bricht nicht nur bei allgemeinen Fehlern ab, sondern auch,
wenn die Datenbank das Kennzeichen nicht findet. Erst ein defined() und dann Testen, ob
der Rückgabewert ungleich 0 ist, wäre sauber.
[ [z1s1, z1s2, ...], [z2s2, z2s2, ...], ..., ]Auf das Beispiel angewendet nimmt folgendes Programmstück das Ergebnis des "SELECT"-Befehls entgegen und gibt die Daten anschließend als Tabelle formatiert aus:
my $aref = $dbh->selectall_arrayref("SELECT * FROM fahrzeuge")
or die $dbh->errstr();
for my $row (@$aref)
{
my($id, $kennz, $sitze) = @$row;
printf "%02d %-10s %d\n", $id, $kennz, $sitze;
}
Selektiert die Datenbank-Abfrage nur eine einzige Zeile, geht es auch einfacher. Um die Zahl der Sitze des Fahrzeugs M-AX 007 herauszufinden, holt selectrow_array() die Daten der ersten passenden Zeile ein:
my ($ID, $sitze) = $dbh->selectrow_array
("SELECT ID, sitze FROM fahrzeuge WHERE kennz = 'M-AX 007'")
or die $dbh->errstr();
print "$ID $sitze\n";
Auch wenn das Ergebnis nur aus einem einzigen Wert besteht, kann man selectrow_array nutzen.
Um die Anzahl der Datensätze zu bestimmen, verwendet man "SELECT COUNT(*) FROM fahrzeuge".
Das Ergebnis ist garantiert eine einzige Zeile mit einer einzigen Spalte:
my ($count) = $dbh->selectrow_array("SELECT count(*) FROM fahrzeuge");
die $dbh->errstr() unless defined $count;
print "$count Einträge in der Tabelle.\n";
Bei vielen Ergebnis-Datensätzen muß man nicht alle auf einmal in ein Array oder Hash einzulesen. Stattdessen geht man Satz für Satz vor. Die zuvor benutzte Methode selectall_arrayref() ist eigentlich eine Zusammenfassung dreier Befehle: prepare() bereitet eine SQL-Query vor und liefert eine Objektreferenz zurück. Per execute() wird die Query an die Datenbank geschickt, von der die fetchrow_array()-Methode das Ergebnis zeilenweise abholt und jeweils eine Liste der Spaltenwerte zurückgibt:
my $sth = $dbh->prepare("SELECT * FROM fahrzeuge WHERE kennz LIKE 'M-%'")
or die $dbh->errstr();
$sth->execute() or die $dbh->errstr;
while (my($id, $kennz, $sitze) = $sth->fetchrow_array())
{
print("$id, $kennz, $sitze\n");
}
Mit selectrow_hashref() erhält man die Ergebnisse in einem Hash, der den Spalten-Namen die Werte zuweist. Das folgende Beispiel selektiert alle Mietwagen mit 4 bis 8 Sitzen.
my $sth = $dbh->prepare
("SELECT * FROM fahrzeuge WHERE sitze >= 4 AND sitze <= 8")
or die $dbh->errstr();
$sth->execute() or die $dbh->errstr;
while (my $h = $sth->fetchrow_hashref())
{
print "$h->{kennz} $h->{sitze}\n";
}
die $dbh->errstr() if $dbh->err();
$dbh->methode(...) or die "Fehler: ", $dbh->errstr();zu formulieren, um das Programm abzubrechen, oder den Fehler wenigstens abzufangen, um darauf im Programm zu reagieren. Die Methode $dbh->errstr() gibt im Fehlerfall stets den zugehörigen Text der Fehlermeldung zurück, der Hinweise zur Ursache liefert. Eine Ausnahme: connect() meldet im Erfolgsfall die Objektreferenz für weitere Aufrufe und im Fehlerfall "undef". Die Fehlermeldung ist aber trotzdem über den Skalar "$DBI::errstr" zugänglich. Man kann "DBI" aber auch mittels RaiseError => 1 anweisen, bei jedem auftretenden Fehler sofort zu sterben:
my $dbh = DBI->connect(@DSN,
{ RaiseError => 1,
AutoCommit => 1,
};
$dbh->do("...");
$dbh->do("...");
# ...
Um die die-Exception abzufangen, läßt sich dann alles in eine "eval"-Anweisung
einbetten. Dann springt Perl sofort aus dem "eval"-Block, sobald irgendwo ein Fehler
auftritt, und setzt die Variable "$@" auf den Fehlertext. Auf diesen kann der folgende
"if"-Block reagieren.
eval
{
$dbh = DBI->connect(@DSN,
{ RaiseError => 1,
AutoCommit => 1,
};
$dbh->do(...);
$dbh->do(...);
};
print "Fehler: $@\n" if($@);
$sql = "select count(*) from Users where Username='" . $User .
' AND Password='" $Pass "';"
Sieht ganz brauchbar aus, oder? Sehen Sie sich an, wie der SQL-String aussieht,
wenn sich der Administrator mit admin / geheim einloggt:
select count(*) from users where Username='admin' AND Password='geheim';Alles bestens! Was aber würde ein Cracker versuchen? Der gibt beispielsweise für Benutzername und und Passwort jeweils die Zeichenfolge ' OR '1'='1 ein (beachten Sie auch die Apostrophe). Der SQL String sieht dann folgendermaß aus:
select count(*) from users where
Username='' OR '1'='1' AND
Password='' OR '1'='1';
Da 1 immer gleich 1 ist, braucht der Bösewicht weder Userkennung
noch Passwort, um auf die geschützte Seite zu gelangen. Nur, weil
die Benutzereingaben nicht validiert wurden.
Aber es geht noch besser! Wie wär es mit dem Benutzernamen '; DROP TABLE users; --? Das ergibt die SQL-Abfrage:
select count(*) from users where Username='';
DROP TABLE users; --' AND Password='';
Oha! Da hat Joe Crack doch gerade die Tabelle mit den Benutzeraccounts
gelöscht! Die zwei Striche (--) leiten einen Kommentar ein,
weswegen der String "AND ..." keinen Fehler ergab.
Statt die Usertabelle platt zu machen, kann sich unser schlauer Freund aber
auch einen eigenen Account anlegen:
select count(*) from users where Username='';
insert into Users values('Joe','topsecret'); --' AND Password='';
SQL-Injection ist also das Einfügen von beliebigen SQL-Befehlen in Formulare, die dann am Server ausgeführt werden. Warum passiert das? Weil die Formulare dem Input der Benutzer vertrauen, und ihn nicht entsprechend validieren! Generell sind alle Daten, die von außen kommen, "böse". Die Validierung erfolgt mit Stringfunktionen, Regulären Ausdrücken, Plausbilitäts-Checks usw.
Haben Sie den Input auf Gültigkeit abgeklopft, sollten Sie nicht den Fehler machen, trotzdem die alten, fehleranfälligen SQL-Konstrukte weiterzuverwenden. Sie könnten ja ein Angriffs-Szenario vergessen haben. Für die Umstellung bieten sich parametrisierte Kommandos an. Generell sieht ein solches Kommando für das Loginformular aus, wie wir es schon bei der Dateneingabe gesehen haben:
SELECT COUNT(*) FROM Users WHERE Username=? AND Password=?Der SQL-String wird auch hier nicht mehr dynamisch zusammengebaut. Adaptiert auf das aktuelle Problem ergibt sich:
check($User); # Validierung
check($Pass); # Validierung
$sth = $dbh->prepare("SELECT COUNT(*) FROM Users WHERE Username=? AND Password=?")
or die $dbh->errstr();
$sth->execute($User, $Pass) or die $dbh->errstr();
(Coding of sub check ist left as a punishment to the reader.)
Auch völlig ohne Validierung funktioniert hier keinerlei SQL-Injection mehr (vergessen Sie die Idee gleich wieder, All input is evil, until proven otherwise!). Der gezeigte Code macht etwas mehr Aufwand als das einfache Zusammenbauen eines SQL-Statements. Allerdings ersparen Sie sich einiges an Kopfschmerzen. Und er funktioniert nicht nur für SELECT, sondern genauso für INSERT, UPDATE oder DELETE.
$dbh Datenbank-Handle $sth Statement-Handle $rc Rückgabe-Code (Status) $rv Rückgabewert (Status)
$dbh = DBI->connect("DBI:mysql:$datenbank", $benutzer, $passwort);
$dbh = DBI->connect("DBI:mysql:$datenbank:$hostname",
$benutzer, $passwort);
$dbh = DBI->connect("DBI:mysql:$datenbank:$hostname:$port",
$benutzer, $passwort);
Wenn der Benutzername und/oder das Passwort nicht angegeben werden,
verwendet DBI die Werte der DBI_USER- und DBI_PASS-
Umgebungsvariablen. Wenn Sie keinen Hostnamen angeben, wird 'localhost'
verwendet. Wenn Sie keine Portnummer angeben, wird der MySQL-Port verwendet.
Modifikatoren:
| mysql_read_default_file=datei | Liest `datei' als eine Optionsdatei. |
| mysql_read_default_group=group_name | Beim Lesen einer Optionsdatei ist die Standardgruppe normalerweise die[client]-Gruppe. Wenn Sie die mysql_read_default_group- Option angeben, wird die Standardgruppe [gruppenname]. |
| mysql_compression=1 | Aktiviert die Kompression während der Kommunikation zwischen Client und Server. |
| mysql_socket=/pfad/zur/socket | Gibt den Pfad des Unix-Sockets an, der zum Verbinden mit dem Ser verwendet wird. |
Sie können mehrere Modifikatoren angeben, dabei muss jedem ein Semikolon vorangestellt sein. Wenn Sie zum Beispiel vermeiden wollen, dass sie Benutzername und Passwort im DBI-Skript angeben müssen, können Sie sie aus der Optionsdatei '~/.my.cnf' nehmen. Ihr connect-Aufruf sieht folgendermaßen aus:
$dbh = DBI->connect("DBI:mysql:$datenbank"
. ";mysql_read_default_file=$ENV{HOME}/.my.cnf",
$benutzer, $passwort);
Dieser Aufruf liest die Optionen für die [client]-Gruppe aus der
Optionsdatei. Wenn Sie dasselbe für die [perl]-Gruppe tun wollen,
könnte Ihr Code so aussehen:
$dbh = DBI->connect("DBI:mysql:$Datenbank"
. ";mysql_read_default_file=$ENV{HOME}/.my.cnf"
. ";mysql_read_default_group=perl",
$benutzer, $passwort);
$rc = $dbh->disconnect;
$sth = $dbh->prepare($statement)
or die "$statement: $dbh->errstr kann nicht vorbereitet werden\n";
$rv = $sth->execute
or die "Die Query: $sth->errstr kann nicht ausgeführt werden.";
$rv = $dbh->do($statement)
or die "$statement: $dbh- >errstr kann nicht vorbereitet werden\n";
Im Allgemeinen ist die do-Methode VIEL schneller (und vorzuziehen)
als die prepare/execute-Methoden, die ohne Parameter
aufgerufen werden.
$rv = $dbh->quote($string)
while(@row = $sth->fetchrow_array)
{
print "$row[0] $row[1] $row[2]\n";
}
while($row_ref = $sth->fetchrow_arrayref)
{
print "$row_ref->[0] $row_ref->[1] $row_ref->[2]\n";
}
while($hash_ref = $sth->fetchrow_hashref)
{
print "$hash_ref->{vorname}";
print "$hash_ref->{nachname}";
print "$hash_ref->{title}\n";
}
my $table = $sth->fetchall_arrayref
or die "$sth->errstr\n";
my($i, $j);
for $i ( 0 .. $#{$table} )
{
for $j ( 0 .. $#{$table->[$i]} )
{
print "$table->[$i][$j] ";
}
print "\n";
}
$rc = $sth->finish;
$rv = $sth->rows;
$rv = $sth->{NULLABLE};
$rv = $sth->{NUM_OF_FIELDS};
@dbs = DBI->datasource("mysql");
$sth->{'ChopBlanks'} = 1;
DBI->trace(2); # alles tracen DBI->trace(2,"/tmp/dbi.out"); # alles nach /tmp/dbi.out tracen $dth->trace(2); # diese Datenbankverbindung tracen $sth->trace(2); # dieses Statement-Handle tracen.Sie können DBI-Tracing auch anschalten, indem Sie die DBI_TRACE-Umgebungsvariable setzen. Wenn Sie sie auf einen numerischen Wert setzen, ist das dasselbe, wie DBI->(wert) aufzurufen. Wenn Sie sie auf einen Pfadnamen setzen, ist das dasselbe, wie DBI->(2,wert) aufzurufen.
Zum vorhergehenden Abschnitt |
Zum Inhaltsverzeichnis |
Zum nächsten Abschnitt |