Форум » LetoDB, HbNetio. » Leto DB Server (продолжение 10) (продолжение) » Ответить

Leto DB Server (продолжение 10) (продолжение)

Dima: Продолжаем тут

Ответов - 191, стр: 1 2 3 4 5 6 7 8 9 10 All

SergKis: alkresin, Pasha извините, что достаю ..., но есть предложение добавить в letodb.ini режим AutoMakeDir = [0] \ 1 - автомат. создание директорий при создании таблицы. такой штукой пользуемся ~5 лет - очень упрощет код на клиенте [pre2] FUNCTION hs_createtable( nUserStru, cCommand ) ... IF Lower(cName) = "/mem:" cDataPath := "" cName := Substr(cName, 2) ELSE cDataPath := leto_GetAppOptions( 1 ) ENDIF // IF ! empty(cDataPath) // cFileName := cDataPath + cName // ENDIF // _MakeDir_(cFileName) // // BEGIN SEQUENCE WITH { |e|break( e ) } ... STATIC FUNCTION _MakeDir_( cDBF ) LOCAL i, cCt, aCt := {} i := RAt(DEF_SEP, cDBF) cCt := Left(cDBF, i-1) DO WHILE ! hb_DirExists( cCt ) i := RAt(DEF_SEP, 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 += (DEF_SEP + aCt[i--]) MakeDir( cCt ) ENDDO RETURN .T. [/pre2] возвращаюсь к наличию в letodb аналогов функций hb_fSetDateTime, hb_fGetDateTime. потестировал: [pre2] FUNCTION UDF_fGetDateTime( nUserStru, cFile ) LOCAL dDate, cTime, lFile, cPath := leto_GetAppOptions(1) IF left(cFile, 1) == DEF_CH_SEP cFile := subs(cFile, 3) cFile := subs(cFile, At(DEF_CH_SEP, cFile)) cFile := StrTran(cFile, DEF_CH_SEP, DEF_SEP) cFile := subs(cFile, len(cPath)+1) ENDIF cPath += DEF_SEP lFile := file(cPath+cFile) IF lFile; hb_fGetDateTime(cPath+cFile, @dDate, @cTime) ENDIF RETURN { dDate, cTime } FUNCTION UDF_fSetDateTime( nUserStru, cFile, dDate, cTime ) LOCAL lFile, cPath := leto_GetAppOptions(1) IF empty(dDate); dDate := Date() ENDIF IF empty(cTime); cTime := Time() ENDIF IF ! '.' $ cTime; cTime += '.001' ENDIF IF left(cFile, 1) == DEF_CH_SEP cFile := subs(cFile, 3) cFile := subs(cFile, At(DEF_CH_SEP, cFile)) cFile := StrTran(cFile, DEF_CH_SEP, DEF_SEP) cFile := subs(cFile, len(cPath)+1) ENDIF cPath += DEF_SEP lFile := file(cPath+cFile) IF lFile; hb_fSetDateTime(cPath+cFile, @dDate, @cTime) ENDIF RETURN lFile [/pre2] работает хорошо, но как мне думается, наличие такого механизма в файловой части letodb (режим EnableFileFunc = 1) более правильное решение, которое позволит организовывать зеркальный letodb, получение изменений на localhost, в mysql, FTP, ... без открытия таблиц (по тэгу модификации dbgobottom, получение данного и закрытие таблицы), что упростит код на клиенте конечно вместо 2-х функций можно соорудить одну: leto_fDateTime(cFileName \ aFileName [, dDate \ .T. [, cTime ]]) leto_fDateTime(cFileName \ aFileName) - возвращает {dDate, cTime} или {{dDate, cTime}, ...} leto_fDateTime(cFileName \ aFileName, .T.) - ставит файлу\файлам дату, время сервера leto_fDateTime(cFileName \ aFileName, dDate, cTime) - - ставит файлу\файлам дату, время параметров наличие такого механизма позволит, попробовать использовать skipbuffer(LastRec()) с прочитанным целиком справочником и отключенной очисткой skipbuffer (вместо чтения в массив или memio) сейчас, в 2-х поточной версии letodb, работает механизм с открытием, ..., закрытием таблиц и набралось ~200 таблиц (только опрос - время) и приходится по клиенту размазывать (урезать) список, перед вых. формами весь, перед документом - связанное с этим докум., и .т.д.

