Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад] [Содержание] [Дальше]

Практика применения Perl, PHP, Apache, MySQL для активных Web-сайтов

(С) Александр Фролов, Григорий Фролов, 2002

7. Базы данных и Perl

7. Базы данных и Perl.. 1

Краткое введение в интерфейс DBI 2

Драйвер DBD.. 2

Модуль DBI 3

Использование DBI 4

Открытие соединения с базой данных. 4

Указание драйвера базы данных и источника данных. 4

Задание идентификатора пользователя и пароля. 5

Выбор способа обработки ошибок. 5

Дескриптор соединения. 6

Выдача команд. 6

Простые команды.. 6

Предварительная подготовка команд. 6

Выполнение заранее подготовленных команд. 6

Привязка параметров. 7

Команда SELECT.. 8

Обработка ошибок DBI в Web-приложениях. 8

Режимы обработки ошибок. 8

Определение причины ошибки. 9

Закрытие соединения. 9

Пакет Trudogolik.. 9

Структура пакета. 10

Глобальные переменные. 10

Функции для работы с DBI 11

DB_OPEN.. 11

DB_CLOSE. 11

DB_SQL_DO.. 12

DB_SQL_PREPARE. 12

DB_SQL_BIND.. 12

DB_SQL_EXECUTE. 13

DB_SQL_FETCHROW_ARRAY.. 13

DB_ERROR.. 13

Пример вызова функций. 14

Удаление всех записей из таблицы.. 14

Обработка исходного файла списка участников. 15

Цикл обработки записей. 15

Вывод содержимого таблицы для контроля. 16

Обработка ошибок. 17

Использование модуля Win32::ODBC.. 17

Открытие соединения с базой данных. 17

Выдача команды SQL. 17

Получение результата выполнения команды SELECT. 18

Закрытие соединения с базой данных. 18

 

В этой главе мы расскажем об использовании модулей Perl, предназначенных для работы с базами данных. При помощи этих модулей Вы легко сможете интегрировать свои Web-приложения с базами данных различных типов на платформе Linux, Microsoft Windows и на других платформах.

Описанные нами приемы, основанные на использовании модуля DBI, позволят Вам при необходимости заменить СУБД или метод доступа другим простым редактированием всего одной строки программного кода. Такой универсализм окажет Вам неплохую услугу, если возникнет необходимость переноса разработанного ранее Web-приложения на другую платформу или необходимость замены одной СУБД другой (например, более мощной или менее дорогостоящей).

Краткое введение в интерфейс DBI

В обозримом прошлом были предприняты неоднократные попытки унификации методов доступа к базам данных. В результате стал доступен, например, интерфейс Open Database Connectivity (ODBC), широко применяющийся на платформе Microsoft Windows.

Популярность метода доступа ODBC, предполагающего применение специальных драйверов баз данных, объясняется относительной независимостью прикладного программного интерфейса этих драйверов от самих баз данных. При этом разработчик СУБД создавал для нее свой драйвер ODBC, а прикладной программист в своих приложениях вызывал функции этого драйвера.

При необходимости адаптировать разработанное ранее приложение для другой СУБД, теоретически достаточно заменить драйвер базы данных. Однако на практике это не так просто сделать. При замене СУБД возникают проблемы архитектурной и концептуальной несовместимости, несовместимости на уровне языка доступа к данным. Например, различные версии языка SQL имеют в своем составе различные наборы команд и обладают различными возможностями. Язык SQL пригоден для работы с реляционными базами данных, но не удобен для обращения к иерархическим и сетевым базам данных, а также к базам данных с другой архитектурой (например, СУБД Adabas допускает использование множественных полей, не имеющих адекватного представления в классическом языке SQL).

Для языка программирования Perl также была разработана унифицированная система доступа к базам данных, основанная на использовании драйверов СУБД. В рамках этой системы используются два модуля — Database Driver (DBD) и Database Interface (DBI).

Хотя указанные модули не решают проблем совместимости СУБД на концептуальном и архитектурном уровне, они могут быть с успехом использованы для организации доступа программ Perl к современным реляционным базам данных. Если эти базы данных и программы составлены таким образом, что используют только базовые возможности SQL, то замена одной СУБД на другую не приведет к необходимости внесения кардинальных изменений в исходный текст программ. С этой точки зрения модули DBВ и DBI представляют, на наш взгляд, большой интерес для разработчиков приложений для разных компьютерных платформ и операционных систем.

Детальное описание интерфейса DBI Вы найдете в [17]. В нашей книге мы рассмотрим только наиболее важные вопросы применения DBI при создании активных Web-приложений.

Драйвер DBD

Модули DBD разрабатываются для каждой СУБД отдельно. Таким образом, для СУБД MySQL нужен один драйвер DBD, а для Oracle — другой. В упоминавшейся ранее библиотеке CPAN имеются драйверы практически для всех популярных СУБД. Что же касается платформы Microsoft Windows, то для нее доступны драйверы DBD, выполняющие интерфейсные функции по отношению к драйверам ODBC и ADO.

Таким образом, чтобы программа Perl могла работать с той или иной СУБД, Вы должны загрузить из Интернета и установить соответствующий драйвер DBD.

В табл. 7-1 мы привели список названий модулей драйверов DBD для некоторых типов СУБД.

Таблица 7-1. Модули  драйверов DBD

СУБД

Модуль драйвера DBD

Sybase

DBD::Sybase

Oracle

DBD::Oracle

Ingres

DBD::Ingres

Interbase

DBD::Interbase

Postgres

DBD::Pg

MySQL

DBD::mysql

Для использования DBI в среде операционной системы Microsoft Window можно установить драйвер DBD:ODBC, обеспечивающий доступ к любой СУБД, для которой существует драйвер ODBC. С помощью драйвера DBD:ODBC можно получить доступ, например, к СУБД Microsoft SQL Server.

