Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil - А Ковязин
Шрифт:
Интервал:
Закладка:
Прежде чем перейти к рассмотрению реальных примеров, необходимо ввести следующие понятия:
Параметры запроса - это переменные, которые передаются серверу (входящие) или возвращаются в качестве результата (исходящие). В примерах эти переменные носят соответственно названия isqlda и osqlda.
Большинство IB API-функций возвращают так называемый STATUS_VECTOR - массив целых чисел (long). Анализируя этот массив, можно узнать, произошел ли вызов функции успешно, и если нет-то по какой причине.
В качестве параметров используется особая структура, XSQLDA - extended SQL Descriptor Area. Она позволяет знать (или передать) всю необходимую информацию о каждом конкретном параметре, а также сами данные в структуре SQLVAR - SQL Variable. В силу изложенного выше при написании CGI- приложений интерес представляют лишь несколько полей из этих структур:
Структура XSQLDA, описывающая параметры запросов
Поле
Тип (для языка С)
Описание
version
short
Версия XSQLDA-структуры; должна быть установлена в соответствии с версией используемой клиентской библиотеки. Для текущих версий InterBase это значение соответствует SQDA_VERSION1
sqln
short
Количество элементов массива sqlvar (иными словами, количество передаваемых и/или получаемых переменных). Это поле должно быть обязательно заполнено для входящих и исходящих параметров до передачи данных запроса серверу
sqld
short
Указывает количество входящих или исходящих параметров. Обычно это поле для исходящих параметров инициализируется библиотекой InterBase. 0 в этом поле означает, что запрос не является выборкой (non-SELECT query)
Структура SQLVAR, описывающая панные параметров структуры XSQLDA
Поле
Тип (для языка С)
Описание
sqldata
char*
Указатель на данные, которые необходимо передать серверу
sqltype
short
Информация о типе передаваемых данных. Для полей, допускающих SQL-значение NULL- используется тип, на единицу больший, чем базовый. Например, для типа SQL INTEGER используется значение SQL_LONG, если NULL не допускается, и тип SQLJ.ONG+1, если в значении поля допускается NULL
sqllen
short
Размер памяти, занимаемой переменной, на которую указывает sqldata
sqlind
short *
Это поле - индикатор того, является ли значение передаваемой переменной NULL (в этом случае это поле должно быть равно единице) или нет (в этом случае значение этого поля 0). Заполнение этого поля является обязательным, если переменная может принимать значение NULL. Таким образом, если вы хотите передать значение NULL серверу и в sqltype указан не базовый тип (например, SQLJ.ONG+1), то инициализировать это поле обязательно; если же указан, например тип SQL_LONG, то это поле можно не инициализировать - сервер проигнорирует его значение. При чтении данных из результата это поле указывает, является ли полученное значение NULL или отличается от него. Анализировать необходимо именно это поле - результат сравнения переменной sqldata с пустым значением может быть абсолютно непредсказуемым
Подробнее все поля этих структур описаны в [7] и представляют интерес > только при написании действительно сложных приложений или библиотек.
Теперь, когда даны необходимые определения, можно непосредственно по* I знакомиться с методами DSQL-программирования. Их всего 4: I
Нет переменных ни на входе ни на выходе. В этом случае обе структуры * SQLDA (isqlda, osqlda) должны быть инициализированы NULL (или любым пустым указателем в используемом вами языке - например, nil в Паскале) в вызове функций isc_dsql_execute, isc_dsql_execute2 и т. д. Обычно это запросы, которые удаляют записи с жестко заданными свойствами.
* Есть переменные как на входе, так и на выходе; в этом случае нужно инициализировать соответствующие структуры.
* Нет переменных на выходе, но есть на входе; в этом случае osqlda = NULL, a isqlda требуется инициализировать.
* Нет переменных на входе, но есть на выходе; в этом случае isqlda = NULL, a osqlda требуется инициализировать.
Теперь примеры. Для простоты будем работать только с одной таблицей, структура которой описана ниже.
Пример 1. Запрос без параметров
Предположим, у нас есть таблица следующей структуры:
CREATE TABLE BOOKS (
B_ID INTEGER NOT NULL,
B_INDEX CHAR(16) NOT NULL,
B_NAME VARCHAR(80) NOT NULL,
B_AUTHOR VARCHAR(SO) NOT NULL,
B_ADDED TIMESTAMP DEFAULT 'now' NOT NULL,
B_THEME VARCHAR(60) NOT NULL);
Для того чтобы отобразить результат запроса "select b_id. b_index, b_name. b_authoi, b_added, b_theme from books" в виде следующей таблицы (см. Рис. 3.1):
Рис 3.1. Результат выполнения SQL-запроса, представленный в браузере в виде HTML- таблицы
понадобится следующий скрипт: (examplel.с)
#include <ibase.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// Эта структура предназначена для хранения переменных типа SQL_VARYING
#define SQL_VARCHAR(len) struct {short vary_length; char
vary_string[(len)+1];}
int main (void){
/ / Константы, необходимые для работы с базой данных - инициализируйте их в
// соответствии с реальным путем к базе, пользователем и паролем
char *dbname = "localhost:/var/db/demo.gdb";
char *uname = "sysdba";
char *upass = "masterkey";
char *query = "select b_id, b_index, b_name, b_author, b_added,
b_theme from books";
/ / Переменные для работы с базой данных
isc_db_handle db_handle = NULL;
isc_tr_handle transaction_handle = NULL;
isc_stmt_handle statement_handle=NULL;
char dpb_buffer[256], *dpb, *p;
short dpb_length;
ISC_STATUS status_vector[20] ;
XSQLDA *isqlda, *osqlda;
long fetch_code;
short
o_ind[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0, 0,0);
/ / Остальные переменные
int i = 0;
long b_id;
char b_index[17];
SQL_VARC HAR(100) b_name;
SQL_VARCHAR(100) b_author;
SQL_VARCHAR(100) b_theme;
ISC_TIMESTAMP b_added;
struct tm added_time;
char decodedTime[100];
Здесь наше приложение начинает вывод стандартного HTML-документа:
printf("Content-type:
text/plainnn<html><body><center><b>Example Nr l</b><br>SELECT
without input parameters</centerxbr>") ;
а затем подключается к базе данных - здесь два этапа
// создаем так называемый database parameter buffer, необходимый
/ / для подключения к базе данных
dpb=dpb_buffer;
*dpb++ = isc_dpb_versionl;
*dpb++ = isc_dpb_user_name;
*dpb++ = strlen(uname);
for(p = uname; *p; ) *dpb++ = *p++;
*dpb++ = isc_dpb_password;
*dpb++ = strlen(upass);
for (p=upass; *p;) *dpb++ = *p++;
dpb_length = dpb dpb_buffer;
// Подключаемся к базе
isc_attach_database(
status_vector,
strlen(dbname),
dbname,
&db_handle,
dpb_length,
dpb_buffer) ;
Далее идет стандартная для большинства API-функций проверка и анализ результата:
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector) ;
return(1);
}
Если подключение к базе данных произошло удачно, начинается транзакция:
if (db_handle){
isc_start_transaction(
status_vector,
&transaction_handle,
1,
&db_handle,
0,
NULL);
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector);
return(1);
}
}
Далее инициализируются структуры, которые будут заполняться результатами запроса:
osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(6));
osqlda -> version = SQLDA_VERSION1;
osqlda -> sqln = 6;
osqlda->sqlvar[0].sqldata = (char *)&b_id;
osqlda->sqlvar[0].sqltype = SQL_LONG;
osqlaa->sqlvar[0].sqlind = &o_ind[0];
osqlda->sqlvar[1].sqldata = (char *)&b_index;
osqlda->sqlvar[1].sqltype = SQL_TEXT;
osqlda->sqlvar[1].sqlind = &o_ind[l];
osqlaa->sqlvar[2].sqldata = (char *)&b_name;
osqlda->sqlvar[2].sqltype = SQL_VARYING;
osqlda->sqlvar[2J .sqlind = ko_ind[2];
osqlda->sqlvar[3].sqldata = (char *)&b_author;
osqlda->sqlvar[3].sqltype = SQL_VARYING;
osqlda->sqlvar[3].sqlind = &o_ind[3];
osqlda->sqlvar[4].sqldata = (char *)&b_added;
osqlda->sqlvar[4].sqltype = SQL_TIMESTAMP;
osqlda->sqlvar[4].sqlind = &o_ind[4];
osqlda->sqlvar[5].sqldata = (char *)&b_theme;
osqlda->sqlvar[5].sqltype = SQL_VARYING;
osqlda->sqlvar[5].sqlind = &o_ind[5];
А вот здесь, собственно, и начинается подготовка к исполнению запроса сервером:
isc_dsql_allocate_statement(
status_vector,
&db_handle,
&statement_handle);
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector);
return(1);
}
isc_dsql_prepare(
status_vector,
&transaction_handle,
&scatement_handle,
0,
query,
SQL_DIALECT_V6,
osqlda);
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector);
return(1);
}
isc_dsql_execute2(
status_vector,
&transaction_handle,
&statement_handle,
1,
NULL,
NULL);
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector) ;
return(1);
}
Здесь начинается таблица HTML-документа. Ситуация, когда в базе данных может не оказаться данных, подробно анализируется во втором примере.
printf("<center><table border=0 bgcolor=black cellpadding=l
cellspacing=l><tr align=center bgcolor=#999999> <td>Book
ID</tr> <td>CODE</tr> <td>TITLE</tr> <td>AUTHOR</tr>
<td>ADDED</tr> <td>THEME</tr> </tr>");
После исполнения запроса сервер готов к передаче данных. "Доставкой" данных !лнимаегся функция isc_dsql_tetch()'
while((fetch_code = isc_dsql_fetch(
status_vector,
&statement_handle,
1,
osqlda))= = 0) {
Для строковых переменных требуется корректно установить длину, так как размер возвращаемых данных не всегда соответствуем максимально возможному, и если этого не сделать, то вместе с реальными данными можно получить' "мусор" из памяти или остатки предыдущих строк: