Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil - А Ковязин
Шрифт:
Интервал:
Закладка:
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) {
Для строковых переменных требуется корректно установить длину, так как размер возвращаемых данных не всегда соответствуем максимально возможному, и если этого не сделать, то вместе с реальными данными можно получить' "мусор" из памяти или остатки предыдущих строк:
b_index[osqlda->sqlvar[1].sgllen]= ' ' ;
b_name.vary_string[b_name.vary_length] = ' ' ;
b_author.vary_string[b_author.vary_length]=' ';
b_theme.vary_string[b_theme.vary_length]=' ';
Структуру типа TIMESTAMP, как и структуры DATE/TIME, перед выводом в документ можно преобразовать в строковый тип в нужном формате. Для этого сначала она декодируется в структуру tm, а затем в строку:
isc_decode_timestamp(&b_added,&added_time);
strftime(decodedTime,sizeof(decodedTime),"%d-%b-%Y
%H:%M",&added_t ime);
printf("<tr bgcolor=white><td>%i</td> <td>%s</td> <td>%s</td>
<td>%s</td> <td>%s</td> <td>%s</td> </tr>",
b_id,
b_index,
b_name vary_strxng,
b_author.vary_string,
decodedTime,
b_theme.vary_string);
}
После вывода всех данных необходимо завершить документ:
printf ("</table></center></body></html>") ;
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector);
return(1);
}
free(osqlda);
isc_dsql_free_statement(
status_vector,
&statement_handle,
DSQL_drop);
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector);
recurn(l);
}
Затем завершить транзакцию и отключиться от базы данных:
if (transaction_handle){isc_commit_transaction(status_vector,
&transaction_handle);}
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector);
return(1),
}
if (db_handle) isc_detach_database(status_vector, &db_handle);
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector) ;
return(1);
}
return(0);
}// end of main
Обратите внимание на маленькую разницу в работе с переменными SQLJVARYING и SQL_TEXT (это соответственно VARCHAR и CHAR языка SQL). Разница в том, что если в базе данных хранится меньше символов, чем максимально возможно для столбца (например, объявлено CHAR(16), а хранится строка "12345"), то сервер добавит N пробелов в конец строки, где N является' разницей между максимально возможным количеством символов и реально хра-1 нящимся в поле таблицы. Тип SQL_VARYING свободен от этого недостача,< однако при получении данных нужно учитывать, что только определенное зна-1 чение символов является реально полученными; остальное количество - это| случайные данные из памяти компьютера, на котором исполняется скрипт. Для | удобства работы с этим типом обычно определяют структуру, где член структу- ры SQL_VARCHAR vary_length указывает размер полученной строки,^ a vary_string собственно содержит строку.
Если запрос гарантированно возвращает одно значение (например, одиночный SELECT или вызов хранимой процедуры), то использовать функцию; isc_dsql_fetch() нет необходимости, вместо этого в параметр функции! isc_dsql_execute2() можно подставить значение osqlda переменной Работа с ти-1 пами SQL DATE и TIME абсолютно не отличается от работы с переменнымиii типа TIMESTAMP - всего лишь используются другие функции для преобразо-" вания: isc_decode_sql_date() и isc_decode_sql_time(). j
Пример 2. Запрос с параметрами
Теперь рассмотрим пример исполнения запроса с параметрами - вызов xpaнимой процедуры, которая просто вставит данные из формы в эту же таблицу.| Принципиально этот пример практически ничем не отличается от вышеприведенного, за исключением того что в нем появляются две дополнительные части - одна разбирает переменные HTML-формы, другая (если переменные переданы) исполняет процедуру.
Вот текст этой ХП.
create procedure InsertData (b_index char(16),
b_name varchar(80),
b_author varchar(80),
b_theme varchar(60))
returns (result_code integer)
as
begin
insert into books (
B_ID,B_INDEX,B_NAME, B_AUTHOR,B_ADDED,BJTHEME)
values(0,.b_index, :b_name, :b_author, 'now', :b_theme);
result_code = 0;
when any
do begin
result_code=-l;
end
end
Текст ХП достаточно банальный, вместо него в действительности можно было бы воспользоваться командой INSERT, однако подразумевается, что в реальной процедуре производятся некоторые манипуляции с входными данными (например, код книги может генерироваться не генератором, а по определенному алгоритму) и в качестве результата либо происходит вставка данных, либо процедура возвращает код ошибки.
Текст скрипта второго примера выглядит так :
#include <ibase.h>
#include <stdio.h>
#include <stdlib.h>
#include <scring.h>
#include <time.h>
#include "cgic.h"
#define SQL_VARCHAR(len) struct {short vary_length; char
vary_string[(len)+1];}
Вот здесь некоторое отличие: используемая для разбора переменных www- библиотека заменяет стандартную функцию main:
int cgiMain (void){ '
char *dbname = "localhost:/var/db/demo.gdb";
char *uname = "sysdba";
char "upass = "masterkey";
char *qaery = "select b_id, b_index, b_name, b_author, b_added,
b_theme from books";
На месте неизвестных входящих параметров - знаки вопроса:
char *SPCall = "execute procedure insertdata (?,?,?,?)";
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;
int hDisplayed=0;
int formVarErr;
int ExecSP=l;
long res_code;
long b_id;
char b_index[17];
SQL_VARCHAR(100) b_name;
SQL_VARCHAR(100) b_author;
SQL_VARCHAR (100) b_theme;
ISC_TIMESTAMP b_added;
struct tm added_time;
char decodedTime[100];
char form_b_index[17],
form_b_name[81],
form_b_author[81],
form_b_theme[61];
В этой части происходит анализ переменных, полученных скриптом, и в зависимости от метода вызова принимается решение, исполнять ли ХП или нет.
printf("Content-type:
text/plainnn<htmlxbody><center><b>Example Nr 2</b><hr
width=%></centerxbr> Part 1: Executable SP
demo with both types of parameters.<br>");
if(strcmp(cgiRequestMethod,"POST")==0){
formVarErr =
cgiFormStringNoNewlines("b_index",form_b_index,17);
if (formVarErr!=cgiFormSuccess){
printf("<br><b>Error: Book index missed or too long</b>");
ExecSP=0,
}
formVarErr = cgiFormStringNoNewlines("b_name",form_b_name,81);
if {formVarErr!=cgiFormSuccess){
printf ("<brxb>Error: Book name missed or too long</b>");
ExecSP=0;
}
formVarErr =
cgiFormStringNoNewlines("b_author",form_b_author,81);
if (formVarErr!=cgiFormSuccess){
printf ("<br><b>Error: Book author missed or too long</b>");
ExecSP=0;
}
formVarErr =
cgiFormStringNoNewlines("b_theme",form_b_theme,61);
if (formVarErr!=cgiFormSuccess){
printf("<br><b>Error: Book theme missed or too long</b>");
ExecSP=0;
}
}
else{
ExecSP=0;
printf ("<brxi>Procedure execution skipped</i> - REQUEST_METHOD
must be POST");
}
Заметьте: если специфика приложения требует того, чтобы определенные www-переменные обязательно присутствовали и удовлетворяли определенным условиям (например, учетные данные пользователя могут быть защищены контрольной суммой), то именно в этом месте приложение производит решение о целесообразности продолжения работы. 1
После разбора переменных идет непосредственно работа с базой данных:
dpb=dpb_buffеr;
*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);
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(l);
}
}
Если были получены данные и они корректны, происходит вызов хранимой процедуры:
if(ExecSP){
printf("<br><i>Attempt to call SP with the following
parameters:
' %s' , '%s' , ' %s' , '%s'</i>.....",form_b_index,form_b_name,form_b_a
uthor,form_b_theme);
Как можно видеть, принципиально инициализация структур для входящих параметров не сильно отличается от инициализации исходящих параметров из первого примера:
isqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(4));
isqlda->version = SQLDA_VERSION1;
isqlda->sqln = 4;
isqlda->sqld = 4;
isqlda->sqlvar[0] .sqldata = (char *)&form_b_index;
isqlda->sqlvar[0].sqltype = SQL_TEXT;
isqlda->sqlvar[0].sqllen = strlen(form_b_index);
isqlda->sqlvar [1] . sqldata = (char * ) &f orm_b_name,
isqlda->sqlvar[1].sqltype = SQL_TEXT;
isqlda->sqlvar[1].sqllen = strlen(form_b_name);
isqlda->sqlvar[2].sqldata = (char *)&form_b_author;
isqlda->sqlvar[2].sqltype = SQL_TEXT;
isqlda->sqlvar[2].sqllen = strlen(form_b_author);
isqlda->sqlvar[3].sqldata = (char *)&form_b_theme;
isqlda->sqlvar[3].sqltype = SQL_TEXT;
isqlda->sqlvar[3].sqllen = strlen(form_b_theme);
osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(1));
osqlda -> version = SQLDA_VERSION1;
osqlda -> sqln = 1; osqlda -> sqld = 1;
osqlda->sqlvar[0].sqldata = (char *)&res_code;
osqlda->sqlvar[0].sqltype = SQL_LONG;
osqlda->sqlvar[0].sqllen = sizeof(long);
osqlda->sqlvar[0].sqlind = &o_ind[0];
Вызов ХП происходит сразу, без предварительной подготовки (подробнее об этом будет рассказано несколько ниже):
isc_dsql_exec_immed2(
status_vector,
&db_handle,
&transaction_handle,
0,
SPCall,
SQL_DIALECT_V6,
isqlda,
osqlda};
if (status_vector[0] == 1 && status_vector[1]){
isc_print_status(status_vector) ;
return(1);
}
if(res_code==0){
printf(" <t»successfly</b>.");
}
else{
printf(" <b>failed with result_code=%i</b>.",res_code);
}
free(isqlda);
free(osqlda);
}