Модуль DBI

Программы Perl обращаются к драйверу DBD не самостоятельно, а через интерфейсный модуль DBI. Этот модуль также можно найти в библиотеке CPAN и бесплатно загрузить на свой компьютер.

В то время как для каждой СУБД нужен свой драйвер DBD, модуль DBI существует в единственном варианте и пригоден для работы с любым драйвером DBD.

Если у Вас возникнет необходимость подключить к разработанной ранее программе Perl другую СУБД, то это можно будет сделать простой заменой драйвера DBD и минимальными изменениями исходного текста самой программы.

На рис. 7-1 мы показали взаимодействие между программой CGI, составленной на языке Perl (или любой другой программой Perl, работающей с СУБД), интерфейсным модулем DBI, драйверами баз данных и базами данных.

Рис. 7-1. Доступ к базам данных через DBI

Если в системе установлено несколько драйверов DBD для разных СУБД, то программы Perl смогут обращаться одновременно ко всем этим СУБД, причем для каждой СУБД будет использован свой драйвер DBD. В случае Web-приложений, однако, чаще всего используется какая-либо одна СУБД.

Как Вы увидите в дальнейшем, интерфейс DBI очень прост в использовании. Вместе с тем его возможности вполне достаточны для удовлетворения потребностей практически любого сколь угодно сложного Web-приложения.

Использование DBI

В этом разделе мы на простых примерах расскажем Вам о том, как пользоваться модулем DBI для работы с такими СУБД, как MySQL и Microsoft SQL Server. В следующих главах нашей книги мы приведем примеры и исходные тексты некоторых Web-приложений, активно работающих с DBI.

Программа CGI, написанная на языке Perl и работающая с базами данных через DBI, должна вначале открыть соединение с базой данных, выдать все необходимые команды, получить результат выполнения этих команд, а затем закрыть соединение.

Расскажем об этом подробнее.

Открытие соединения с базой данных

Прежде всего, для того чтобы программа Perl могла обращаться к интерфейсу DBI, в ней необходимо подключить соответствующий модуль:

use DBI qw(:sql_types);

Разумеется, предварительно необходимо установить модуль DBI и нужный Вам драйвер базы данных DBD.

Чтобы открыть соединение с базой данных, необходимо вызвать метод connect, как это показано ниже:

$dsn='dbi:mysql:trudogolik:localhost';
$db_usrname='admin';
$db_pwd='admin_password';

my %attr = (PrintError=>0, RaiseError=>0);

my $dbh  = DBI->connect($Trudogolik::dsn,
  $Trudogolik::db_usrname, $Trudogolik::db_pwd, \%attr);

if(!$dbh)
{
  # Обработка ошибки
}

Рассмотрим процесс открытия соединения подробнее.

Указание драйвера базы данных и источника данных

В качестве первого параметра методу connect нужно передать так называемое имя источника данных Data Source Name (DSN). Источник данных DSN определяет, к какой базе данных будет подключаться программа, и на каком компьютере эта база данных установлена. О том, как создавать DSN для сервера Microsoft SQL Server, мы подробно рассказали в [1].

При работе с DBI имя источника данных представляет собой строку параметров соединения, в которой указан драйвер базы данных, имя базы данных, а также адрес IP узла, на котором работает сервер СУБД. Вся эта информация нужна драйверу для того, чтобы определить, где находится база данных.

Вот как формируется строка имени источника данных $dsn для доступа к базе данных с именем trudogolik (эта база данных будет подробно описана в главе, посвященной виртуальному кадровому агентству Трудоголик.Ру):

$dsn='dbi:mysql:trudogolik:localhost';

Строка имени источника данных должна начинаться с префикса dbi, вслед за которым идут другие поля, разделенные двоеточием.

В нашем примере мы указали драйвер базы данных MySQL с названием DBD::MYSQL. Наша программа CGI работает на том же компьютере, что и сервер базы данных MySQL, поэтому в качестве адреса IP мы использовали строку localhost.

Заметим, что строка параметров соединения определяет, с какой базой данных, и через какой драйвер программа будет обращаться к базе данных. Чтобы обратиться к базе данных MySQL через драйвер ODBC, нужно указать параметры соединения следующим образом:

$dsn='dbi:ODBC:trudogolik_dsn';

Видно, что для доступа к базе данных здесь нужно использовать драйвер DBD::ODBC. Кроме этого, в строке параметров соединения указан источник данных нашей базы данных trudogolik. Что касается СУБД Microsoft SQL Server, то к ней можно обращаться не только через ODBC, но и с помощью механизма ADO, описанного нами в [1]. Для этого нужно использовать драйвер DBD::ADO, доступный для загрузки из библиотеки модулей CPAN.

Для СУБД Oracle имя источника данных задается, например, так:

$dsn='dbi:Oracle:trudogolik_descriptor';

Здесь после обозначения драйвера DBD::Oracle идет дескриптор соединения.

Таким образом, заменой всего лишь одной строки мы можем изменить для программы Perl метод доступа, тип СУБД и базу данных.

Задание идентификатора пользователя и пароля

Для надежной работы Web-приложения необходимо защитить все базы данных при помощи пароля. Ранее мы говорили о том, как задать идентификатор пользователя, пароль и права доступа в СУБД MySQL. Аналогичную защиту можно предусмотреть и в СУБД других типов.

В наших приложениях мы будем указывать идентификатор и пароль доступа к базе данных следующим образом:

$db_usrname='admin';
$db_pwd='admin_password';

Для каждого Web-приложения лучше всего предусмотреть отдельный идентификатор, определив для каждого такого идентификатора минимально необходимые права доступа к базе данных. Например, административное приложение, управляющее работой Web-узла, может иметь больше прав доступа к базе данных, чем приложение, предназначенное для обычных посетителей этого узла.

Выбор способа обработки ошибок

В процессе работу программы CGI с базой данных могут возникать ошибки различных типов. Вначале при отладке приложения, возможно, это будут ошибки в Вашей программе. Но и после завершения отладки необходимо предусмотреть обработку ошибок, которые могут появиться при переполнении диска, при возникновении проблем в данных или сетевых соединений и т.д.

Модуль DBI предлагает различные методы обработки ошибок. В частности, он может полностью взять эту работу на себя, генерируя исключение при возникновении ошибки и отображая сообщение об ошибках на консоли.

Чтобы не смущать посетителей Web-узла сообщениями об ошибках, возникающих в модуле DBI и не содержащих в себе никакой полезной информации для посетителей (кроме той, что Ваше приложение не работает!), лучше выполнять обработку ошибок самостоятельно.

Для отключения внутренней обработки ошибок модуля DBI мы создаем хеш %attr и записываем в него следующие значения:

my %attr = (PrintError=>0, RaiseError=>0);

Ссылка на хеш затем передается методу connect в качестве последнего параметра:

my $dbh  = DBI->connect($Trudogolik::dsn,
  $Trudogolik::db_usrname, $Trudogolik::db_pwd, \%attr);

if(!$dbh)
{
  # Обработка ошибки
}

Если внутренняя обработка ошибок отключена, то при возникновении ошибки метод connect возвратит нулевое значение. Мы обрабатываем эту ситуацию в своей программе как ошибочную.

Подробнее обработка ошибок DBI будет рассмотрена позже в разделе «Обработка ошибок DBI в Web-приложениях» этой главы.

Дескриптор соединения

В том случае, если соединение установлено, программа получит от метода connect дескриптор соединения, который будет использоваться во всех остальных операциях с базой данных. Пользуясь этим дескриптором, программа сможет выдавать команды SQL, а также закрыть ненужное более соединение с базой данных.

Выдача команд

Открыв соединение с базой данных, ваша программа может выдавать команды SQL двух типов. В результате выполнения команд первого типа выборка данных из базы данных не выполняется, а в результате выполнения команд второго типа — выполняется.

Команды первого типа — это такие команды, как вставка и удаление строк, создание или удаление таблиц и т.д. Мы будем называть их простыми командами.

Команды второго типа выбирают данные из таблиц при помощи оператора SELECT. Выдав такую команду, программа должна прочитать результаты ее выполнения.

Простые команды

Чтобы выдать простую команду, не возвращающую данных, используйте метод do:

$sql = 'DROP TABLE test_table';
if(!$dbh->do($sql))
{
  #
Обработка ошибки
}

В качестве единственного параметра методу нужно передать текстовую строку команды SQL. Обратите внимание, что метод do вызывается для открытого ранее соединения с идентификатором, записанным в переменную $dbh.

Если команда выполнилась без ошибок, метод do возвращает ненулевое значение — количество успешно обработанных строк сценария SQL или -1. При ошибке возвращается неопределенное значение undef. Заметим, что такое поведение определяется параметрами обработки ошибок, заданными методу connect при открытии соединения с базой данных.

Предварительная подготовка команд

В предыдущем разделе мы рассмотрели метод do, предназначенный для выполнения простых команд. Более сложные команды перед исполнением нужно подготовить. Подготовка заключается в проведении синтаксического разбора команды SQL. При этом определяется наличие ошибок и возможность выполнения команды.

Подготовка команды выполняется методом prepare:

my $sth = $dbh->prepare($sql);
if(!$sth)
{
  #
Обработка ошибки
}

Команда передается методу в качестве единственного параметра. Метод prepare вызывается для открытого ранее соединения с базой данных $dbh.

Если подготовка команды прошла успешно, метод prepare возвращает дескриптор команды. Пользуясь этим дескриптором, программа Perl может выполнить команду.

Выполнение заранее подготовленных команд

Команды, подготовленные заранее методом prepare, можно выполнить при помощи метода execute:

if(!$sth->execute())
{
  # Обработка ошибки
}

Если команда выполнится успешно, метод execute возвратит значение true. При ошибке будет возвращено неопределенное значение undef. Заметим еще раз, что такое поведение определяется параметрами обработки ошибок, заданными методу connect при открытии соединения с базой данных.

При необходимости подготовленную команду можно выполнять методом execute несколько раз. Некоторые типы СУБД позволяют в этом случае сэкономить время выполнения многократных запросов, так как подготовка команд SQL выполняется один раз.

Привязка параметров

При обращении к базе данных с запросами часто встает задача создания запросов SQL «на лету». Такие запросы SQL называются динамическими.

Существуют два способа создания динамических запросов SQL:

·         программа может динамически сформировать команду SQL в переменной и затем выполнить ее;

·         при создании запросов SQL можно применять параметры, привязывая их к выполняемой команде SQL.

Первый способ при кажущейся простоте обладает одним существенным недостатком. Если запрос SQL формируется с использованием строк, введенных пользователем, то в нем могут оказаться кавычки и различные специальные символы, интерпретируемые СУБД особым образом. При этом программа должна, например, следить за тем, чтобы кавычки использовались только парами, обрабатывать вложенные кавычки и т.д.

Второй способ — привязка параметров (binding), намного удобнее. Он заключается в том, что в строке SQL запроса для отметки позиции параметров используются знаки вопроса:

$sql="select name, city, company from members where name LIKE ?";

Здесь, например, мы выполняем поиск по таблице members с применением команды LIKE. Этой команде передается один параметр — шаблон для поиска. Описание команды LIKE можно найти в любой книге, посвященной SQL.

Вот как вызывается метод bind_param, предназначенный для выполнения привязки:

if(!$sth->bind_param($number, $param, $type))
{
  #
Обработка ошибки
}

В качестве первого параметра методу bind_param нужно передать последовательный номер привязываемого параметра. Для первого параметра этот номер равен 1, для второго — 2 и т.д.

Через второй параметр методу bind_param передается значение привязываемого параметра, а через третий — тип параметра.

Перед использованием метода необходимо подготовить команду методом prepare:

$sql="select name, city, company from members where name LIKE ?";

my $sth = $dbh->prepare($sql);
if(!$sth)
{
  #
Обработка ошибки
}

if(!$sth->bind_param(1, $fio, SQL_VARCHAR))
{
  #
Обработка ошибки
}

if(!$sth->execute())
{
  #
Обработка ошибки
}

В данном случае в качестве значения привязываемого параметра мы указали содержимое переменной $fio. Эта переменная хранит текстовые строки переменой длины, поэтому указан тип данных SQL_VARCHAR. Для числовых параметров тип данных указывается как SQL_INTEGER.

Команда SELECT

В отличие от простых команд, команда SELECT возвращает результат запроса к базе данных в виде таблицы. Программа, выдавшая эту команду, должна не только проверить результат ее завершения, но и прочитать результат запроса.

Существует несколько способов сделать такую выборку, но мы будем использовать в своих программах только один, самый простой и эффективный способ. Он основан на применении метода fetchrow_array. Этот метод возвращает список со всеми полями очередной строки, выбранной из таблицы результата запроса. С помощью простого присвоения программа может сохранить эти поля в соответствующих переменных.

Приведем простой пример. Пусть нам нужно выполнить следующий запрос:

$sql="select add_date, name, phone, email, msg from record";

Здесь нам нужно выбрать несколько полей из таблицы record.

Вначале мы выполняем подготовку команды и выполняем ее:

my $sth = $dbh->prepare($sql);
if(!$sth)
{
  # Обработка ошибки
}
if(!$sth->execute())
{
  # Обработка ошибки
}

Далее необходимо обеспечить выборку результата запроса, вызывая в цикле метод fetchrow_array:

while(
 (my $m_add_date, my $m_name, my $m_phone, my $m_email,
  my $m_msg) = $sth->fetchrow_array())
{
  my %row = (ADD_DATE => $m_add_date, NAME => $m_name, PHONE => $m_phone, EMAIL => $m_email, MSG => $m_msg);

  . . .
}

Здесь мы сохраняем содержимое полей извлеченной строки в хеш %row, но Вы, разумеется, можете использовать их как угодно.

Обработка ошибок DBI в Web-приложениях

Создавая профессиональное Web-приложение, Вы должны тщательно продумать технологию обработки ошибок, возникающих при обращении к СУБД. Даже если в приложении возникала ошибка, пользователю нужно выдать такое сообщение, которое будет ему понятно. От «сырых» диагностических сообщений, отображаемых по умолчанию, посетитель может прийти в замешательство.

Режимы обработки ошибок

Модуль DBI позволяет задавать два режима обработки ошибок — с применением механизма исключений или с анализом кода возврата методов DBI. Также допустимо использование обоих режимов одновременно.

Режим обработки ошибок DBI задается при открытии соединения с базой данных при помощи последнего аргумента метода connect:

my %attr = (PrintError=>0, RaiseError=>0);

my $dbh  = DBI->connect($Trudogolik::dsn,
  $Trudogolik::db_usrname, $Trudogolik::db_pwd, \%attr);

Как видите, здесь мы передаем методу connect ссылку на хеш %attr, содержащий элементы с ключами PrintError и RaiseError.

Если значение элемента с ключом PrintError равно 1, при возникновении ошибки модуль DBI выводит сообщение об ошибке в стандартный поток вывода при помощи функции warn, вследствие чего оно появится в окне браузера посетителя.

Установив значение элемента с ключом RaiseError, равным 1, можно указать DBI на необходимость аварийного завершения работы программы при возникновении ошибки (выполняемую вызовом функции die).

В наших примерах Web-приложений мы будем пользоваться первым режимом обработки ошибок, поэтому при открытии соединения с базой данных элементам с ключами PrintError и RaiseError мы присваиваем нулевое значение.

Определение причины ошибки

Для того чтобы определить причину ошибки, нужно использовать специальные диагностические методы err, errstr и state. Все эти методы вызываются для дескриптора установленного соединения, например:

my $err_msg = $dbh-> errstr();

Метод err возвращает номер ошибки, который зависит от СУБД и не всегда информативен. Метод state возвращает строку из 5 символов в формате команды SQLSTATE и, к сожалению, работает не для всех драйверов СУБД.

Что же касается метода errstr, то с его помощью можно получить текстовое описание ошибки, из которого обычно бывает нетрудно понять причину ее возникновения.

Помимо перечисленных выше методов в модуле DBI предусмотрено три поля класса DBI, содержимое которых определяется последним использованным дескриптором:

my $err_msg = $DBI::errstr;
my $err = $DBI::err;
my $err_state = $DBI::state;

Если анализ ошибки выполняется сразу после ее возникновения, то Вы можете использовать перечисленные выше поля. В наших Web-приложениях обработка выполняется именно так — при формировании сообщения об ошибке мы обращаемся к полю $DBI::errstr.

Закрытие соединения

Завершив работу с базой данных, для освобождения ресурсов необходимо закрыть установленное ранее соединение. Если этого не сделать, при длительной работе Вашего Web-приложения какие-нибудь ресурсы СУБД или операционной системы могут оказаться исчерпанными. В результате произойдет нарушение нормальной работы или «зависание» Web-сервера.

Закрытие соединения выполняется при помощи метода disconnect следующим образом:

$dbh->disconnect();
if(!$dbh)
{
  #
Обработка ошибки
}

При составлении программ для Web-приложений необходимо внимательно следить за использованием ресурсов, освобождая их после использования. В противном случае в работе приложения могут возникать трудно локализуемые ошибки.

Пакет Trudogolik

Чтобы облегчить работу с базами данных, мы создали для наших Web-приложений пакет Perl с названием Trudogolik. Этот пакет содержит все функции и определения, нужные для работы с модулем DBI, а также некоторые другие функции, о которых мы будем рассказывать по мере изложения материала.

Структура пакета

Как Вы, наверное, знаете, пакеты Perl позволяют разбивать программу на автономные фрагменты и сильно облегчают процесс программирования. Если Вы не знакомы с пакетами Perl, перед тем как продолжить работу с нашей книгой, прочитайте главу о создании пакетов и модулей в каком-либо руководстве по Perl.

Заголовок нашего пакета Trudogolik, записанного в файле с именем Trudogolik.pl (что важно!) выглядит следующим образом:

package Trudogolik;
use CGI qw(:all escape);
use Digest::SHA1 qw(sha1_hex sha1_base64);
use DBI qw(:sql_types);
use Net::SMTP;
use HTML::Entities ();

В первой строке заголовка мы указываем имя пакета Trudogolik с помощью оператора package. Далее в программах мы будем использовать это имя при обращении к переменным и функциям, определенным в нашем пакете.

Следующие пять строк необходимы для подключения различных модулей, вызываемых из пакета Trudogolik. Это модуль CGI (необходимый для создания программ CGI), модуль Digest::SHA1 (для вычисления хеш-функций при работе с паролями), модуль DBI (для работы с базами данных), модуль Net::SMTP (для отправки электронной почты через сервер SMTP), а также модуль HTML::Entities (для кодирования текстовых строк, размещаемых в страницах HTML).

После заголовка в файле пакета располагается область определения глобальных переменных и определения функций.

Файл нашего пакета завершают следующие строки:

return 1;
END {}

Глобальные переменные

Мы использовали пакет Trudogolik с небольшими изменениями в разных проектах. Фактически меняется источник данных DBI, парольная информация и путь к каталогу шаблонов страниц HTML.

Вот как выглядит определение глобальных переменных в версии пакета Trudogolik одного из наших проектов:

# ---------------------------------
#
Глобальные переменные
# ---------------------------------
$dsn='dbi:ODBC:angio';
$db_usrname='admin_name';
$db_pwd='admin_password';
$cookie_password='Hard45678Password';
$template_path_ru='template/ru/';

Здесь в переменную $dsn мы записали имя источника данных angio. Доступ к этому источнику осуществляется через драйвер ODBC операционной системы Microsoft Windows. Переменные $db_usrname и $db_pwd хранят, соответственно, идентификатор и пароль для доступа к базе данных.

В другом проекте, созданном нами для работы в среде Linux и Microsoft Windows, эта переменная инициализируется следующим образом:

$dsn='dbi:mysql:trudogolik:localhost';

Здесь источник данных ссылается на базу данных trudogolik, работающую под управлением СУБД MySQL на том же самом компьютере, что и Web-сервер (указан адрес localhost).

Таким образом, изменением содержимого только одной переменной можно поменять источник данных для всех функций, определенных в нашем пакете.

В глобальной переменной $cookie_password мы храним текстовую строку, применяемую при вычислении хеш-функции пароля, предназначенного для записи в Cookie посетителя:

$cookie_password='Hard45678Password';

Использование этой переменной будет описано в главе 8 «Виртуальное кадровое агентство Трудоголик.Ру», посвященной Web-узлу виртуального кадрового агентства Трудоголик.Ру.

Функции для работы с DBI

В этом разделе мы расскажем о функциях пакета Trudogolik, применяемых для работы с базами данных посредством интерфейса DBI. Эти функции обеспечивают обработку ошибок и не зависят от типа используемой СУБД и метода доступа.

DB_OPEN

Функция DB_OPEN открывает соединение с базой данных, вызывая для этого метод connect:

sub DB_OPEN()
{
  my %attr = (PrintError=>0, RaiseError=>0);
  my $dbh = DBI->connect($Trudogolik::dsn,
    $Trudogolik::db_usrname, $Trudogolik::db_pwd, \%attr);
  if (!$dbh)
  {
    DB_ERROR(__FILE__,__LINE__,$DBI::errstr); 
  }
  return $dbh;
}

Обратите внимание на то, что мы отключаем механизм исключений при возникновении ошибок, задавая нулевые значения для элементов хеша %attr с именами PrintError и RaiseError. При этом обработка ошибок, возникающих при вызове всех функций, выполняется после анализа кода возврата функций.

В качестве первых трех параметров мы передаем методу connect глобальные переменные $Trudogolik::dsn, $Trudogolik::db_usrname и  $Trudogolik::db_pwd. Имена переменных снабжены префиксом Trudogolik, так как это глобальные переменные, определенные в пакете Trudogolik.

Через последний параметр методу connect передается ссылка на локальный хеш %attr, определяющий способ обработки ошибок.

В случае успеха функция DB_OPEN возвращает дескриптор установленного соединения, который должен использоваться во всех последующих операциях с базой данных.

Если же при подключении к базе данных возникла ошибка, метод connect возвращает значение undef. В этом случае мы вызываем функцию DB_ERROR, ответственную за обработку ошибок и определенную в нашем пакете Trudogolik.

Через первые два параметра функции DB_ERROR передается имя файла программы Perl и номер строки, в которой произошла ошибка. Эта информация поможет локализовать ошибку. Что же касается сообщения об ошибке, оно передается функции DB_ERROR через третий параметр и берется из переменной $DBI::errstr. Эта переменная была описана выше в разделе «Определение причины ошибки».

