Форум » LetoDB, HbNetio. » Триггеры в Letodb » Ответить

Триггеры в Letodb

alkresin: Об установке/активизации триггера с клиента. Сейчас это работает: dbInfo( DBI_TRIGGER, ... ) - можно и включить/отключить, и новую процедуру установить. Я считаю это принципиально неверным, но убирать совсем это дело тоже, наверное, неправильно - может, кто-то использует это вовсю. В конце концов, каждый - сам кузнец своего счастья. Добавлю, наверное, опцию в letodb.ini, которая разрешает установку этого параметра с клиента ( по умолчанию должно быть запрещено - все опции по умолчанию должны обеспечивать безопасность и целостность базы ). И по поводу установки триггеров для каждой таблицы индивидуально. Предлагаю сделать это так: в letodb.ini добавляем параметр, который это разрешает, можно и для отдельных папок ( [DATABASE] ). Далее, чтобы установить триггер для таблицы sklad/otdel10/prihod.dbf, помещаем в letoudf.hrb процедуру с названием, например, trig_sklad_otdel10_prihod - и все. При наличии разрешения и при наличии такой процедуры сервер все делает сам.

Ответов - 20

SergKis: alkresin пишет:чтобы установить триггер для таблицы sklad/otdel10/prihod.dbf, помещаем в letoudf.hrb процедуру с названием, например, trig_sklad_otdel10_prihod - и все как быть, если таблицы prihod.dbf находятся по подкаталогам месяцев, по годам, т.е. имя таблицы одно, а мест расположений много ? надо как то группировать, нескольким таблицам (может по шаблону) один триггер

SergKis: В SixNsx была метода в триггере: в EVENT_PREUSE (при наличии ini у dbf), если он отсутсвовал, читать структуру, индексы из ini, делать dbCreate, OrdCreate. как правильно поступать в letodb, при наличии ini у таблицы ?

alkresin: Теперь по умолчанию dbInfo(DBI_TRIGGER,...) не может изменить установки триггера. Чтобы разрешить dbInfo() менять установки, ставьте в letodb.ini: EnableSetTrigger = 1 SergKis пишет: как быть, если таблицы prihod.dbf находятся по подкаталогам месяцев, по годам, т.е. имя таблицы одно, а мест расположений много ? надо как то группировать, нескольким таблицам (может по шаблону) один триггер Можно сделать, например, так: называть такие функции trigall_prihod(), а в коде сервера добавить проверку наличия такой функции, если нет trig_path_prihod(). как правильно поступать в letodb, при наличии ini у таблицы ? Наверное, в коде триггерной функции это можно сделать.


SergKis: alkresin пишет:Можно сделать, например, так: называть такие функции trigall_prihod(), а в коде сервера добавить проверку наличия такой функции, если нет trig_path_prihod(). сейчас в триггре, можно фильтровать "нужные" таблицы: cTable := (nArea)->(dbInfo(DBI_FULLPATH)) и т.д. (сейчас некоторые табл.отсекаю от триггера) FUNCTION my_Trigger( nEvent, nArea, nPos, xTrigVal , nUserStru) может надо добавить в параметры nUserStru т.к. функ. на сервере letoUseArea( nUserStru, cFileName, cAlias, lShared, lReadOnly, cdp ), ... alkresin пишет:Наверное, в коде триггерной функции это можно сделать. функции letoUseArea(...), letoOrdListAdd(...), letoOrdCreate(...) есть, а leto_dbCreate(...) нет. правильно ли использовать dbCreate(...) ?

SergKis: SergKis пишет:функции letoUseArea(...), letoOrdListAdd(...), letoOrdCreate(...) есть, а leto_dbCreate(...) нет. правильно ли использовать dbCreate(...) ? в UDF_Init и TRIGGER dbCreate(...) работает, а letoUseArea(...), ... не работают нет nUserStru в UDF_Init можно создать, открыть таблицу dbUseArea(...), но в TRIGGER она закрыта, а доступны только открытые с клиента. Что и как надо делать, для открытия таблиц(ы) в UDF_Init ? О таблице(ах) клиент ничего не знает.

