Документирование сервера
Очень много времени и энергии уходит на настройку SQL-сервера и объектов, расположенных на нем. Способ документирования подобной информации может пригодиться в ряде ситуаций. Если база данных будет повреждена, а резервной копии не окажется, вам придется восстанавливать все ее таблицы. При необходимости перенести данные с одного сервера на другой важно знать конфигурацию источника и приемника данных. Даже для собственных баз данных может пригодиться возможность просмотреть карту таблиц.
Чтобы передать всю «прелесть» непереносимой (nonportable) природы администрирования баз данных, приведу пример реализации одной простой задачи для трех различных SQL-серверов с использованием как DBI, так и ODBC. Каждая из этих программ делает одно и то же: выводит список всех баз данных на сервере, их таблицы и структуру каждой таблицы. Эти сценарии можно очень легко расширить для предоставления более подробной информации о каждом объекте. Например, бывает полезно узнать, в каких полях есть значения ti'J.; или NOT NULL. Вывод всех трех программ выглядит одинаково:
— sysad'ii — hosts
i.ame [c:;ar(30)J ipaddr [char(lb)
aliases [char(50)]
owner [char(40)J
dept [char(15)]
bldg [char(10)]
room [char(4)]
manuf [char(10)J
model [char(10)] —hpotter—
customers
cid [char(4)J
cname [varchar(l3)]
city [varchar(20)J
discnt [real(7>] agents
aid [char(3)]
aname [varchar(13)]
city [varchar(20)]
percent [int(10)] products
pid [char(3)]
pname [varchar(13)]
city [varchar(20)]
quantity [int(10)]
price [real(7)] orders
ordno [int(10>]
month [char(3)]
cid [char(4)]
aid [char(3)]
pid [char(3)]
qty [int(K))]
dollars [real(7)
Сервер MySQL и DBI
Вот как выглядит способ получить эту информацию с сервера MySQL с использованием DBI. Существующее в MySQL дополнение команды SHOW очень упрощает эту задачу:
use DBI;
print "Введите имя пользователя: ";
chomp($user = <STDIN>);
print "Введите пароль для $user: chomp($pw = <STDIN>)
Sstart = "mysql"; tf первоначально будем подсоединяться к этой базе данных
О соединяемся с базой данных
$dbh = DBI->connect("DBI:mysql:$start".$user.$pw);
die "Невозможно соединиться: ".$DBI::errstr."\n"
unless (defined $don): ft ищем базы данных на сервере
$sth=$doh->prepartj(q;SHOW DATABASES}) or
die "Невозможно подготовить запрос show dataoaset; ".
die "Невозмохо заполнить запрос
push(@dcs.$a'-e*->[0]): > $sth->finish;
# ищем таблица в каждой базе данных foreach $db (!g>dbs) { print "---$db---\n";
$sth=$dbh->prepare(qq{SHOW TABLES FROM $db}) or
die "Невозможно подготовить запрос
show tables: ". $dbh->t:r rs*
die "Невозможно выполнить запрос show tables: ".
$dbh->crr:
(g>tables=();
while (Saref = $sth->fetchrow_arrayref) {
push(Stables,$aref->[0]); }
$sth->finish;
tt
ищем информацию о полях для каждой таблицы
foreach Stable (^tables) { print "\t$table\n":
$sth=$dbh->prepare(qq[SHOW COLUMNS FROM Stable FROM Sob!) o'
die "Невозможно подготовить запрос show columns: ". $cihh-^errstr."\n";
$sth->execute or die "Невозможно выполнить запрос show columns: ".
while (Saref = $sth->fetchrow_arrayref) {
print "\tAt".Saref->[0]." [". $aref->[1 ]. "]\n":
$stn->finisn \ I
} Sdbr->d;.scor/iec::
Добавим несколько комментариев к приведенной программе:
Если читатель думает, что команды подготовки и выполнения запросов SHOW TABLES и SHOW COLUMNS являются отличными кандидатами на использование заполнителей, то он совершенно прав. К сожалению, именно эта комбинация DBD драйвера /сервера не поддерживает заполнители в таком контексте (по крайней мере, это было так в период написания данной книги). В следующем примере мы столкнемся с подобной ситуацией.
поскольку альтернативы (прописывание их в коде или передача в командной строке, при которых любой, кто просматривает список процессов, сможет их увидеть) еще хуже. В данном случае символы пароля будут отображаться при вводе. Для большей осторожности стоит применять что-то подобное Те г тт. : Reaakey, чтобы подавить отображение символов.
Сервер Sybase и DBI
В этом подразделе представлен аналог для Sybase. Внимательно просмотрите программу, а после этого поговорим о некоторых существенных моментах:
use DBI;
print "Введите имя пользователя: "; chomp($user = <STDIM>);
print "Введите пароль для $user: "; chomp($pw = <STDIN>);
$dbh = DBl->connect('dbi:Sybase'',Suser,$pw);
die "Невозможно соединиться: $DBI::errstr\n" unless (defined $dbh);
ищем базы данных на сервере
$sth = $dbh->prepare(q{SELECT name from master dbo.sysdatarases}) cr
die "Невозможно подготовить запрос к sysdatabases: ".
$db'i->er rst r . "\n", $stli->oxecute or
die "Невозможно выполнить запрос к sysdarabases: '.
$dori->errstr. "\п";
while (Sarof = $sth->fetchrow_arrayref) (
push((3dbs, $aref->[0]): }
$sth->finisn:
foreach $cm (Mbs) {
$dbh->do("USE $do") or
die "Невозможно использовать $db: ".
®tables=():
while ($агч'( - $.::;->fotchrow_arrayref) {
die "Невозможно изменить Sdb: ".
S'Jor->err-str."' n":
# ищем поля для каждой таблицы foreach Stable (tables) { print "\t$table\n";
$sth=$dbh->prepare(qq{EXEC bp_colunns Stable}) or
die "Невозможно подготовить запрос sp^columns ", $obh-:-err.v
$sth->execute or
die "Невозможно выполнить запрос sp^columns: ".$dbh->errstr.
while ($aref = $sth->fetchrow_arrayref) {
print "\t\t",$aref->[3]," [",$aref->[5],"(",
$aref->[6],")]\n": }
$sth->finish; ! }
$dbh->discohnect or warn "Невозможно отсоединиться: ".
$dbh->errstr."\n";
Вот обещанные заметные моменты:
Сервер MS-SQL и ODBC
Наконец, вот код для получения той же информации с сервера MS-SQL через ODBC. Заметьте, что применяемый синтаксис SQL практически идентичен предыдущему примеру благодаря связи Sybase/MS-SQL. Интересны отличия между этим примером и предыдущим:
sysdatabases.
Вот как выглядит программа:
use Win32::ODBC;
print "Введите имя пользователя: ";
chomp($user = <STDIN>);
print "Введите пароль для $user: "; chomp($pw = <STDIN>);
$dsn="sysadm"; и имя источника данных, которое мы используем
# ищем доступные DSN, создаем переменную $dsn,
если она еще не существует
die "Невозможно запросить доступные DSN",Win32::ODBC::Error()."\n"
unless (%dsnavail = Win32::ODBC::DataSources());
if (Idefined $dsnavail{$dsn}) {
die "невозможно создать DSN:".Win32::ODBC::Error()."\n"
unless (Win32::ODBC::ConfigDSN(ODBC_ADD_DSN. "SQL Server", ("DSN=$dsn",
"DESCRIPTION=DSN for PeriSysAdm",
"SERVER=mssql.happy.edu". "DATABASE=master",
"NETWORK=DBMSSOCN".
библиотека сокетов TCP/IP ))); }
it
соединение с основной базой данных
$dbh = new Win32: :ODBC("DSN=$dsn;UID=$iJSer:PWD=$pw: "):
die "Невозможно соединиться с DSN $dsn:".Win32:
# ищем базы данных на сервере
if (defined $dbh->Sql(q{SELECT name from sysdatabases})){
die "Невозможно послать запрос к базе данных:" .Win32: :ODBC: Error().
while ($dbh->FetchRow()){
push(@dbs, $doh->0ata("name"));
}
$dbh->DropCursor();
п
ищем пользовательские таблицы в каждой базе данных
foreach $db (@dbs) {
if (defined $dbh->Sql("use $db")){
die "Невозможно изменить базу данных на $db:" .
Win32::ODBC::Error() . "\n"; >
print "---$db---\n"; @tables=(); if (defined $dbh->
Sql(q{SELECT name from sysobjects
WHERE type="U"})){ die "Невозможно запросить таблицы из $db:" .
Win32::ODBC::Error() . "\n"; } while ($dbh->FetchRow()) {
push(@tables,$dbh->Data("name")); > $dbh->DropCursor();
ft ищем информацию о полях для каждой таблицы
foreach Stable (©tables) { print "\t$table\n";
if (defined $dbh->Sql(" {call sp_columns (\'$table\')} ")){
die "Невозможно запросить поля из таблицы Stable:".
Win32::ODBC::Error() . "\n"; >
while ($dbh->FetchRow()) { @cols=();
@cols=$dbh->Data("COLUMN_NAME","TYPE.NAME","PRECISION");
print "\t\t",$cols[0]," [",$cols[1],"(",$cols[2],")]\n";
} $dbh->DropCursor();
}
}
$dbh->Close();
"SQL Server","DSN=$dsn"))