Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Линтер методичка

.pdf
Скачиваний:
78
Добавлен:
21.05.2015
Размер:
1.38 Mб
Скачать

классе CSsqlDS инкапсулируется большинство работы нашего примера с СУБД ЛИНТЕР. Используются обычные вызовы Call-интерфейса, уже изученные нами.

П

Для связи элементов графического интерфейса с данными из БД необходимо

модифицировать стандартные элементы управления так, чтобы они работали через

CDataSourceра .

кт

Однострочный элемент должен выглядеть примерно так:

ич

class CMyEdit : public QLineEdit

ес

ко

{

е

 

за ...

ня

public:

ти

void setDS(CDataSource *ds);

е

12

void setField(int f) {m_field = f;}

П

ри

public slots:

ме

void onDsError(long);

р

ра

void onDsFullChange();

зр

void onCurRowChange(long oldrow);

аб

от

...

ки

пр

};

ил

 

ож

Для многострочного элемента необходимы (на примере с 'QListBox')

ен

а) Функции конвертации номера строки в базе и в элементе:

ия

long rowToDbRow(int);

дл

int dbRowToRow(long);

я

б) Синхронизация изменения положения курсора в источнике данных при

Л

 

изменении строки в управляющем элементе. Для этого подписываемся на сигнал:

И

connect(this, SIGNAL(highlighted(int)), this,

Н

ТSLOT(slot_highlighted(int)));

Еи пишем следующий код:

Р

void CMyListBox::slot_highlighted(int)

с

ис

{

по

m_ds->setCurRow(rowToDbRow(currentItem()));

ль

зо

}

ва

ни

в) Работу многострочного элемента можно построить так, что он не будет содержать

полную копию всех строчек выборки, а только видимую часть. И только при перемещении

ем

 

по строчкам по необходимости брать записи из источника данных. В нашем простом

Qt

 

примере это не рассматривается.

E-

Указания к практической работе:

!81

1. Просмотрите исходные тексты 1-го этапа (директория step1). В директории

mai

l:

datasource находятся h-файлы с описанием интерфейсных классов и cpp-файлы

ma rke t@ rele x.r

u

с
ис
по
ль
зо
ва
ни

82!

с реализацией классов источника данных. В директории ctrl находятся описания

Пи реализация классов визуальных элементов управления, расширенных до

ра

кт

ич

ес

ко

е

за

ня

ти

е

12

возможности работы с источниками данных. Кроме того, там описан и реализован класс формы нашего тестового приложения. В файле main.cpp содержится код основной программы, которая инициализирует приложение, создает соединение с БД ЛИНТЕР (используются жестко закодированные имя и пароль “SYSTEM/MANAGER”), инициализирует источник данных и создает графическую форму. Обратите внимание на makefile. Там необходимо переопределить переменные LINTER (на путь, в котором установлена СУБД ЛИНТЕР) и QTDIR (путь к установке Qt).

20.Скомпилируйте исполняемый файл (команда make). Обратите внимание, что программа компилируется с отладочной информацией (ключ –g).

21.

Запустите программу и просмотрите содержимое таблицы AUTO

в форме

 

приложения.

 

22.

Запустите программу в отладчике gdb . Поставьте точки останова в методах,

Преализующих работу источника данных с СУБД ЛИНТЕР, например в методах

CSqlDS::retrieve и CSqlDS::getField. Понаблюдайте за исполнением методов,

ри

 

включая вызовы ЛИНТЕР, по шагам.

 

ме

 

 

 

 

 

 

р

12.2. Второй этап

 

ра

 

Добавим в наш пример возможность обработки сообщений от СУБД ЛИНТЕР. Для

зр

приложения интересны асинхронный прием сообщения и синхронная обработка.

аб

 

 

 

 

Асинхронная обработка имеет ряд системных ограничений и не все вызовы из нее можно

от

 

 

 

 

делать (в POSIX стандарте есть список разрешенных вызовов).

ки

Разработаем по нашей проблеме класс

AsyncManager Наш класс содержит три

статическиепр

функции:

 

ил

AsyncManager::addNewAsync - регистрация синхронного обработчика для

ож

 

асинхронного запроса по определенному каналу;

ен

AsyncManager::removeAsync - снятие регистрации синхронного обработчика для