alkresin: В udf_init() можно только открыть с помощью dbUseArea() для каких-либо операций и здесь же закрыть. О таблице(ах) клиент ничего не знает. Естественно, он знает только о тех таблицах, которые сам открыл. Чтобы добавить nUserStru в параметры вызова триггера, надо полностью изменить реализацию триггеров в letodb сервере. Сейчас все операции по вызову триггерных процедур осуществляются на уровне DBFCDX ( из dbf1.c ), letodb сервер только устанавливает имена процедур, а надо, чтобы он непосредственно вызывал их, тогда он сможет и nUserStru передать. Правда, в этом случае появится новая проблема - триггеры не будут срабатывать из udf-функций, если там манипуляции с таблицей будут осуществляться с помощью прямого использования DBFCDX ( т.е., просто будет: APPEND BLANK, REPLACE... )

SergKis: alkresin пишет:Чтобы добавить nUserStru в параметры вызова триггера, надо полностью изменить реализацию триггеров... Со всем, сказанным, согласен, но ... Если в сервере завести "условного" клиента с nUserStru 0 или 9999 (принадлежащего серверу), то менять работу триггера не надо, можно в UDF_Init, Trigger работать от имени этого клиента, так же как сейчас работаем UDF функцией с клиента - весь набор функций сервера leto_.... доступен .

alkresin: SergKis пишет: Если в сервере завести "условного" клиента с nUserStru 0 или 9999 (принадлежащего серверу) Я обдумывал такой вариант. Он всем хорош, но: триггер запускается в результате операции, производимой клиентом, а действия, производимые в триггерной процедуре, будут исполняться от имени системы, все файлы, используемые в этой процедуре, надо будет открывать/закрывать по-новой ( а если они открыты клиента в exclusive mode ? ). Может, это все и не критично, но подумать еще надо.

SergKis: alkresin пишет:Может, это все и не критично, но подумать еще надо. Согласен. По моим потребностям сегодня, в тригере работаем с табл. уже открыми с клиента (как сейчас) и плюс доп. таблицы, окрытые на сервере, о которых клиент ничего не знает, Несколько открытых в UDF_Init и некоторые табл., открываются при открытии с клиента определенных таблиц, т.е. таблицы не пересекаются (например ведение логов, требует законадательсво). Сегодня это код на клиенте. Если имеем UDF_Init, то надо и UDF_Stop или UDF_Exit (задествовать при перезагрузке letoudf.hrb и завершении letodb).

alkresin: SergKis пишет: Если имеем UDF_Init, то надо и UDF_Stop или UDF_Exit (задествовать при перезагрузке letoudf.hrb и завершении letodb). UDF_Exit - не проблема, а вот с перезагрузкой... Она ведь выполняется в потоке клиента - соответственно, все действия в предполагаемой UDF_Stop и в вызываемой еще раз UDF_Init будут в этом потоке.

SergKis: alkresin пишет: все действия в предполагаемой UDF_Stop и в вызываемой еще раз UDF_Init будут в этом потоке. UDF_Stop (если есть) должен отрабатывать как UDF_Init на сервере перед WrLog( "Server has been closed." ) и возможно при ELSEIF cCommand != NIL .AND. Lower( cCommand ) == "reload", т.е.перед загрузкой new letoudf.hrb UDF_Stop и после relod, UDF_Init из new загруженной hrb. Я такое имел ввиду, но может, что не так понял ? т.е. если в работе был Hash массив, чтобы его сохранить и перечитать, в new версии hrb.

alkresin: SergKis пишет: UDF_Stop (если есть) должен отрабатывать как UDF_Init на сервере перед WrLog( "Server has been closed." ) Ну, вообще-то, эту роль я отвел Udf_Exit() ( уже сделал ) - название, симметричное Udf_Init(), только вызывается он из EXIT процедуры. и возможно при ELSEIF cCommand != NIL .AND. Lower( cCommand ) == "reload" Отсюда его вызывать вряд ли имеет смысл - это ведь другой процесс, другой экземпляр исполняемого модуля сервера, который не имеет доступа к ресурсам работающего сервера. Вот из hs_UdfReload() - можно, он, вроде, будет отрабатывать в основном потоке. Я ошибся, посчитав, что эта функция вызывается в потоке клиента - меня сбил столку код из letofunc.c, где эта функция вызывается в ответ на запрос с клиента udf_rel - но на клиенте такой запрос нигде не формируется. Павел мог бы пояснить, для чего этот фрагмент в letofunc.c.