Подробное описание функции DB_ERROR приведено ниже в этой главе.

DB_CLOSE

Функция DB_CLOSE предназначена для закрытия соединения с базой данных:

sub DB_CLOSE($)
{
  my $dbh = shift @_;
  $dbh->disconnect();
  if (!$dbh)
  {
    DB_ERROR(__FILE__,__LINE__,$DBI::errstr); 
  }
}

Эта операция выполняется с помощью метода disconnect, вызываемого для открытого ранее соединения. Идентификатор соединения передается функции в качестве параметра и сохраняется в локальной переменной $dbh.

Если при закрытии соединения возникла ошибка, она обрабатывается упомянутой выше функцией DB_ERROR.

DB_SQL_DO

Функция DB_SQL_DO позволяет выполнить команды SQL без предварительной подготовки:

sub DB_SQL_DO($$)
{
  (my $dbh, my $sql) = @_;
  if(!$dbh->do($sql))
  {
    DB_ERROR(__FILE__,__LINE__,$DBI::errstr); 
  }
}

В качестве первого параметра этой функции передается идентификатор соединения с базой данных, а в качестве второго — строка команды SQL.

При возникновении ошибки вызывается функция DB_ERROR.

DB_SQL_PREPARE

Функция DB_SQL_PREPARE выполняет предварительную подготовку команд SQL, вызывая для этого метод prepare:

sub DB_SQL_PREPARE($$)
{
   (my $dbh, my $sql) = @_;
   my $sth = $dbh->prepare($sql);
   if(!$sth)
   {
     DB_ERROR(__FILE__,__LINE__,$DBI::errstr);
   }
   else
   {
     return $sth;
   }
}

В качестве первого параметра функции DB_SQL_PREPARE передается идентификатор соединения с базой данных, а в качестве второго — строка команды SQL, подлежащая подготовке.

Если подготовка команды прошла успешно, функция DB_SQL_PREPARE возвращает дескриптор команды, который затем можно будет использовать для выполнения этой команды. При ошибке вызывается функция DB_ERROR, инициирующая остановку выполнения программы.

DB_SQL_BIND

Функции DB_SQL_BIND, выполняющей привязку параметров команды SQL, передаются четыре параметра:

sub DB_SQL_BIND($$$$)
{
  (my $sth, my $number, my $param, my $type) = @_;
  if(!$sth->bind_param($number, $param, $type))
  {
    DB_ERROR(__FILE__,__LINE__,$DBI::errstr); 
  }
}

Через первый параметр передается идентификатор команды, подготовленной предварительно при помощи только что описанной функции DB_SQL_PREPARE. Второй параметр указывает номер привязываемого параметра, через третий передается сам параметр, а через четвертый — тип параметра.

При возникновении ошибки привязки вызывается функция DB_ERROR.

DB_SQL_EXECUTE

Чтобы выполнить предварительно подготовленную команду, используйте функцию DB_SQL_EXECUTE:

sub DB_SQL_EXECUTE($)
{
  my $sth = shift @_;
  if(!$sth->execute())
  {
    DB_ERROR(__FILE__,__LINE__,$DBI::errstr); 
  }
}

В качестве параметра этой функции нужно передать дескриптор команды, полученный от функции DB_SQL_PREPARE.

Обработка ошибок, как обычно, выполняется функцией DB_ERROR.

DB_SQL_FETCHROW_ARRAY

Функция DB_SQL_FETCHROW_ARRAY поможет извлечь результаты выполнения команды SELECT:

sub DB_SQL_FETCHROW_ARRAY($)
{
  my $sth = shift @_;
  return $sth->fetchrow_array();
}

Эта функция, вызывающая метод fetchrow_array, получает в качестве входного параметра дескриптор команды, а возвращает массив значений столбцов текущей строки извлеченных при выполнении команды SELECT. Если в результате выполнения команды не было извлечено ни одной строки, функция DB_SQL_FETCHROW_ARRAY возвратит пустую строку.

DB_ERROR

В задачу функции DB_ERROR входит обработка ошибочных ситуаций, возникающих при обращении к базе данных средствами интерфейса DBI:

sub DB_ERROR($$$)
{
  ($file, $line, $dbi_err) = @_;
  my $err_msg= HTML::Entities::encode_entities($dbi_err);

  $template = HTML::Template->new(
    filename => $template_path_ru.'error.htm');

  $template->param(ERR_FILE => $file, ERR_LINE => $line,
    ERR_MSG => $err_msg);

  print header (-charset=>'windows-1251'); 
  print $template->output;
  die;
}

Функция получает три параметра и сохраняет их в переменных $file, $line и $dbi_err. Через первый параметр функции передается имя файла программы Perl, в которой возникла ошибка, через второй — номер строки этой программы, вызвавшей ошибку, а через третий — текстовое сообщение об ошибке.

Так как сообщение об ошибке может содержать кавычки, угловые скобки и другие символы, имеющие в языке HTML особое значение, перед записью в документ HTML их необходимо преобразовать в символьные объекты. Например, кавычки нужно преобразовать в символьный объект ", открывающую угловую скобку — в объект <, а закрывающую — в объект >.

Вместо того чтобы выполнять такое преобразование в нашей программе самостоятельно, мы использовали метод encode_entities из модуля Perl с названием HTML::Entities. Этот метод выполняет необходимое нам преобразование текстовых строк, заменяя особые символы  символьными объектами.

После преобразования наша программа загружает шаблон страницы с сообщением об ошибке. Путь к каталогу с шаблонами записан в глобальной переменной $template_path_ru. Наша функция DB_ERROR добавляет к нему имя файла error.htm с шаблоном.

