Текущий архив: 2006.02.26;
Скачать: CL | DM;
Вниз
Интерфейс взаимодейстия с базой Найти похожие ветки
← →
Piter © (2006-01-02 23:33) [0]Нужна организовать для DLL-плагина к программе доступ к SQL-базе. База - Interbase, но это не так важно.
То есть, внутри программы понятно: TDatabase / TQuery - все ясно.
А вот как организовать интерфейс взаимодействия с плагином? Чтобы плагин мог выполнять SQL запросы и получать результаты? Может кто занимался подобным, имеет опыт?
Может что-то типа:
CreateRequest("SQL-выражение") - функция возвращает Handle некоего созданного объекта (ну там ноль в случае неудачи)
Потом всякие:
GetParam(Handle, "ParamName", AsInteger)
А в конце работы нужно закрыть объект:
CloseRequest(Handle);
Соответственно, в программе Handle это может быть чисто физически просто адрес TQuery. При выполнении запроса CreateRequest создается объект TQuery, Handle возвращается плагину. При CloseRequest объект TQuery уничтожается.
TQuery вроде как потоко-безопасный.
Покритикуйте плиз данную схему и как по другому можно?
В данной схеме меня пока смущает частое создание/уничтожение объекта TQuery - при каждом запросе. С другой стороны, вроде ничего страшного, TDataBase один общий будет, так что по скорости проблем особо не должно быть.
← →
Piter © (2006-01-02 23:37) [1]Ну а:
CreateRequest, GetParam, CloseRequest - соответственно, просто импортируемые плагином функции
← →
Piter © (2006-01-02 23:40) [2]Еще вот что смущает - GetParam же может возвращать разные типы: integer, float, string и т.д.
То есть, в общем случае он возвращает PByte какой-нибудь, а уж плагин должен разобраться на что это ссылка в соответствии с поданым запросом: AsInteger, AsString и т.д.
Или сделать отдельные функции:
GetIntegerParam(Handle, "ParamName")
GetStringParam(Handle, "ParamName")
и т.д.
Вот думаю - как лучше?
← →
Sergey Masloff (2006-01-02 23:43) [3]Непонятно зачем это. Да и клиентская библиотека InterBase все рвно в очередь построит все запросы, непараллельная она.
Плагины к разным базам обращаться будут чтоли?
← →
Sergey Masloff (2006-01-02 23:57) [4]Можно заюзать MIDAS.
Например что-то в таком роде:
1 модуль реализующий IAppServer. Он реализует соединение с БД
Можно сделать его универсальным то есть он будет получать SQL запрос в текстовом виде, создавать динамически TQuery и провайдера для него и отдавать ссылку на провайдер клиенту. А тот уже штатными средствами ClientDataSet будет работать с данными. Параметры можно передавать через OwnerData
Можно сделать несколько стандартных интерфейсов - выполнение произвольного SQL выполнение ХП и так далее. Интерфейс наследник IAppServer публикуется и используется всеми другими плагинами дла работы с базой.
Может конечно и изврат но может и нет. Я особо идею не обдумывал но реализовать так точно можно. И будет пусть и не тривиальное но использование довольно стандартной технологии.
← →
Piter © (2006-01-03 01:12) [5]Sergey Masloff (02.01.06 23:43) [3]
ты не понял. Программа - плагиновая.
Плагин может делать в принципе что ему захочется. Он, конечно, вправе использовать сам какие ему угодно базы данных. НО - зачем?
Хочется с помощью ядра программы предоставить доступ к нормальной базе данных. Более того, вполне возможно, что работа с базой будет осуществляться еще одним плагином. Хочешь - плагин для Interbase, хочешь - плагин для Access или MS SQl, да хоть Oracle.
Для других плагинов все это абсолютно прозрачно. Пусть они просто оперируют простыми SQL выражениями и получают наборы данных. А откуда взяты эти данные - плагин и не знает. Да хоть IB, хоть Oracle, хоть из текстовых файлов.
Проблема не в распараллелизации и подобном. Проблема в описании интерфейса взаимодействия плагина с базой, хранилищем данных.
В Delphi это удобно сделано с помощью TQuery, например. Верно же? Это некая стандартизация доступа к данным. НО. У меня .NET пока не планируется, поэтому действовать на уровне объектов нельзя - плагин может быть написан и на Delphi, и на C++, на чем угодно, что может вызывать WinApi функции и компилировать библиотеки DLL.
То есть, в программе то наверняка будет TDataBase, TQuery, но как предоставить данные плагину? В этом и проблема...
Уф, надеюсь внятно объяснил... Если что - спрашивай.
← →
Piter © (2006-01-03 01:17) [6]Кстати, не исключая вариант, что это я тебя не понял, Сергей.
Тогда плиз поподробнее :)
Особенно про:
Sergey Masloff (02.01.06 23:57) [4]
создавать динамически TQuery и провайдера для него и отдавать ссылку на провайдер клиенту
что значит отдавать ссылку на провайдера? Что это такое?
Что может извлечь плагин, имея ссылку на провайдера. Да и что такое провайдер? :)
P.S. Думаю, и так понятно, что в базах я ламачок :)
← →
Piter © (2006-01-03 01:19) [7]Piter © (03.01.06 1:12) [5]
В Delphi это удобно сделано с помощью TQuery
ну точнее, наверное, сказать TDataSet, но опять же не суть важно, думаю проблема понятна...
← →
Sergey Masloff (2006-01-03 08:29) [8]Ну я же дал направление поиска - MIDAS. Все уже давно реализовано. Провайдер посредник между датасетом в серверном модуле (Query там или StoredProcedure) и датасетом в клиентском модуле.
То есть схема работы такая:
1) У тебя появляется интерфейс потомок IAppServer в котором тебе придется добавить методы принимающие строку например с текстом SQL и возвращающие провайдер к которому можно подключить ClientDataSet
2) У тебя появляется плагин реализующий твой потомок IAppServer. ТО есть в нем появляется TREmoteDataModule и методы реализации твоего IAppServer. Базовое приложение (к которому подцепляются все плагины) будет использовать эту реализацию для предоставления ее остальным плагинам - клиентам. Кстати меняя этот "серверный" плагин можно допустим коннектится действительно не к IB а к ораклу или еще куда
3) Появляются плагины-клиенты. Им всем передается ссылка на IAppServer которую они дергают для заполнения данными своих TClientDataSet.
Естественно все это работает только если клиентские плагины дельфийские или C++Builder овские.
Так что вооружайся справкой и вперед. Ключевые слова multi-tiered database applications. Простейший пример MIDAS есть в каталоге DEMOS. Только там все рассчитано что клиент и сервер на разных машинах поэтому всякие доп. прослойки появляются тебе они не нужны. Просто нужно напрямую ссылки на IAppServer передавать а не парится со всякими SocketServer-ами наверное.
Если есть вопросы общего плана я ответить могу а вото подробности - врядли Delphi у меня нет проверить ничего не могу ;-)
← →
Sergey Masloff (2006-01-03 08:39) [9]Сейчас в новых (D6 D7) MIDAS по-другому называется. DataSnap вроде.
← →
Sergey Masloff (2006-01-03 10:00) [10]Еще можно, видимо, обмениваться ADO-шными рекордсетами но там больше придется писать в плане изменений данных. То есть не проблема заполнить рекордсет и отдать его клиенту а вот написать универсальный механизм записи результатов редактирования клиентом рекордсета в базу не так просто.
Именно если это должен быть универсальный механизм сложно так-то ничего сложного
← →
Desdechado © (2006-01-03 11:30) [11]Обязательно при передаче хэндлов (если ты хочешь именно по-своему сделать) помни, что компилироваться твоя прога и плагины должны на ИДЕНТИЧНЫХ версиях, ибо описание класса хранится и в EXE, и в DLL. И может (при разных версиях) получиться так, что обращаясь (в EXE) к TQuery.SQL.Text из плагина ты, на самом деле, обратишься к какому-нить другому свойству, расположенному по этому адресу (из-за несоответствия описания таблицы классов).
Часто это выявляется далеконе сразу, ибо положение свойств классов меняется редко, но вполне возможно. И будешь ловить AV и долго гадать, в чем дело...
← →
Piter © (2006-01-03 12:51) [12]Sergey Masloff (03.01.06 8:29) [8]
Естественно все это работает только если клиентские плагины дельфийские или C++Builder овские
так вот ничего естественного!
Сергей, я же писал:
Piter © (03.01.06 1:12) [5]
плагин может быть написан и на Delphi, и на C++, на чем угодно, что может вызывать WinApi функции и компилировать библиотеки DLL.
то есть, фактически НИКАКИХ объектов, так как .NET не планируется.
Desdechado © (03.01.06 11:30) [11]
Обязательно при передаче хэндлов (если ты хочешь именно по-своему сделать) помни, что компилироваться твоя прога и плагины должны на ИДЕНТИЧНЫХ версиях
да НЕ БУДЕТ плагин знать о том, что Handle - это ссылка на TQuery. Для плагина это как объект ядра программы. Handle можно и не ссылкой на TQuery делать, а рандомно генерить, просто так удобно и обеспечивается уникальность Handle...
← →
Sergey Masloff (2006-01-03 13:00) [13]Piter © (03.01.06 12:51) [12]
>плагин может быть написан и на Delphi, и на C++,
Да я это просмотрел. Тогда какие к фигам Query? Их просто не существует в це бейсики и вообще нигде кроме борланда.
Тогда можно использовать или рекордсеты ADO (но соответственно ADO нужно) или все данные отдавать в raw виде - ну там массив OLE Variants или самому конструировать строку вида
<rowset name = "custom name">
<row>
<col kind="myint" name = "field1">field1value</col>
<col kind="mystr" name = "field2">field2value</col>
....
</row>
....
</rowset>
выделять под нее память и отдавать указатель на нее и размер плагину. А там пусть разбирает как хочет
← →
Piter © (2006-01-03 13:32) [14]Sergey Masloff (03.01.06 13:00) [13]
Тогда какие к фигам Query?
Эх... Сергей!!! Перечитай плиз первые мои два поста!!!
О TQuery я говорил в том смысле - КАК сама работа с базой будет в ядре осуществляться. А плагин то понятное дело ни о каких TQuery и не знает.
Твою идею понял - возвращать весь набор данных в виде аля XML... Но а вдруг запрос вернет тысячи записей??? А если вдруг миллион? На формирование такого списка уйдет безумная куча времени и ресурсов... Мне пока мой способ нравится больще.
Итак, еще раз мой способ.
Это как бы перенос идеи объекта ядра из Windows. То есть, плагин создает объект ядра:
CreateRequest("SQL запрос");
Ему возвращается Handle. Далее используя этот Handle он может оперировать с открытым набором данных, как то:
GetIntegerParam(Handle, "ParamName"): integer
SetPosition(Handle, spNext): boolean
вместо spNext там: spBack, spFirst, spLast.
В САМОМ же движке это может быть наглядно реализовано, например:
function CreateRequest(SQLRequest: PChar): THandle; stdcall;
var
query: TQuery
s: string;
begin
s := SQLRequest;
query := SQLManager.CreateQuery;
if query = nil then
Result := 0
else
begin
query.SQL.add(s);
query.Open;
Result := THandle(Pointer(Query));
end;
Или типа подобного, я сейчас от руки набросал - но думаю понятно.
← →
Piter © (2006-01-03 13:38) [15]Возникает пока 1 вопрос по МОЕМУ способу:
хотелось бы универсальной функции: CreateRequest, то есть, которая принимала бы и SELECT запросы, и INSERT, UPDATE и так далее, преобразовывая в вызовы нужных методов TQuery: Open или ExecSQL.
← →
Piter © (2006-01-03 13:41) [16]В общем, я так понял мое направление правильное :)
Сергей мыслит бизнес-процессами, поэтому предполагает использование серъезных технологий, а у меня тут не так :)
Думаю, это самое оптимальное - использование TQuery и предоставление доступа к нему как к объекту ядра, то есть плагин просто каждый раз будет передавать Handle и вызывать нужные ему функции по работе с данными.
А в конце вызывается: CloseRequest(Handle) и тогда в ядре программы соответствующий объект TQuery уничатожается...
Если кто что понял - покритикуйте :)
← →
Sergey Masloff (2006-01-03 13:46) [17]Нет непонятно.
1) при чем тут вообще THandle - дельфийский псевдоним для Integer
2) если запрос вернет несколько тысяч записей ресурсов при использовании T(IB)Query уйдет не меньше а как бы и не больше чем в моем варианте
3) Ты собираешься держать тучу открытых запросов еще и с навигацией по ним. Причем если у тебя плагин отвалится то ты об этом никогда не узнаешь и открытый запрос останется навеки
Вобщем, идея мертвая. ИМХО. Дерзай может что и получится
← →
Desdechado © (2006-01-03 13:50) [18]ты для каждого плага собираешься свое множество квериков хранить (т.е. разделять будешь) или общий пул на всех (а как тогда с "чужими" некорректные операции)?
← →
Sergey Masloff (2006-01-03 13:57) [19]Piter © (03.01.06 13:38) [15]
>хотелось бы универсальной функции: CreateRequest, то есть, которая >принимала бы и SELECT запросы, и INSERT, UPDATE и так далее, >преобразовывая в вызовы нужных методов TQuery: Open или ExecSQL
Тут проблем нет есть свойство StatementType или что-то в этом роде возвращает перечислимое значение с типом выражения нк там INSERT или скажем DDL или DELETE
← →
Piter © (2006-01-03 13:58) [20]Sergey Masloff (03.01.06 13:46) [17]
1) при чем тут вообще THandle - дельфийский псевдоним для Integer
ну просто ссылки на объекты ядра обычно имеют в Delphi возвращаемый тип YHandle, вот и все. Например: CreateMutex, CreateSemaphore :)
Можно с таким же успехом просто LongWord использовать
Sergey Masloff (03.01.06 13:46) [17]
2) если запрос вернет несколько тысяч записей ресурсов при использовании T(IB)Query уйдет не меньше а как бы и не больше чем в моем варианте
хм... почему?
В твоем варианте я считаю кучу времени уйдет на формирование ИЗ набора даных текстовых XML строк. А у меня?
Sergey Masloff (03.01.06 13:46) [17]
Ты собираешься держать тучу открытых запросов еще и с навигацией по ним
а какие еще варианты? Ведь может быть несколько плагинов, каждый вправе использовать базу данных...
Sergey Masloff (03.01.06 13:46) [17]
Причем если у тебя плагин отвалится то ты об этом никогда не узнаешь и открытый запрос останется навеки
а что значит плагин отвалится? Выгрузится DLL-библиотека из адресного пространства? Сам себя что ли плагин выгрузит?
← →
Piter © (2006-01-03 14:04) [21]Desdechado © (03.01.06 13:50) [18]
ты для каждого плага собираешься свое множество квериков хранить
собственно, как видно выше, поводом к созданию TQuery является подача команды из плагина. Handle создаваемого TQuery передается в конкретный плагин, по завершению работы с базой плагин может "закрыть" объект ядра, что приведет к уничатожению этого TQuery. Поэтому сколько плагин захочет таких объектов создать, столько и будет.
Собственно, с другой стороны - один плагин может вполне обойтись одним своим TQuery.
Sergey Masloff (03.01.06 13:57) [19]
Тут проблем нет есть свойство StatementType или что-то в этом роде возвращает перечислимое значение
это свойство есть у TQuery?
Тогда не в тему, но странно - а зачем в самом TQuery разделили процедуры Open и ExecSQL?
← →
Sergey Masloff (2006-01-03 14:11) [22]2) Потому что (IB)Query расходует кучу ресурсов на поддержку двунаправленых курсоров. Так что прочитать все через компоненты без этого оверхеда и завернуть все в строковый буфер врядли сильно медленннее. Кроме того тот же IBQuery упадет с EOutOfMemory гораздо раньше чем пойдет речь о десятках тысяч записей.
>Ведь может быть несколько плагинов, каждый вправе использовать базу >данных...
Как обычно спросил->данные получил->отвалился. И так все хоть 100 штук одновременно
>а что значит плагин отвалится?
Да что угодно. Потеряет плагин твой хендл и все. Вариантов масса - от простых ошибок до умелого применения try except<пусто><end>
← →
Piter © (2006-01-03 14:27) [23]Sergey Masloff (03.01.06 14:11) [22]
Да что угодно. Потеряет плагин твой хендл и все
не, ну знаешь, уверен, что в твоем способе тоже плагин может намудрить - насоздавать там кучу провайдеров или еще чего-нибудь (я просто не разбираюсь) - это и так понятно.
Тоже самое, что процесс в Windows может наоткрывать кучу файлов, насоздовать кучу окон - все будет тормозить, но система ничего с этим поделать не в состоянии - а может прада процессу столько нужно?
Так что тут вопрос такой... В конце концов плагин работает в ВАП моего процесса, поэтому при "умелом" программировании он полюбому сможет убить ядро программы, тут как ни крутись...
Sergey Masloff (03.01.06 14:11) [22]
Как обычно спросил->данные получил->отвалился
Да, тут согласен, в моем варианте ПЛАГИН должен закрывать TQuery, это как недостаток при неумелом программировании, так и достоинство при умелом.
Sergey Masloff (03.01.06 14:11) [22]
Кроме того тот же IBQuery упадет с EOutOfMemory гораздо раньше чем пойдет речь о десятках тысяч записей
хм... сча потестю :)
← →
Piter © (2006-01-03 17:22) [24]Sergey Masloff (03.01.06 14:11) [22]
Кроме того тот же IBQuery упадет с EOutOfMemory гораздо раньше чем пойдет речь о десятках тысяч записей
сейчас руки дошли.
Итак, в таблице post всего записей: 17610
При этом запрос:
IBQuery1.SQL.Add("Select * from post");
Выполняется быстро и никаких нехваток памяти.
← →
Sergey Masloff (2006-01-03 18:06) [25]Piter © (03.01.06 17:22) [24]
Last() сделал? ;-)
Если нет то и говорить не о чем. По моим (давним) наблюдениям предел размера буфера порядка 5 Мб.
← →
Piter © (2006-01-03 18:11) [26]Sergey Masloff (03.01.06 18:06) [25]
сделал
← →
Piter © (2006-01-03 18:17) [27]Sergey Masloff (03.01.06 18:06) [25]
Last сделал, тестировал на стандартном D7.
Записей в таблице: 17610
Вроде никаких таких тормозов и ошибок памяти.
Единственное что - использовал для тестов Embedded версиб FB, локальную. Но это должно быть пофигу я думаю для TQuery - ему то откуда знать.
← →
Piter © (2006-01-03 18:19) [28]Sergey Masloff (03.01.06 18:06) [25]
По моим (давним) наблюдениям предел размера буфера порядка 5 Мб.
в этом наверное дело, вся база занимает: 11,2 Mb
Там 2 больших таблицы и несколько мелких (очень мелких). Тестируемая таблица самая большая, но видимо не дотягивает...
Тогда надо таблицу на сотню тысяч записей взять... У меня такой под рукой нету :(
← →
Piter © (2006-01-03 18:20) [29]генерировать влом, но потом может потестю :)
← →
Sergey Masloff (2006-01-03 18:25) [30]Ты сделай select * from big_table, small_table
получишь m*n записей ;-)))))
только 2 раза большую не множь а то устанешь ждать
Кстати может в новых IBX подняли размеры? Надо будет посмотреть. Я в 2003 году явление наблюдал - у меня начали клиенты стали сообщать об ошибке при одной из операций, в результате очень долгих тестовых мук было установлено что дело в IBQuery (или IBDataSet) и ограничении его внутр. буфера.
Страницы: 1 вся ветка
Текущий архив: 2006.02.26;
Скачать: CL | DM;
Память: 0.56 MB
Время: 0.032 c