SergKis: alkresin пишет:эту роль я отвел Udf_Exit() ( уже сделал ) Это здорово. Спасибо. Еще три вопроса:[pre2] 1. AutoMakeDir = [0] \ 1 ; автоматическое создание поддиректорий, при создании hs_dbcreate(...), что бы облегчить код клиента (при переводе с clipper, vo, другой разработчик,...). Уж очень хлопотно ... в 2-х поточной версии сделано и работает: STATIC FUNCTION _CreateDir( cDBF ) LOCAL i, cCt, aCt := {} i := RAt('\', cDBF) cCt := Left( cDBF, i-1) DO WHILE ! hb_DirExists( cCt ) i := RAt('\', cCt) IF i == 0 ; EXIT ENDIF AAdd( aCt , subs(cCt, i+1) ) cCt := Left( cCt, i-1) ENDDO i := Len( aCt ) DO WHILE i > 0 cCt += ('\' + aCt[i--]) MakeDir( cCt ) ENDDO RETURN .T. ... и в hs_createtable( nUserStru, cCommand ) добавить: IF Lower(cName) = "/mem:" // line 620 cDataPath := "" cName := Substr(cName, 2) ELSE cDataPath := leto_GetAppOptions( 1 ) ENDIF cFileName := cDataPath + cName IF ! empty(cDataPath) .and. leto_GetAppOption(<AutoMakeDir>) // что то такое _DirCreate(cFileName) ENDIF BEGIN SEQUENCE WITH { |e|break( e ) } leto_SetUserEnv( nUserStru ) dbCreate( cFileName, aStru, leto_Driver( nDriver ), .T. , cRealAlias ) RECOVER USING oError lres := .F. END SEQUENCE ... [/pre2] 2. Тоже связанный с hs_createtable(). Сегодня клиент (работает c mysql и перевожу на letodb) ничего не знает о некоторых полях записи, которые ведет сервер, + при переводе разных clipper программ на letodb, в структурах возникает дублирование (не по именам, по значениям), которые решить измененнием кода клиента - большой геморой. В связи с этим просьба (при наличии letoudf.hrb) она, может быть востребованна. Ввести UDF_hs_CrateTable(aStru), которой передавать структуру таблицы и возвращать функция должна также модифицированный массив структуры (если такая функция задана в hrb), т.е. в hs_createtable( nUserStru, cCommand ) добавить вызов:[pre2] aStru := Array( nLen, 4 ) // line 600 FOR i := 1 TO nLen aStru[i,1] := GetCmdItem( cCommand, nPos + 1, @nPos ) aStru[i,2] := GetCmdItem( cCommand, nPos + 1, @nPos ) aStru[i,3] := Val( GetCmdItem( cCommand,nPos + 1,@nPos ) ) aStru[i,4] := Val( GetCmdItem( cCommand,nPos + 1,@nPos ) ) NEXT // здесь добавить вызов, если функция есть // aStru := UDF_hs_CrateTable(aStru) cName := StrTran( cName, DEF_CH_SEP, DEF_SEP ) IF Empty( hb_FNameExt( cName ) ) cName += ".dbf" ENDIF [/pre2] 3. Это скорее пожелание. Некоторые функции из UDF перевести в leto_..., есть leto_Sum, leto_groupby и например UDF_getFields --> leto_getFields. Тогда у многих клиентов (организация) не надо устанавливать letoudf.hrb ... , это часто делают сторонние люди (нет удаленного доступа) и могут быть проблеммы (версии, ...) Эти изменения полностью закрывают потребности в функционале letodb, по моему мнению.

SergKis: PS. функция должна получать еще имя таблицы, т.е. UDF_hs_CrateTable(aStru, cName)

Pasha: Вот из hs_UdfReload() - можно, он, вроде, будет отрабатывать в основном потоке. Я ошибся, посчитав, что эта функция вызывается в потоке клиента - меня сбил столку код из letofunc.c, где эта функция вызывается в ответ на запрос с клиента udf_rel - но на клиенте такой запрос нигде не формируется. Павел мог бы пояснить, для чего этот фрагмент в letofunc.c. Если это еще актуально. Запрос на перезагрузку letoudf.hrb выдается не с клиента, а на сервере командой: letodb reload При этом посредством SendMessage сообщение передается службе/демону letodb, и им обрабатывается как раз в этом фрагменте в letofunc.c

alkresin: Я имел ввиду несколько строчек в letofunc.c, начиная с 8267: else if( !strcmp( ptr,"udf_rel" ) ) ... которые, по идее, предназначены для обработки команды с клиента - но в клиентской части такой команды нет.

Pasha: Там еще обрабатываются команды stop, ну и reload тоже

SergKis: Pasha пишет:Если это еще актуально. Запрос на перезагрузку letoudf.hrb выдается не с клиента, а на сервере командой: letodb reload срабатывае пока так: UDF_INIT letodb test UDF_INIT letodb reoload UDF_EXIT letodb stop а зачем сообщение о перезагрузке letoudf.hrb идет в letodb_0.log, а не в основной ? letodb_0.log: 127.0.0.1 04/30/15 15:17:28: letoudf.hrb has been unloaded. 04/30/15 15:17:28: E:\LETO\LetoDb\bin\letoudf.hrb has been loaded.

SergKis: работа триггера сломалась. /* $Id: Changelog,v 1.350.2.236 2015/05/06 14:12:51 ptsarenko Exp $ */ ... letodb.ini [Main] Port = 2812 DataPath = . EnableFileFunc = 1 Trigger = UDF_Test_Trigger letodb.log 05/25/15 10:51:35: Leto DB Server has been started. Leto DB Server v.2.15b3 ! INIT: DataPath=., ShareTables=0, MaxUsers=500, MaxTables=5000, CacheRecords=10 05/25/15 10:51:35: E:\LETO\LetoDb\bin\letoudf.hrb has been loaded. 05/25/15 10:51:35: UDF_INIT // в UDF_Init добавлен WrLog(...) 05/25/15 10:53:37: Send STOP to server... 05/25/15 10:53:40: Server has been closed. letoudf.prg[pre2] FUNCTION UDF_Init /* * This function called immediately after loading letoudf.hrb, if exist */ SET AUTORDER TO 1 WrLog(Procname()) RETURN Nil ... ////////////////////////////////////////////////////////////////// проверка FUNCTION UDF_Test_Trigger( nEvent, nArea, nPos, xTrigVal ) dbInfo(DBI_TRIGGER, .F.) WrLog( hb_valtoexp({ GetTriggerEvent[nEvent], nEvent, nArea, nPos, xTrigVal }) ) // нет вывода dbInfo(DBI_TRIGGER, .T.) RETURN .T. FUNCTION GetTriggerEvent( nEvent ) LOCAL a := { ; 'EVENT_PREUSE ', ; // 1 'EVENT_POSTUSE ', ; // 2 'EVENT_UPDATE ', ; // 3 'EVENT_APPEND ', ; // 4 'EVENT_DELETE ', ; // 5 'EVENT_RECALL ', ; // 6 'EVENT_PACK ', ; // 7 'EVENT_ZAP ', ; // 8 'EVENT_PUT ', ; // 9 'EVENT_GET ', ; // 10 'EVENT_PRECLOSE ', ; // 11 'EVENT_POSTCLOSE ', ; // 12 'EVENT_PREMEMOPACK ', ; // 13 'EVENT_POSTMEMOPACK', ; // 14 'EVENT_ERROR ' ; // } IF nEvent > len(a) .or. nEvent < 1 nEvent := len(a) ENDIF RETURN a[ nEvent ] [/pre2]

SergKis: SergKis пишет:работа триггера сломалась. разобрался. Триггер не работает при создании с открытием. dbCreate(cPath+cFile, aStru, 'LETO', .T., 'MYALIAS') при dbCreate(cPath+cFile, aStru, 'LETO') USE ( cPath+cFile ) NEW ALIAS MYALIAS все нормально с триггером



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