Далее программа инициализирует переменные шаблона ERR_FILE, ERR_LINE и ERR_MSG, записывая в них, соответственно, имя файла, в котором произошла ошибка, номер строки в файле, а сообщение об ошибке, обработанное предварительно методом encode_entities.

Перед завершением своей работы функция DB_ERROR выводит заголовок HTTP и шаблон в стандартный поток вывода, формируя страницу с сообщением об ошибке. Далее работа программы CGI прерывается оператором die, так как ее дальнейшее выполнение невозможно.

Пример вызова функций

В следующих главах нашей книги мы неоднократно будем обращаться к различным функциям, определенным в пакете Trudogolik. Ниже мы приведем простой пример использования этих функций в программе CGI, обновляющей содержимое таблицы участников симпозиума в базе данных из файла.

Подробнее этот пример будет описан в главе 9 «Web-узел симпозиума»[AF1] , а сейчас мы заметим только, что при создании сайта было нужно разработать программу для импортирования списка участников симпозиума из текстового файла в базу данных. Каждая строка исходного текстового файла соответствовала одной записи таблицы, причем поля строки были отделены друг от друга символом точка с запятой.

Итак, рассмотрим исходный текст программы, опустив несущественные на данном этапе детали.

Ниже мы привели начальный фрагмент программы, содержащий ссылки на внешние модули, а также пакет Trudogolik:

#!/usr/bin/perl -w
use CGI qw(:all);
use HTML::Template;
use Text::ParseWords;
use DBI qw(:sql_types);
use strict;
require 'trudogolik.pl';

Далее следуют определения локальных переменных, используемых в программе:

my (@words, $name, $city, $company, $dbh, $sql, $sth);

Удаление всех записей из таблицы

На первом этапе нам нужно удалить все записи из таблицы members, содержащей список участников симпозиума. Для этого мы вначале открываем базу данных, а затем выдаем команду DELETE FROM members:

$dbh = Trudogolik::DB_OPEN();
Trudogolik::DB_SQL_DO($dbh, 'DELETE FROM members');

Вся информация, необходимая для открытия соединения с базой данных (в том числе идентификатор и пароль доступа), определена в глобальных переменных пакета Trudogolik, поэтому функция Trudogolik::DB_OPEN «знает», что именно и как нужно открывать.

Функция Trudogolik::DB_SQL_DO выдает команду, удаляющую из таблицы members все записи.

Заметим, что на данном этапе мы не будем закрывать соединение с базой данных, так как нам нужно еще загрузить в таблицу members новый список участников.

Обработка исходного файла списка участников

Теперь программа должна открыть и обработать исходный текстовый файл списка участников. Вот как открывается текстовый файл списка:

open (MEMBERS_FILE, "/V-Hosting/admin.angio/upload/members.txt") || die;

Если файл участников открыть не удалось, программа завершает свою работу при помощи команды die. Отсутствие обработчика данной ошибки оправдано тем, что с программой обновления списка имеет дело администратор, который в случае чего найдет причину ошибки и устранит его. Однако в своем приложении, особенно если оно предназначено для пользователей, имеет смысл при возникновении ошибки вывести какое-либо осмысленное сообщение о причинах ошибки.

При успешном открытии файла его записи считываются и обрабатываются в цикле:

while(<MEMBERS_FILE>)
{
  . . .
}
close (MEMBERS_FILE);

После завершения обработки входной файл MEMBERS_FILE закрывается командой close.

Цикл обработки записей

Внутри цикла мы выполняем разбор очередной текстовой строки, прочитанной из файла, выделяя в ней поля, а затем формируем из выделенных полей строку и добавляем ее в таблицу members нашей базы данных.

Поля выделяются следующим образом:

@words = &quotewords(';', 0, $_);
$name = $words[0].' '.$words[1].' '.$words[2];  chomp $name;
$city = $words[3]; chomp $city;
$company = $words[4]; chomp $company;

Разбор строки выполняет функция quotewords. В качестве первого параметра мы передаем этой функции символ-разделитель, а в качестве последнего — очередную строку, прочитанную из файла в переменную $_.

Первые три поля — это имя, фамилия и отчество. Мы формируем из них в переменной $name одну строку, обрезая служебные символы (такие как перевод строки) функцией chomp. Аналогичным образом из входной строки выделяется название города, откуда приехал участник конференции ($city), и название компании, где он работает ($company).

Если при регистрации участник не указал название города или компании, то программа запишет в соответствующие поля таблицы прочерк:

if($city eq '')
{
  $city = '-';
}
if($company eq '')
{
  $company = '-';
}

Таким образом, нам нужно добавить в таблицу members три поля — имя участника, название города и название компании. Это делается при помощи следующей команды SQL:

$sql="INSERT members (name, city, company) VALUES (?, ?, ?)";

Чтобы выполнить эту команду, программа должна сначала подготовить ее функцией Trudogolik::DB_SQL_PREPARE, а затем привязать к ней три параметра, вызвав три раза функцию Trudogolik::DB_SQL_BIND:

$sth = Trudogolik::DB_SQL_PREPARE($dbh, $sql);
Trudogolik::DB_SQL_BIND($sth, 1,
  Trudogolik::ASCII_TO_HTML($name), SQL_VARCHAR);
Trudogolik::DB_SQL_BIND($sth, 2,
  Trudogolik::ASCII_TO_HTML($city), SQL_VARCHAR);
Trudogolik::DB_SQL_BIND($sth, 3,
  Trudogolik::ASCII_TO_HTML($company), SQL_VARCHAR);

Теперь команду можно выполнять, вызвав для этого функцию с именем Trudogolik::DB_SQL_EXECUTE:

Trudogolik::DB_SQL_EXECUTE($sth);

Далее программа читает из входного текстового файла следующую строку, разбирает ее содержимое и записывает в таблицу members, как это только что было описано.

Вывод содержимого таблицы для контроля

Когда все стоки исходного файла будут прочитаны, а список участников — обновлен в базе данных, наша программа прочитает содержимое таблицы members и отобразит его в окне браузера для контроля. Администратор, запустив программу обновления списка в базе данных, сможет сразу увидеть полученный результат.

Чтобы прочитать весь список участников из базы данных, программа выдает команду SQL следующего вида:

$sql="select id, name, city, company from members order by name";
$sth = Trudogolik::DB_SQL_PREPARE($dbh, $sql);
Trudogolik::DB_SQL_EXECUTE($sth);

Здесь команда вначале подготавливается функцией Trudogolik::DB_SQL_PREPARE, а затем выполняется функцией Trudogolik::DB_SQL_EXECUTE.

Далее наша программа получает в цикле результат выполнения запроса:

my $template = HTML::Template->new(filename => 'template/ru/upload_ok.htm');

my $i=1;
@::loop = ();
while((my $m_id, my $m_name, my $m_city, my $m_company)= Trudogolik::DB_SQL_FETCHROW_ARRAY($sth))
{
  my %row = (ID => $i, NAME => $m_name, CITY => $m_city,
    COMPANY => $m_company);
  push(@::loop, \%row); $i++;
}
$template->param(MEMBER_LIST => \@::loop);

При этом каждая строка полученного результата извлекается при помощи функции с именем Trudogolik::DB_SQL_FETCHROW_ARRAY, причем поля строки сохраняются в переменных с именами $m_id, $m_name, $m_city и $m_company. Содержимое этих переменных используется для заполнения шаблона $template, предназначенного для оформления результатов запроса в виде таблицы.

Перед завершением своей работы программа закрывает ненужное больше соединение с базой данных, вызывая для этого функцию Trudogolik::DB_CLOSE:

Trudogolik::DB_CLOSE($dbh);

Затем она выводит результат своей работы с использованием заполненного шаблона:

#print header (-charset=>'windows-1251'); 
print "Content-Type: text/html\n";
print "Pragma: no-cache\n";
print "Cache-Control: no-cache\n";
print "Expires: Thu Jan  1 01:01:01 1970\n";
print "Charset: windows-1251\n\n";
print $template->output;

Обработка ошибок

Обратите внимание, что наша программа непосредственно не вызывает функцию обработки ошибок при обращении к базе данных Trudogolik::DB_ERROR. Однако в этом нет никакой необходимости — использованные нами функции пакета Trudogolik обрабатывают ошибки самостоятельно, вызывая для этого функцию Trudogolik::DB_ERROR. Такой подход сильно сокращает размер исходного текста программы CGI и упрощает отладку.

Использование модуля Win32::ODBC

Рассмотренный ранее в этой главе модуль DBI предоставляет очень удобный интерфейс для доступа к базам данных различного типа, работающих на разных платформах. На наш взгляд, создавая программы CGI на языке Perl, работающие с базами данных, лучше всего применять именно этот модуль.

Тем не менее, для полноты изложения материала нельзя не упомянуть про модуль Win32::ODBC. Он применяется для работы с базами данных в среде операционной системы Microsoft Windows через стандартный интерфейс ODBC. Если Вы разрабатываете Web-приложение, предназначенное исключительно для Microsoft Windows, то модуль Win32::ODBC может оказаться полезным.

Данные модуль подключается к программе следующим образом:

use Win32::ODBC;

Далее программа может открывать соединения с базами данных, выдавать команды SQL и извлекать результаты их выполнения, обрабатывать ошибки и т.д.

Открытие соединения с базой данных

Прежде чем обращаться к базе данных через функции модуля Win32::ODBC, необходимо открыть соединения с базой данных. Это делается при помощи конструктора класса:

my $dsn="forum";
if (!($db=new Win32::ODBC($dsn)))
{
  print "
Ошибка: ".Win32::ODBC::Error();
}

Здесь через переменную $dsn мы передаем конструктору имя источника данных ODBC. О том, как создать источник данных ODBC в Microsoft Windows, мы рассказали в [1].

Если для доступа к базе данных необходим идентификатор и пароль, эта информация указывается конструктору класса Win32::ODBC следующим образом:

$db = new Win32::ODBC("dsn=forum; uid=admin; pwd=admin_password");

В том случае, когда при открытии соединения произошла ошибка, Вы можете использовать функцию Win32::ODBC::Error чтобы получить ее текстовое описание.

Выдача команды SQL

После открытия соединения можно выдавать команды SQL. Для этого предназначен метод Win32::Sql:

$Sql = "SELECT * FROM members";
if ($db->Sql($Sql))
{
  print "
Ошибка: ".$db->Error();
}

При возникновении ошибки можно вызвать функцию Win32::ODBC::Error для получения текста диагностического сообщения.

Получение результата выполнения команды SELECT

После выдачи команды SELECT нужно получить результат ее выполнения. Это можно сделать, например, с помощью метода Win32::ODBC::FetchRow, вызывая его в цикле:

while($db->FetchRow())
{
  %Data = $db->DataHash();
  if ($Data{"Name"} eq $value)
  {
    print "
Имя: ".$value.$Data{"Name"};
  }
}

Каждый раз после вызова метод Win32::ODBC::FetchRow возвращает хеш, элементы которого соответствуют полям выбранной строки. К ним можно обращаться по именам, используя их в качестве ключей хеша.

Закрытие соединения с базой данных

После завершения работы с базой данных необходимо закрыть соединение, вызвав для этого метод Win32::ODBC:: Close, как это показано ниже:

$db->Close();

При этом будут освобождены ресурсы, использованные операционной системой и СУБД для работы с базой данных.

[Назад] [Содержание] [Дальше]