alkresin: Вроде работают теперь триггеры, но, учтите, располагаться сами функции должны в letoudf.hrb. Мне не понятно, почему вы все время говорите о letodb.hrb - файл с таким именем нигде в коде сервера не упоминается. SergKis пишет: наверное, правильно, устанавливать "свой" триггер только для нужной области (с клиента) В смысле - для нужной таблицы ? Я тоже так думаю. Установка общего триггера довольно накладна - эта процедура вызывается при всех операциях со всеми таблицами. А вот насчет "с клиента" - не соглашусь. Триггер должен быть определен только на сервере, в этом весь его смысл - чтобы он работал независимо от того, что написано в клиентской программе.

SergKis: alkresin letodb.hrb - это описка, конечно letoudf.hrb А вот насчет "с клиента" - не соглашусь. Триггер должен быть определен только на сервере Здесь, мы друг друга не допоняли. Триггер(ы) определен(ы) ТОЛЬКО на сервере в letoudf.hrb, но активировать (нужный) триггер для нужной таблицы - с клиента, я это имел ввиду говоря "с клиента" Общий триггер, тоже полезная штука - сейчас (в 2-х поточной версии) работает общий и для всех таблиц заполняет поля обязательные (пост был выше) - нет мороки ставить\снимать для таблиц Если выбирать, какой предпочтительней, то по моей ситуации на сегодня лучше общий - переход на новую версию безболезненный Как разграничить алгоритмы ... ? Если в ini задан Trigger - то общий, иначе с клиента, что то типа leto_SetTrigger(...)


SergKis: alkresin пишет:Вроде работают теперь триггеры туплю в SIXNSX было:[pre2] /* * USE command with support for TRIGGER and PASSWORD clauses */ #command USE <(db)> [VIA <rdd>] [ALIAS <a>] [<nw: NEW>] ; [<ex: EXCLUSIVE>] [<sh: SHARED>] [<ro: READONLY>] ; [CODEPAGE <cp>] [INDEX <(index1)> [, <(indexN)>]] ; [TRIGGER <trig>] [PASSWORD <pass>] => ; [Sx_SetTrigger( TRIGGER_PENDING, <trig>, <rdd> ); ] <-trig-> ; [Sx_SetPass( <pass>, 1, <rdd> ); ] <-pass-> ; dbUseArea( <.nw.>, <rdd>, <(db)>, <(a)>, ; if(<.sh.> .or. <.ex.>, !<.ex.>, NIL), <.ro.> [, <cp>] ) ; [; dbSetIndex( <(index1)> )] ; [; dbSetIndex( <(indexN)> )] [/pre2] как ставить ? sele 0 HB_RddInfo( RDDI_PENDINGTRIGGER, 'my_Trigger', 'LETO' ) или HB_RddInfo( RDDI_PENDINGTRIGGER, 'my_Trigger', 'DBFCDX' ) dbUseArea(.F., 'LETO', ...) разъясните, пожалуйста и в чем разница RDDI_PENDINGTRIGGER и RDDI_TRIGGER ?

alkresin: SergKis пишет: Триггер(ы) определен(ы) ТОЛЬКО на сервере в letoudf.hrb, но активировать (нужный) триггер для нужной таблицы - с клиента, я это имел ввиду говоря "с клиента" Насколько я понимаю предназначение триггеров, они должны работать всегда, независимо от того, активирует их или нет клиентская программа. Если вы ставите, например, триггер на добавление записи в table1 - чтобы при этом и в table2 что-то добавлялось, а в table3, например, увеличивался счетчик - то важно, чтобы это работало всегда, иначе пострадает целостность базы данных. Обеспечение целостности данных, целостности связей между таблицами, как я понимаю, одна из важнейших задач триггеров. как ставить ? sele 0 HB_RddInfo( RDDI_PENDINGTRIGGER, 'my_Trigger', 'LETO' ) или HB_RddInfo( RDDI_PENDINGTRIGGER, 'my_Trigger', 'DBFCDX' ) dbUseArea(.F., 'LETO', ...) О чем это вы ? Имя процедуры ведь на сервере в letodb.ini указывается. и в чем разница RDDI_PENDINGTRIGGER и RDDI_TRIGGER ? Честно говоря, понятия не имею.

SergKis: alkresin пишет:О чем это вы ? Имя процедуры ведь на сервере в letodb.ini указывается. Не увидел в текстах, где установка, потому и непонятка возникла, если как было через ini, то пошел пробовать. Спасибо.

alkresin: По поводу RDDI_PENDINGTRIGGER. Он используется в dbf1.c следующим образом: [pre2] if( SELF_RDDINFO( SELF_RDDNODE( &pArea->area ), RDDI_PENDINGTRIGGER, pOpenInfo->ulConnection, pItem ) == HB_SUCCESS ) { if( HB_IS_STRING( pItem ) ) hb_dbfTriggerSet( pArea, pItem ); } if( ! pArea->fTrigger ) { if( SELF_RDDINFO( SELF_RDDNODE( &pArea->area ), RDDI_TRIGGER, pOpenInfo->ulConnection, pItem ) == HB_SUCCESS ) { if( HB_IS_STRING( pItem ) ) hb_dbfTriggerSet( pArea, pItem ); } } [/pre2] Т.е., при открытии таблицы сначала устанавливается он, а затем, если его не удалось установить (а это может произойти, если соответствующая функция не найдена), RDDI_TRIGGER. Мне не очень понятно, зачем это может понадобиться.

SergKis: alkresin Спасибо

finder: Возникла пара вопросов после крайнего обновления. Увидел что добавилась функция Udf_Exit() Если я правильно понимаю: Udf_Init() - вызывается как при запуске сервера так и при загрузке letoudf.hrb Udf_Exit() - вызывается только при остановке сервера, но не при выгрузке letoudf.hrb Есть ли возможность добавить, что-то типа Udf_Unload() - которая будет вызываться при выгрузке letoudf.hrb когда идет команда reload.

nbatocanin: Я тестирую Leto_*Transaction: USE (cPath + "Artikli1") ALIAS Artikli1 SHARED NEW VIA "LETO" USE (cPath + "Artikli2") ALIAS Artikli2 SHARED NEW VIA "LETO" FLock () SELECT Artikli1 Leto_BeginTransaction () GO TOP WHILE !Eof() Artikli2->(DBAppend()) Artikli2->a_naziv := Upper(a_naziv) Artikli2->a_naziv2 := Lower(a_naziv2) Artikli2->a_por := a_por + "ABC" ... SKIP END DO Leto_CommitTransaction () Эта программа работает 73 секунд. Без Begin/EndTransaction работает 15 секунд. Почему это так, и может ли это быть ускорено? Спасибо, Ненад

Dima: nbatocanin Давно я работал с LETO но попробуйте Leto_CommitTransaction () поставить до SKIP а Leto_BeginTransaction () перед Artikli2->(DBAppend())

nbatocanin: Dima пишет: Давно я работал с LETO но попробуйте Leto_CommitTransaction () поставить до SKIP а Leto_BeginTransaction () перед Artikli2->(DBAppend()) Я пробовал, когда это сделать получается 790 секунд :(

Andrey: nbatocanin пишет: Я пробовал, когда это сделать получается 790 секунд :( Попробуйте тесты, которые я сделал и проверял. - https://cloud.mail.ru/public/Lr52/WR9soGyFn TEST_DBF.hbp - терминалка сделанная SergKis. Закачивает базу на сервер супер быстро. Сборка под МиниГуи для терминалки. DbftoServer.hbp - загрузка dbf на сервер. Сборка под МиниГуи. Задание вручную параметров размера буфера транзакций.

Pasha: nbatocanin пишет: Эта программа работает 73 секунд. Без Begin/EndTransaction работает 15 секунд. Почему это так, и может ли это быть ускорено? Спасибо, Ненад Если транзакция большая, то желательно задать параметр для Leto_BeginTransaction([ nBlockLen ]) Это размер памяти в байтах для выделения и перевыделения памяти. Попробуйте задать этот параметр равным 16k, или больше Можно разбить большую транзакцию на несколько маленьких, по количеству добавляемых записей.

digikv: Почему leto_directory бы не вернуться имена каталога на сервере LetoDB? У меня есть 3 каталоги на сервере LetoDB но пример вернуться только один файл (test2.txt) #include "rddleto.ch" Function Main( cPath ) Local lRes, cBuf, arr, i REQUEST LETO RDDSETDEFAULT( "LETO" ) IF Empty( cPath ) cPath := "//127.0.0.1:2812/" ELSE cPath := "//" + cPath + Iif( Right(cPath,1) $ "/\", "", "/" ) ENDIF ? "Connect to " + cPath + " - " IF ( leto_Connect( cPath ) ) == -1 nRes := leto_Connect_Err() IF nRes == LETO_ERR_LOGIN ?? "Login failed" ELSEIF nRes == LETO_ERR_RECV ?? "Recv Error" ELSEIF nRes == LETO_ERR_SEND ?? "Send Error" ELSE ?? "No connection" ENDIF Return Nil ELSE ?? "Ok" ENDIF ? ? 'leto_file( "test1.txt" ) - ' ?? Iif( leto_file( cPath + "test1.txt" ), "Ok", "No" ) ? 'leto_memowrite( "test1.txt", "A test N1" ) - ' ?? Iif( leto_memowrite( cPath + "test1.txt", "A test N1" ), "Ok", "Failure" ) ? 'leto_file( "test1.txt" ) - ' ?? Iif( leto_file( cPath + "test1.txt" ), "Ok", "No" ) ? 'leto_memoread( "test1.txt" ) - ' ?? leto_memoread( cPath + "test1.txt" ) ? 'leto_frename( "test1.txt","test2.txt" ) - ' ?? Iif( leto_frename( cPath + "test1.txt","test2.txt" ) == 0, "Ok", "Failure" ) ? 'leto_file( "test1.txt" ) - ' ?? Iif( leto_file( cPath + "test1.txt" ), "Ok", "No" ) ? 'leto_file( "test2.txt" ) - ' ?? Iif( leto_file( cPath + "test2.txt" ), "Ok", "No" ) ? 'leto_fileread( "test2.txt", 7, 2 ) - ' ?? Iif( leto_fileread( cPath + "test2.txt", 7, 2, @cBuf ) > 0, cBuf, "Failure" ) ? 'leto_filewrite( "test2.txt", 7, "N2" ) - ' ?? Iif( leto_filewrite( cPath + "test2.txt", 7, "N2" ), "Ok", "Failure" ) ? 'leto_memoread( "test2.txt" ) - ' ?? leto_memoread( cPath + "test2.txt" ) ? 'leto_filesize( "test2.txt" ) - ' ?? leto_filesize( cPath + "test2.txt" ) arr := leto_directory( cPath ) ? 'leto_directory(): (' + Ltrim(Str(Len(arr))) + ")" ? "Press any key to continue..." Inkey(0) FOR i := 1 TO Len( arr ) ? arr[i,1] NEXT ? "----------" ? 'leto_ferase( "test2.txt" ) - ' ?? Iif( leto_fErase( cPath + "test2.txt" ) == 0, "Ok", "Failure" ) ? ? "Press any key to finish..." Inkey(0) Return Nil

Pasha: Добавьте при вызове функции 2-й параметр: arr := leto_directory( cPath, "D" ) и массив arr будет включать и имена каталогов.

digikv: спасибо! но опять некоторое время возвращает пустую строку, когда вы запустите программу снова это хорошо

Pasha: Дайте пожалуйста пример

nbatocanin: Pasha пишет: Pasha пишет: Если транзакция большая, то желательно задать параметр для Leto_BeginTransaction([ nBlockLen ]) Это размер памяти в байтах для выделения и перевыделения памяти. Попробуйте задать этот параметр равным 16k, или больше Можно разбить большую транзакцию на несколько маленьких, по количеству добавляемых записей. Извините за задержку. Я пытался изменить этот параметр, и получил небольшое улучшение (69 секунд). Менся здесь действительно не нужно транзакции, я просто хочу, чтобы ускорить группа REPLACE команды. Я думал, что транзакции это сделать.

nbatocanin: Andrey пишет: Попробуйте тесты, которые я сделал и проверял. - https://cloud.mail.ru/public/Lr52/WR9soGyFn TEST_DBF.hbp - терминалка сделанная SergKis. Закачивает базу на сервер супер быстро. Сборка под МиниГуи для терминалки. DbftoServer.hbp - загрузка dbf на сервер. Сборка под МиниГуи. Задание вручную параметров размера буфера транзакций. Очень интересные и полезные примеры! Спасибо!



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