ия

 

асинхронного запроса;

 

AsyncManager::onAsyncWork - универсальный асинхронный обработчик,

дл

 

передается как аргумент вызова inter.

 

я

 

 

 

 

 

 

Л

Для синхронизации используем системный (Qt-1.4.x) цикл обработки сообщений. В

И

 

 

таком цикле обязательно стоит вызов типа 'select(.....)', который проверяет состояние по

указаннымН

дескрипторам ввода-вывода, в Qt есть класс QSocketNotifier, который позволяет

зарегистрироватьТ дескриптор ввода-вывода на такую проверку. Мы воспользуемся им.

Создадим 'pipe' и будем его использовать как флаг наличия синхронного обработчика (для

Е

асинхронного запроса) в очереди. Для этого переводим 'pipe' в режим неблокирующей

Р

работы и регистрируем дескриптор чтения (на прослушивание в 'select') таким образом:

m_qNotifier = new QSocketNotifier(m_pipe[0], QSocketNotifier::Read, this);

connect(m_qNotifier, SIGNAL(activated(int)), this, SLOT(SyncProc(int)));

В нашем объекте будем вести два списка объектов типа AsyncObject, которые содержатем информацию о канале, по которому был подан запрос, и синхронной функции-

обработчике. Первый список m_targetList - список поставленных на обработку, второй

Qt

m _ r a i s e L i s t - с п и с о к гото в ы х к с и н х р о н н о й о б р а б от к е. В о б р а б от ч и к е

AsyncManager::onAsyncWork производится перевод из m_targetList в m_raiseList список.

 

 

 

Для активизации 'QSocketNotifier' в 'pipe' записывается 1 байт таким образом:

 

 

 

 

 

 

E-

while(write(m_pipe[1], &event, sizeof(event)) < 0)

 

 

mai

 

 

 

l:

if (errno != EINTR)

 

 

ma

 

 

rke

 

 

 

t@

 

rele

 

 

 

x.r

 

 

 

u

 

{

Пerrno = 0;

ра

break;

кт

}

ич

ес

Цикл 'while' здесь применяется для гарантированной записи в 'pipe'. Если ‘pipe ’ уже

заполненко

, то запись пропускается (т.к. любой системный вызов может быть прерван

обработкой по сигналу, см. POSIX).

е

Все функции класса, которые модифицируют списки m_targetList и m_raiseList при

за

с в о е й р а б о т е, д о л ж н ы б л о к и р о в а т ь а с и н х р о н н у ю о б р а б о т к у т.к.

ня

 

 

 

AsyncManager::onAsyncWork также модифицируют эти списки. Блокировать необходимо

ти

 

SIGIO и SIGUSR1 - эти сигналы могут быть использованы для асинхронной

сигналы

обработкие

на клиентской части СУБД ЛИНТЕР.

12

Универсальный синхронный обработчик AsyncManager::SyncProc вызывается из

цикла обработки очереди сообщений при активизации класса QSocketNotifier. В нем из

П

 

 

списка m_raiseList активизируется синхронные обработчики по каждому каналу. Список

ри

 

 

m_raiseList и 'pipe' очищается. Цикл ‘while(read(m_pipe[0], &event, sizeof(event)) > 0)’

ме

 

 

применяется для гарантированной очистки 'pipe', как и в случае с write.

р

В приложении создается один объект класса AsyncManager, его конструктор

содержитра

код инициализации - создание 'pipe' и QSocketNotifier.

зр

Класс CDbEvent непосредственно связывается с ожидаемым событием Линтера, в

его конструктор передается имя события. Активизация события в программе

аб

 

осуществляется через метод-сигнал onRaise, интересующиеся объекты должны

от

 

подписаться на этот сигнал (здесь говорится о сигнале, как о термине Qt).

ки

Вся работа по обработки асинхронного запроса производится через

пр

 

функциональность выше описанного класса, синхронный обработчик - статическая функция

классаил CDbEvent::onSelect. Объект может находиться в 3-х состояниях: Empty/Busy/Ready.

В состояние Busy (ожидания события) объект переводится вызовом метода retrieve, т.е. -

ож

 

асинхронно выполняется запрос

ен

wait event auto_change;

ия

дл

Внутри метода retrieve открывается новый курсор, чтобы освободить соединение для

я

 

другой работы. Метод clear выполняет запрос

Л

clear event auto_change;

И

НМетод destroy переводит объект в состояние 'Empty'.

Т

Указания к практической работе:

Е

1.

Просмотрите исходные тексты 2-го этапа (директория step2). По сравнению с

Р

 

первым этапом, во-первых, добавлена директория async, в которой находится

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

ис

директории datasource добавлены исходные тексты класса CDbEvent. В файле

по

main.cpp добавлена инициализация события.

23. Скомпилируйте исполняемый файл (команда make).

ль

24. Запустите программу и просмотрите содержимое таблицы AUTO в форме

зо

приложения. Затем измените содержимое таблицы AUTO из другой сессии,

ва

например, подав запрос из программы INL . Приложение должно автоматически

ни

отобразить изменения.

ем

25. Запустите программу в отладчике gdb . Посмотрите по шагам, как в программе

Qt

выполняется обработка события БД.

E-

12.3. Третий этап

!83

mai

На этом этапе мы добавим асинхронную обработку запросов в классе CSqlDS.

l:

ma

 

rke

 

t@

 

rele

 

x.r

 

u

 

 

В интерфейсе класса CDataSource никаких изменений нет, но объект теперь может

 

находитьсяП

в 3-х состояниях: Empty/Busy/Ready. Новое состояние 'Busy', как и для объекта

 

класса

CDbEvent, характеризует объект, находящийся в процессе ожидания ответа на

 

ра

 

 

 

 

 

 

 

 

запрос к СУБД ЛИНТЕР.

 

 

 

 

кт

Добавлен новый метод-сигнал onState - вызывается при изменении состояния

 

ич

 

объекта. Метод retrieve изменен: открывается новый канал для работы (как в CDbEvent) и

 

ес

 

 

 

 

 

 

 

 

запрос посылается асинхронно с переводом в состояние 'Busy'. При получении ответа

 

вызываетсяко

 

синхронный обработчик - статическая функция класса CSqlDS::onSelect -

 

который переводит объект в состояние 'Ready' (вызов метода-сигнала onState) или

 

е

 

 

 

 

onError. При изменении состояния объекта

CSqlDS

в

 

вызывается метод-сигнал

 

за

 

 

 

 

CDataSource вызывается метод-сигнал

onFullChange и

 

интерфейсном классе

 

ня

 

 

 

 

 

 

 

 

переустанавливается положение курсора.

 

 

 

ти

Указания к практической работе:

 

 

 

е

 

 

 

1.

Просмотрите исходные тексты 3-го этапа (директория step3). Имеются изменения

12

 

в директории ctrl. Вместо списка в форму добавлен табличный элемент – класс

 

П

 

 

 

CMyTable, который реализует работу с источником данных простого табличного

 

ри

 

визуального элемента, позаимствованного из стандартных примеров

Qt

 

ме

 

(исходные тексты этого элемента находятся в поддиректории table; они взяты из

 

р

 

примеров Qt как есть). Есть изменения также и в директории datasource: они

 

 

касаются, в основном, асинхронной обработки. Соответствующим образом

 

ра

 

 

 

модифицирован и файл main.cpp.

 

 

 

зр

26. Скомпилируйте исполняемый файл (команда make).

 

 

 

аб

27. Запустите программу. Обратите внимание, что запрос на выборку данных теперь

 

от

 

выполняется долго (мы специально вставили перемножение таблицы AUTO

,

ки

 

чтобы время работы запроса было велико). Но наше приложение при этом

 

пр

 

продолжает работать: оно выдает сообщения о состоянии, реагирует на события

 

 

от клавиш и мыши и т.д. Это возможно благодаря асинхронной обработки

 

 

ил

 

 

 

 

запроса.

 

 

 

 

ож

 

 

 

 

 

 

 

 

ен

12.4. Задание для самостоятельной работы

 

 

 

ия

 

 

 

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

 

 

дл

 

 

модификации (возможность удаления, модификации и добавления записи в источник

 

я

 

 

 

 

 

 

 

 

данных).

 

 

 

 

 

 

 

Л

Для этого в форме уже предусмотрены кнопки "Обновить", "Вставить", "Удалить".

 

НеобходимоИ

добавить обработчики нажатия на эти кнопки, которые будут формировать и

 

исполнять запросы UPDATE, UNSERT, DELETE для таблицы AUTO. При этом данные для

 

Н

 

 

 

 

 

 

 

 

этих запросов можно взять из четырех однострочных полей ввода, имеющихся в форме.

 

Т

 

 

 

 

 

 

 

 

Непосредственные вызовы Call-интерфейса ЛИНТЕР можно скрыть в классе CSqlDS,

 

Е

 

 

 

 

 

 

 

 

например, добавить в него метод modify, который будет в качестве параметра принимать

 

текстР запроса, и выполнять этот запрос при помощи команды Call-интерфейса “ “ (четыре

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

 

с

 

 

 

 

(например, функцией sprintf, используя тексты однострочных

 

требуемый текст запроса

 

ис

 

 

 

 

 

 

 

 

полей) и передать его методу modify.

 

 

 

по

Отметим, что после модификации таблицы старая выборка, сделанная запросом

 

ль

 

 

 

 

 

 

 

 

 

SELECT, перестает отображать реальные данные. Простейший способ избежать этого –

 

выбратьзо

данные заново после их модификации (это позволяет, в том числе, использовать

 

для INSERT,DELETE,UPDATE тот же канал ЛИНТЕР, т.к. результаты старой выборки

 

ва

 

 

 

 

 

 

 

 

SELECT нам все равно уже больше не понадобятся). Заметим, что в нашем примере

 

ни

 

 

 

 

 

 

 

 

данные автоматически будут перечитываться после любой операции модификации

 

ем

 

 

 

 

 

 

 

 

благодаря отслеживанию события БД.

 

 

 

Qt

Учащимся необходимо самостоятельно реализовать и отладить описанную

 

функциональность. Она является самым простым способом обработки модификаций в БД.

 

Более сложный подход описан в следующем разделе. Однако, он требует уже более

 

серьезного программирования, и в рамках данной практики не реализуется.

 

 

E-

12.5. Пути развития класса – источника данных

 

 

 

mai

 

 

 

l:

Что еще не хватает в разработанном классе CDataSource?

 

 

 

!84

 

 

 

 

 

 

 

 

ma rke t@ rele x.r

u

Не хватает также управлением транзакциями RollBack/Commit.

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

перечитывая данные из базы, а также поднять производительность обмена между ядром

П

СУБД ЛИНТЕР и клиентом за счет использования команд пакетной загрузки и выгрузки данныхра GETM, PUTM.

кт ич Кроме того, источники данных можно делать связанными в соотношении 'Главный-

подчиненный', организовать фильтрацию/сортировку в памяти, работу с выборкой с полной

ес

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

ко

е В AsyncManager можно произвести оптимизацию по вызовам new/delete, если ввести

список свободных объектов и использовать его как буфер свободных объектов.

за

ня

ти

е

12

П

ри

ме

р

ра

зр

аб

от

ки

пр

ил

ож

ен

ия

дл

я

Л

И

Н

Т

Е

Р

с

ис

по

ль

зо

ва

ни

ем

Qt

E!85- mai

l: ma rke t@ rele x.r

u

86!

Практическое занятие 13 Использование прекомпилятора встроенного SQL

Практика 6 часов (Лекция 11)

На занятии будут приведены примеры, иллюстрирующие работу прикладных программ с СУБД ЛИНТЕР, написанных на языке C с использованием встроенного SQL.

Будут рассмотрены следующие вопросы:

выполнение запросов без параметров;

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

выполнение запросов с параметрами;

получение данных из выборки;

обработка результатов запроса с неизвестным форматом ответа;

выполнение хранимых процедур.

13.1. Компиляция примеров

Компиляция программ, написанных с использованием встроенного SQL, осуществляется в два этапа. Препроцессор pcc поставляется в дистрибутиве СУБД ЛИНТЕР. Результатом его работы является c -файл, который следует компилировать обычным компилятором:

prac13_e1: prac13_e1.pc

$(LINTER)/bin/pcc prac13_e1.pc prac13_e1.c $(CC) -c $(CFLAGS) prac13_e1.c

$(LINKER) prac13_e1.o $(LDKEY)prac13_e1 $(INTLIB) rm -f $@.o

С примерами поставляется makefile.

13.2. Соединение, выполнение запросов без параметров, обработка ошибок

Пример prac13_e1.pc иллюстрирует работу с запросами без параметров. Его алгоритм таков:

создается соединение;

делается попытка удаления таблицы PRAC13_T1;

создается таблица PRAC13_T1;

вставляются данные;

делается выборка количества записей в переменную;

делается выборка поля NAME в переменную buf.

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

EXEC SQL WHENEVER SQLERROR GOTO not_inserted;

EXEC SQL FOR :i EXECUTE IMMEDIATE insert into PRAC13_T1 (id, name) values (:arr_i, 'aaa');

E-mail: market@relex.ru

ЗАО НПП «РЕЛЭКС»

http://www.relex.ru

для этого переменной i присваивается количество повторений, в данном случае, 10. Обработка ошибок осуществляется оператором WHENEVER. Для продолжения

работы используется режим CONTINUE, для перехода на метку – GOTO, для вызова

П

 

функции – CALL и для завершения приложения – STOP.

ра

EXEC SQL WHENEVER SQLERROR CONTINUE;

кт

ич

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

ес

EXEC SQL WHENEVER SQLERROR GOTO not_selected;

ко

еEXEC SQL SELECT count(*) from PRAC13_T1 into :i;

за

При этом переменная I должна быть описана в разделе DECLARE SECTION:

ня

EXEC SQL BEGIN DECLARE SECTION;

ти

е

 

short i = 10;

13

 

Ис

EXEC SQL END DECLARE SECTION;

по

Разрешено включать в запрос переменные:

ль

EXEC SQL EXECUTE IMMEDIATE SELECT NAME into :buf from

зо

ва

PRAC13_T1 where ID = :tid;

ни

Задача 1. Модифицировать данный пример, таким образом, чтобы при

е

невозможности соединения приложение завершало свою работу.

пр

Задача 2. Модифицировать данный пример, добавив возможность обновления поля

ек

 

 

NAME пользователем в строке по указанному ID.

ом

Задача 3. Модифицировать данный пример, добавив полноценный обработчик

ошибокпи , анализирующий переменную ErrPCI_.

ля

13.3. Запросы с параметрами

то

Для выполнения запроса с параметрами необходимо использовать операторы. В

ра

 

 

примере prac13_e2.pc для вставки строк в таблицу и для построения выборки используются

запросывс

с параметрами:

тр

создается соединение;

ое

делается попытка удаления таблицы PRAC13_T1;

нн

создается таблица PRAC13_T1;

ог

создаются два оператора – один для вставки данных, другой – для выборки;

о

в цикле: пользователь вводит данные одной строки, и выполняется оператор

S

 

вставки, причем данные передаются как параметры;

Q

выбирается количество записей из таблицы;

L

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

 

в цикле по данному интервалу делается выборка одной записи, чей номер

!87

 

принадлежит интервалу.

 

 

 

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

 

EXEC SQL PREPARE ST_INS FROM INSERT INTO PRAC13_T1 (id,

 

name) VALUES (:tid, :tname);

 

EXEC SQL PREPARE ST_SEL FROM SELECT id, name

E-

into :tid, :tname from (SELECT id, name, rownum as rn FROM

PRAC13_T1) WHERE Rn = :i;

mai

l:

Вставка данных:

ma rke t@ rele x.r

u

Алгоритм работы примера таков:
создается соединение;
резервируется память под дескриптор;
13.4. Получение структуры ответа
scanf("%d %s", &tid, tname);
EXEC SQL EXECUTE ST_INS USING :tid, :tname;
Выборка данных:
for (i = n1; i <= n2; i++)
{
EXEC SQL EXECUTE ST_SEL USING :i into :tid, :tname; printf("\t%d.\t%d\t%s\n", i, tid, tname);

88!

П

ра

кт

ич

ес

ко

е

за

ня

ти

е}

13

Ис

по Получение структуры ответа во встроенном SQL осуществляется при помощи

дескрипторов. Работа с дескрипторами показана в файле примера prac13_e3.pc.

ль

зо

ва

ни

еEXEC SQL ALLOCATE DESCRIPTOR :selectOutDesc;

пр

пользователь вводит SELECT-запрос;

ек

по запросу создается оператор;

ом

EXEC SQL PREPARE ST_SEL FROM :szSQL;

пи

ля

делается описание выходных данных оператора в дескриптор;

то

EXEC SQL DESCRIBE OUTPUT ST_SEL INTO SQL

ра

вс

DESCRIPTOR :selectOutDesc;

тр

выводится тип и длина полей ответа;

ое

привязывается к дескриптору буфер, в который следует помещать ответ;

нн

for (i = 1; i <= colCount; i++)

ог

о

{

 

S

 

 

p = &(buf[shift]);

Q

 

L

EXEC SQL GET DESCRIPTOR :selectOutDesc VALUE :i :length =

 

 

LENGTH;

EXEC SQL SET DESCRIPTOR :selectOutDesc VALUE :i DATA

=:p;

shift += align4(length);

}

 

описывается и открывается статический курсор;

E-

EXEC SQL DECLARE CUR_SEL CURSOR FOR ST_SEL;

EXEC SQL OPEN CUR_SEL;

mai

l:

осуществляется проход по выборке.

ma

rke t@ rele x.r u

E- mai l: ma rke t@ rele x.r u
89!
return tointeger(ch1) + tointeger(ch2); end
нн Работа данного оператора иллюстрируется в примере prac13_e4.pc. Для демонстрацииог данного примера необходимо создать тестовую процедуру, при помощи,
например, утилиты inl:
о
S create procedure STRADD(in ch1 char(10); in ch2 char(10))
Q result int
L
code

for (i = 1; ;i++)

П{

ра

 

memset(buf, 0, sizeof(buf));

кт

 

 

EXEC SQL FETCH CUR_SEL ABSOLUTE :i USING

ич

 

ес

DESCRIPTOR :selectOutDesc;

ко

 

if (ErrPCI_)

е

 

за

 

{

 

ня

 

 

 

if (ErrPCI_ == 3000) printf("No more records\n");

ти

 

е

 

else printf("Error code: %d\n", ErrPCI_);

13

 

Ис

 

break;

по

 

}

 

ль

 

 

зо

 

printAnswer();

ва

 

}

 

ни

 

е

В примере присутствует функция для определения имени типа по его номеру,

возвращенногопр

дескриптором (нумерация описана в разделе пользовательской

документации

«Встроенный SQL»). Для совместимости со Sparc-платформами

ек

 

 

 

предназначена функция align4, вычисляющая адрес, нацело делящийся на 4.

ом

Задача 4. Модифицировать пример prac13_e2.pc, изменив алгоритм выборки:

пи

пользовательский интервал учитывается параметрами в операторе (WHERE Rn >= :num1

ля

 

 

 

and Rn <= :num1). Движение по выборке осуществлять при помощи оператора FETCH.

то

Задача 5. Модифицировать пример prac13_e3.pc, добавив обработку типов byte,

varbyte,ра

varchar, date, decimal, boolean.

вс

13.5. Вызовы хранимых процедур

тр

Встроенный SQL позволяет выполнять хранимые процедуры. Для этого

ое

 

предназначен оператор EXECUTE PROCEDURE.

Алгоритм работы примера таков:

создается соединение;

объявляется процедура;

EXEC SQL DECLARE PROCEDURE STRADD(in ch1 char(10); in ch2 char(10)) result int;

пользователь вводит две строки, в которых находятся целые числа;

!90

П

вызывается процедура с этими параметрами;

EXEC SQL EXECUTE PROCEDURE :res = STRADD(:ch1, :ch2);

ра

кт

выводится на экран возвращенное значение.

ич

 

 

ес

Важно, что типы фактических параметров, принимающих результаты (в примере –

переменная int res), должны совпадать с типами формальных параметров (в примере –

ко

 

 

result int – результат процедуры).

 

е

Задача 6. На основе примера prac13_e4.pc, написать программу, проверяющую

за

наличие таблицы, представления или синонима по маске, заданной пользователем (в

ня

 

select * from $$$SYSRL where $$

хранимой процедуре использовать запрос

ти

 

 

$S13 = :s;).

 

е

 

 

13

Ис

по

ль

зо

ва

ни

е

пр

ек

ом

пи

ля

то

ра

вс

тр

ое

нн

ог

о

S

Q

L

E- mai l: ma rke t@ rele x.r u