Форум » LetoDB, HbNetio. » Вопросы новичка... » Ответить

Вопросы новичка...

Andrey: Взял последнюю версию с "Новая страница с бинарниками", установил на Win2008 Server, чуток помучился с портом... Собрал на МиниГуи+BCC 551 - \MiniGui\batch\hbmk2.bat rddleto.hbp Получил rddleto.lib - 131072 байт - правильный ли размер ? И еще Warning-и лезут: lib\.hbmk\win\bcc\rddsys.c: source\client\letocl.c: source\client\leto1.c: source\client\letomgmn.c: source\common\blowfish.c: source\common\common_c.c: source\common\hbip.c: TLIB 4.5 Copyright (c) 1987, 1999 Inprise Corporation /P32 lib\rddleto.lib -+ lib\.hbmk\win\bcc\rddsys.obj -+ lib\.hbmk\win\bcc\letocl.obj -+ lib\.hbmk\win\bcc\leto1.obj -+ lib\.hbmk\win\bcc\letomgmn.obj -+ lib\.hbmk\win\bcc\blowfish.obj -+ lib\.hbmk\win\bcc\common_c.obj -+ lib\.hbmk\win\bcc\hbip.objWarning: 'rddsys' not found in library Warning: 'letocl' not found in library Warning: 'leto1' not found in library Warning: 'letomgmn' not found in library Warning: 'blowfish' not found in library Warning: 'common_c' not found in library Warning: 'hbip' not found in library Так должно быть или нет ? Вопрос сразу напрашивается с путями - как писать правильно "\" или "/" : Local cPathServer := "//127.0.0.1:2812/" cPathServer := cPathServer +"DATE_TEST_PATH\test.dbf" Помню что где то обсуждали, а результат не запомнил...

Ответов - 300, стр: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 All

Dima: Пиши "пионера"

SergKis: Andrey пишет:Получил rddleto.lib - 131072 байт - правильный ли размер ? у меня rddleto.lib - 134656 (сечас пересобрал). Собираю: @echo off cls echo ... wait ... set path=e:\minigui\harbour\bin;c:\borland\bcc55\bin; if exist .\bin\letodb.exe del .\bin\letodb.exe > nul if exist .\bin\.hbmk\win\bcc\*.obj del .\bin\.hbmk\win\bcc\*.obj > nul hbmk2.exe -info -comp=bcc -trace rddleto.hbp letodb.hbp > _hbmk2.txt протокол: hbmk2: Autodetected platform: win hbmk2: Using Harbour: e:\minigui\harbour\bin e:\minigui\harbour\include e:\minigui\harbour\lib e:\minigui\harbour\bin e:\minigui\harbour\contrib e:\minigui\harbour\addons hbmk2: Using C compiler: c:\borland\bcc55\bin\bcc32.exe hbmk2: Target up to date: lib\rddleto.lib hbmk2: Autodetected platform: win hbmk2: Using Harbour: e:\minigui\harbour\bin e:\minigui\harbour\include e:\minigui\harbour\lib e:\minigui\harbour\bin e:\minigui\harbour\contrib e:\minigui\harbour\addons hbmk2: Using C compiler: c:\borland\bcc55\bin\bcc32.exe hbmk2: Compiling Harbour sources... hbmk2: Harbour compiler command (embedded): (e:\minigui\harbour\bin\harbour.exe) -n2 source\server\server.prg -n -w -q0 -es2 -D__BM -D__WIN_SERVICE__ -obin\.hbmk\win\bcc\ -ic:\borland\bcc55\Include -ie:\minigui\harbour\include -iinclude hbmk2: Compiling... hbmk2: C/C++ compiler command: bcc32.exe -c -q -CP437 -d -O2 -OS -Ov -Oc -Oi -6 -D__WIN_SERVICE__ -D__BM -tW -tWM -w -Q -w-sig- -nbin\.hbmk\win\bcc -Ic:\borland\bcc55\Include -Ie:\minigui\harbour\include -Iinclude bin\.hbmk\win\bcc\server.c bin\.hbmk\win\bcc\errorsys.c bin\.hbmk\win\bcc\letobm.c bin\.hbmk\win\bcc\common.c bin\.hbmk\win\bcc\letoserv.c source\server\errint.c source\server\leto_win.c source\server\letoacc.c source\server\letovars.c source\server\letofunc.c source\server\letolist.c source\server\leto_2.c source\common\blowfish.c source\common\common_c.c bin\.hbmk\win\bcc\server.c: bin\.hbmk\win\bcc\errorsys.c: bin\.hbmk\win\bcc\letobm.c: bin\.hbmk\win\bcc\common.c: bin\.hbmk\win\bcc\letoserv.c: source\server\errint.c: source\server\leto_win.c: Warning W8004 source\server\leto_win.c 116: 'State' is assigned a value that is never used in function leto_ServiceControlHandler source\server\letoacc.c: source\server\letovars.c: source\server\letofunc.c: Warning W8012 source\server\letofunc.c 1348: Comparing signed and unsigned values in function leto_CheckClientVer source\server\letolist.c: source\server\leto_2.c: source\common\blowfish.c: source\common\common_c.c: hbmk2: Linking... bin\letodb.exe hbmk2: Linker command: ilink32.exe @C:\Users\MASTE_~1\AppData\Local\Temp\5eqzvd.lnk hbmk2: Linker script: -Gn -Tpe -Lc:\borland\bcc55\Lib;c:\borland\bcc55\Lib\PSDK;e:\minigui\harbour\lib -aa c0w32.obj bin\.hbmk\win\bcc\server.obj bin\.hbmk\win\bcc\errorsys.obj bin\.hbmk\win\bcc\letobm.obj bin\.hbmk\win\bcc\common.obj bin\.hbmk\win\bcc\letoserv.obj bin\.hbmk\win\bcc\errint.obj bin\.hbmk\win\bcc\leto_win.obj bin\.hbmk\win\bcc\letoacc.obj bin\.hbmk\win\bcc\letovars.obj bin\.hbmk\win\bcc\letofunc.obj bin\.hbmk\win\bcc\letolist.obj bin\.hbmk\win\bcc\leto_2.obj bin\.hbmk\win\bcc\blowfish.obj bin\.hbmk\win\bcc\common_c.obj, bin\letodb.exe, nul, rddbm.lib hbextern.lib hbdebug.lib hbvmmt.lib hbrtl.lib hblang.lib hbcpage.lib gtcgi.lib gtpca.lib gtstd.lib gtwin.lib gtwvt.lib gtgui.lib hbrdd.lib hbuddall.lib hbusrrdd.lib rddntx.lib rddcdx.lib rddnsx.lib rddfpt.lib hbrdd.lib hbhsx.lib hbsix.lib hbmacro.lib hbcplr.lib hbpp.lib hbcommon.lib kernel32.lib user32.lib gdi32.lib advapi32.lib ws2_32.lib iphlpapi.lib winspool.lib comctl32.lib comdlg32.lib shell32.lib uuid.lib ole32.lib oleaut32.lib mpr.lib winmm.lib mapi32.lib imm32.lib msimg32.lib wininet.lib hbpcre.lib hbzlib.lib cw32mt.lib import32.lib, , Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland

andrey: Dima пишет: Пиши "пионера"

andrey: Есть чуть меньше 100 файлов справочников (весит меньше мегабайта). Нужно периодически, два-четыре раза в месяц стаскивать эти справочники на локальную машину. Вопрос, как лучше сделать, чтобы быстро это операция проходила ? 1) Вариант открывать каждую базу справочника на LetoDB и через транзакцию записывать на локальную машину. 2) Вариант копировать каждый файл справочника через LETO_MEMOREAD() и записывать на локальную машину. Только как правильно можно реализовать 2-ой вариант ? Подскажите пожалуйста.

Dima: andrey пишет: цитата: Пиши "пионера"

Andrey: Теперь понял !

alkresin: Andrey пишет: 1) Вариант открывать каждую базу справочника на LetoDB и через транзакцию записывать на локальную машину. 2) Вариант копировать каждый файл справочника через LETO_MEMOREAD() и записывать на локальную машину. Наверное, второй вариант быстрее. Впрочем, попробуйте, сравните. В первом случае транзакции не помогут - они предназначены для записи данных. Для буферизации чтения после открытия каждого файла на сервере ставьте leto_setSkipBuffer( количество_записей_в_буфере ). Для второго варианта cBuf := leto_memoRead( путь/имя_файла) hb_memowrit( путь/имя_файла, cBuf )

Andrey: alkresin пишет: Для второго варианта cBuf := leto_memoRead( путь/имя_файла) hb_memowrit( путь/имя_файла, cBuf ) Спасибо БОЛЬШОЕ ! Буду пробовать этот вариант, мне он кажется проще. А какой максимальный размер можно передать через cBuf ? А если надо несколько (10) больших баз из сотни полей и общим весом 150-200 Мб переносить. Как быть в этом случае ? Может там их на сервере zip-пом сжать (будет примерно 3 Мб), перетащить архив как файл, а уже на локальной машине распаковать и уже добавлять в локальные базы.

Dima: Andrey пишет: Может там их на сервере zip-пом сжать (будет примерно 3 Мб), перетащить архив как файл, а уже на локальной машине распаковать и уже добавлять в локальные базы. Пробовать надо Андрей.

alkresin: Andrey пишет: А какой максимальный размер можно передать через cBuf ? Насколько я помню, ограничений нет, но все в пределах разумного... Может там их на сервере zip-пом сжать ... Вообще говоря, не вижу большого смысла использовать letodb для этих процедур. Если надо просто время от времени переписывать группу файлов, почему не использовать ftp или rsync, предназначенные именно для подобных целей.

Andrey: alkresin пишет: не вижу большого смысла использовать letodb для этих процедур Понятно. Просто эти функции хотелось бы сделать как дополнение. А так основные базы буду переделывать под letodb.

Andrey: Сделал чтение/запись справочников через файловые функции LETO_MemoRead/LETO_MemoWrite. Тестовая база: 1) 70 файлов примерно общий весом 1 Мб. 2) Старенький нетбук Самсунг 10 дюймов экран, WinXP Home, сеть WiFi 54 Мб 3) Интернет WiFi - через сотовый телефон оператор МТС 4) Местонахождение - комп в г.Севастополе, сервер LetoDB в Москве Скорость передачи всех файлов МЕНЬШЕ минуты !!! Классно !!! Это я еще ставлю INKEGUI(100) для красоты прорисовки бегунка...

SergKis: Andrey пишет:Сделал чтение/запись справочников через файловые функции Сделай Browse\TsBrowse на них (с ведением) и гонять никуда не надо. Проверено деревня под Питером (Билаин, МТС) клиент, сервер в Риге.

Andrey: SergKis пишет: Сделай Browse\TsBrowse на них (с ведением) и гонять никуда не надо. Это проба. Browse\TsBrowse чуть позже буду делать, пока отдельную задачу реализовать надо, загрузка базы на локальную машину. Пока пробую, привыкаю...

Andrey: Сделал копирование баз с LetoDB на локальную машину. 10 файлов общим размером 136 Мб копируются на локальную машину за 2 мин. 33 сек. Вот это скорость.... Нафиг нужны всякие fpt и другое если можно сделать самому для юзера. Всё бросаю и перехожу на LetoDB !!!

Andrey: Использую пока у себя гибрид: 1) главное меню МиниГуи (кнопочки, рюшечки ...), по кнопке вызывается 2) exe-ник терминалка (таблицы пока все в терминалке). Терминалку конечно же переделаю, но времени на это много нужно. Хочу сделать у себя отправку заказов сразу на LetoDB, т.е. правится таблица заказов на терминалке, передает НОМЕР ЗАПИСИ которая правилась юзером в главную программа на МиниГуи. А главная программа отправляет эту запись в LetoDB. Вопрос такой - как организовать передачу номера записи между своими программами ? По таймеру не хочу... Программа многопользовательская.

SergKis: Andrey пишет:Вопрос такой - как организовать передачу Можно посмотреть в сторону сообщения WM_COPYDATA смотри: SAMPLES\Applications\DbfView\source\userfun.prg SAMPLES\Applications\RunCmd\RunCmd.prg как сделать посылку из WVT не скажу (надо смотреть), мои такие изыскания закончились 2-мя вариантами, т.к. менять wvt модуль надо полюбому, то вполне можно: 1. коннект с лето перенести туда и запись перебросить уже не проблемма (это практически одна ф-я с параметрами) 2. запускать на run (с отрывом) прогу, которая делает тоже самое в фоне

SergKis: Andrey пишет:Вопрос такой - как организовать передачу Можно посмотреть в сторону сообщения WM_COPYDATA смотри: SAMPLES\Applications\DbfView\source\userfun.prg SAMPLES\Applications\RunCmd\RunCmd.prg как сделать посылку из WVT не скажу (надо смотреть), мои такие изыскания закончились 2-мя вариантами, т.к. менять wvt модуль надо полюбому, то вполне можно: 1. коннект с лето перенести туда и запись перебросить уже не проблемма (это практически одна ф-я с параметрами) 2. запускать на run (с отрывом) прогу, которая делает тоже самое в фоне

Andrey: SergKis пишет: 2. запускать на run (с отрывом) прогу, которая делает тоже самое в фоне У меня есть уже есть в фоне главная программа. Как туда передать НОМЕР измененной записи и как принять это сообщение ? SAMPLES\Applications\RunCmd\RunCmd.prg посмотрел и ни фига не понял...

Haz: Andrey пишет: Как туда передать НОМЕР измененной записи и как принять это сообщение ? глянь MiniGUI\SAMPLES\Advanced\HMGTALK\ PS. там клиенты общаются через свою программу- сервер. При желании можно позаимствовать идею

Andrey: Andrey пишет: Сделал копирование баз с LetoDB на локальную машину. 10 файлов общим размером 136 Мб копируются на локальную машину за 2 мин. 33 сек. Вот это скорость.... Сделал обратную процедуру копирование ОДНОЙ базы на LetoDB (сервер где-то в Москве). База с мемо полями, общий вес 2,5 Мб - кол-во полей 12, из них 3 мемо поля. Кол-во записей 5000. Время копирования на LetoDB - 8 мин. А если будет 15000 записей ? Блин... скорость не та... Пробовал делать через транзакции, программа виснет глухо, потом развисает. База копируется, только поведение программы у юзеров будет Что делать ? Выгрузка на LetoDB нужна один раз для первоначальной работы, ну может и с некоторой периодичностью. Не будешь же сидеть и ждать когда базы зальются у юзеров...

Dima: Andrey пишет: Время копирования на LetoDB - 8 мин что то долго. какова скорость инета ?

Andrey: Dima пишет: что то долго. какова скорость инета ? Хорошее ! Это в коде что-то не так сделано. Доделаю пример выложу.

PSP: Подключение не ADSL случаем?

Andrey: PSP пишет: Подключение не ADSL случаем? Обычное ! Быстрое... Скачивание с сайта на ура, а запись на сервер - тормоза. Что-то в коде не указываю наверное...

Pasha: Может там их на сервере zip-пом сжать (будет примерно 3 Мб), перетащить архив как файл, а уже на локальной машине распаковать и уже добавлять в локальные базы. Достаточно добавить в server.prg строку: Request __Run и можно будет добавить в letoudf.prg функцию, которая бы вызывала внешний архиватор, и возвращала бы клиенту тело упакованного файла. Но такой способ мне не нравится, так как у клиентов появится возможность с помощью letodb запускать на сервере что угодно.

Dima: Pasha пишет: Request __Run А нельзя разве добавить request hb_zipfile и пожать средствами Harbour минуя внешний архиватор ?

Andrey: Pasha пишет: Но такой способ мне не нравится, так как у клиентов появится возможность с помощью letodb запускать на сервере что угодно Сделать для админа, т.е. только для разработчика программ ! Клиентам такой возможности не давать. Т.е. создание архива и распаковка только программисту. Dima пишет: А нельзя разве добавить request hb_zipfile и пожать средствами Harbour минуя внешний архиватор ? Вот, вот внутренним архиватором - средствами Harbour !

Pasha: Наверное, так и сделаю. Добавлю к серверу letodb модуль letozip.prg Модуль будет необязательный, и, если его подключить при сборке сервера, будет добавлен соответствующий функционал.

Andrey: Pasha пишет: Наверное, так и сделаю. Добавлю к серверу letodb модуль letozip.prg Ура !!! Pasha пишет: Модуль будет необязательный, и, если его подключить при сборке сервера Обязательным ! Зачем подключать, добавка к коду небольшая. Всем нужно будет потом, все оценят.... Можно будет архивы (картинки, .doc, .xls ) zip-ованые пересылать ! И примерчик небольшой С БЕГУНКОМ как использовать ! Спасибо БОЛЬШОЕ !

Andrey: Всем привет ! Опять уперся в стену. Сделал программу на МиниГуи - скачивание баз с ЛетоДБ просто быстрый ! Делаю обратную программу загрузку ОДНОЙ базы на ЛетоДБ - тормоза. Ну думаю неправильно сделал, ан нет. Спасибо ОГРОМНОЕ SergKis за помощь, он сделал классную тестовую программу. Просто на Харборе - терминалка, считает мемо поля для размера буфера транзакции и моя база просто загружается ВЛЁТ ! Вот тесты: Transaction No //18.15.100.50:1234/test/TestCopy.dbf Recno:4283 Время загрузки базы - 00:04:52 Transaction Yes // расчет длины строки базы по мемо полям и по структуре базы: {8, 8, 4, 25, 129, 187, 362, 40, 45, 40, 1, 10} // длина записи - 860 //кол-во записей для расчета буфера транз. - 1000 // кол-во строк в буф. транз. - 700 // размер буфера транзакции - 860000 //18.15.100.50:1234/test/TestCopy.dbf Recno:4283 Время загрузки базы - 00:00:02 За 0,2 сек. база загружается на сервер.... Просто супер ! Делал и другие тесты, рабочая база 80 Мб с кучей мемо-полей загружается за 1 мин. 24 сек. Компилировал исходник Харбором из поставки МиниГуи - последним, Harbour 3.2.0dev (r1504082220), библиотеку LetoDB (letodb-2.15-b3.src.zip) тоже компилировал этим Харбором. Сервер поставил готовый как службу из letodb-2.15-b3.bcc.zip Переходим теперь к самой программе собранной на Harbour MiniGUI Extended Edition 2.4.6 - 2015.04.22 Она не особо большая, несколько менюшек, выгрузка для ЛетоДБ - ехе-ник размером 4 Мб. Выгрузка базы без транзакции на МиниГуи занимае 8 мин.12 сек., хотя у тестовой программы 04:52, алгоритм одинаков. Причем бегунок при загрузке замирает периодически... Но программа не вешается. Перетащил алгоритм загрузки с ТРАНЗАКЦИЯМИ к себе заново, думал - полетит.... Ага, обломись... С такими же параметрами как у тестовой программы база загружается... 5 минут ... и программа периодически просто вешается (выскакивает в поле окна Программа не отвечает)... Пробовал по совету SergKis уменьшать буфер транзакции, время выгрузки чуток увеличивается, и программа чуток меньше вешается... Короче полный облом.... Кто пробовал делать загрузку файлов на сервер LetoDB на МиниГуи ? Может дело в МиниГуи ? Поделитесь опытом, куда копать ?

Pasha: Добавил на сервере letodb 2 функции: leto_Zip и leto_UnZip Собирать сервер надо командой: hbmk2 -env:__ZIP=yes letodb.hbp Как использовать эти функции - я написал в инструкции. Андрей, а из-за чего весь сыр-бор ? Загрузка базы на сервер - это разовая операция, и какая разница, будет она выполняться очень быстро или просто быстро ? letodb - это все-таки прежде всего СУБД, и зачем его перегружать несвойственными СУБД действиями ?

Andrey: Pasha пишет: Андрей, а из-за чего весь сыр-бор ? Да большая непонятка, на голом Харборе ВСЁ быстро, как переделываем на МиниГуи становиться МЕДЛЕННО.... Что там влияет... Ну хоть бы не за 0,2 сек но не за 5 минут же... Это при базе всего в 5000 записей. А если будет 40000 то всё, можно идти обедать. Согласен, разовая операция загрузка базы, боюсь как бы в другом месте тормоза не вылезли потом. Нельзя там посмотреть, что такого в МиниГуи делается, что задача на транзакциях вешается ? Может я неправильно собрал саму либу ? Я про это уже спрашивал. Делал так \MiniGui\batch\hbmk2.bat rddleto.hbp Размер либы - 131072 Содержимое build.log lib\.hbmk\win\bcc\rddsys.c: source\client\letocl.c: source\client\leto1.c: source\client\letomgmn.c: source\common\blowfish.c: source\common\common_c.c: source\common\hbip.c: TLIB 4.5 Copyright (c) 1987, 1999 Inprise Corporation /P32 lib\rddleto.lib -+ lib\.hbmk\win\bcc\rddsys.obj -+ lib\.hbmk\win\bcc\letocl.obj -+ lib\.hbmk\win\bcc\leto1.obj -+ lib\.hbmk\win\bcc\letomgmn.obj -+ lib\.hbmk\win\bcc\blowfish.obj -+ lib\.hbmk\win\bcc\common_c.obj -+ lib\.hbmk\win\bcc\hbip.objWarning: 'rddsys' not found in library Warning: 'letocl' not found in library Warning: 'leto1' not found in library Warning: 'letomgmn' not found in library Warning: 'blowfish' not found in library Warning: 'common_c' not found in library Warning: 'hbip' not found in library Где можно взять готовую и проверенную rddleto.lib на тесты ?

Dima: Andrey пишет: Делал так \MiniGui\batch\hbmk2.bat rddleto.hbp Размер либы - 131072 Andrey пишет: на голом Харборе ВСЁ быстро Либа то та же если я верно понял !? Если так , то она тут не причем. Andrey пишет: Нельзя там посмотреть, что такого в МиниГуи делается Что то в GUI фейсе делаешь не так , коли тормоза. По идее скорость не должна сильно отличаться а в идеале должна быть одинакова что в консоли что в GUI. Andrey пишет: Нельзя там посмотреть, что такого в МиниГуи делается, что задача на транзакциях вешается ? Внутри транзакции надеюсь ты не воткнул обновление бегунка ?

Andrey: Dima пишет: Внутри транзакции надеюсь ты не воткнул обновление бегунка ? Нет конечно. Dima пишет: Либа то та же если я верно понял !? Да, собирал и консоль и МиниГуи с одной либой.

Pasha: Есть смысл переделать загрузку/выгрузку БД на сервер letodb через архивные операции, поскольку их поддержка уже сделана. Это будет самый быстрый способ.

alkresin: Андрей, убирать бегунок не пробовал ? Просто чтобы проверить, как он влияет на время ?

Andrey: alkresin пишет: Андрей, убирать бегунок не пробовал ? Совсем убирал. Так же подвисает и столько же времени проходит. Дело всё таки в МиниГуи наверно. Сейчас доделаю тест и выложу, может что-то я не так делаю. Pasha пишет: Есть смысл переделать загрузку/выгрузку БД на сервер letodb через архивные операции, поскольку их поддержка уже сделана. Это будет самый быстрый способ. Спасибо БОЛЬШОЕ !!!

Andrey: Пытаюсь пробовать TsBrowse из Минигуи со связкой LetoDb. Как лучше организовать просмотр базы на LetoDB по условию ? Допустим в базе есть числовое поле KOD, я построил индекс на локальном компе по этому полю (можно потом будет держать на сервере, это пока не важно). Как задать показ в TsBrowse записи допустим по KOD==1 ? Я всегда делал по условному индексу, было моментально. Хочу послушать как другие делают. Можно с помощью фильтра: cExp := "KOD==1" DbSetFilter( &("{||" + cExp + "}"), cExp ) Что посоветуете ?

Pasha: Как лучше организовать просмотр базы на LetoDB по условию ? Допустим в базе есть числовое поле KOD, я построил индекс на локальном компе по этому полю (можно потом будет держать на сервере, это пока не важно). Как задать показ в TsBrowse записи допустим по KOD==1 ? Надо использовать scope

Andrey: Вот сделал тестовые программы - https://cloud.mail.ru/public/Lr52/WR9soGyFn TEST_DBF.hbp - терминалка сделанная SergKis, я чуток подправил. Закачивает базу на сервер супер быстро. Сборка под МиниГуи для терминалки. DbftoServer.hbp - загрузка dbf на сервер. При dbf без мемо-полей, тоже летает. Как появляются мемо-поля, то всё - кранты на сервер качает ну просто с ТОРМОЗАМИ. Если сервер стоит локально (//127.0.0.1:2812/test/), то ТОМОЗОВ НЕТ !!! Сборка под МиниГуи. DbfFromServer.hbp - скачивает dbf с сервера. Быстро скачивает, есть мемо-поля, или нет - скорость хорошая. Сборка под МиниГуи. Тестовые базы тоже в архиве. Папка /test/ - на сервере должна быть обязательна. Ну не знаю еще как выделить из пути (//127.0.0.1:2812/test/) папку для файлов.... Библиотеку rddleto.lib не выкладываю. Просьба потестить примеры, может кто подскажет как ускорить загрузку dbf с мемо-полями ? Вариант загрузки баз через архивные операции - буду пробовать. А какой буфер для файловых операций использовать MAX можно ? А то файл на 150 мб комп то прочитает, а сервер его примет ?

Pasha: DbftoServer работает медленно с транзакциями или без ? Конечно, надо использовать транзакции.

Andrey: Pasha пишет: DbftoServer работает медленно с транзакциями или без ? Работает (просто тормоза) с транзакциями. Программа просто подвисает и не регирует. Потом развисает, до окончания следующей транзакции. В качестве примера загрузить с транзакцией Test2.dbf (без мемо полей) - будет очень быстро, а потом загрузить TestCop2.dbf ( с мемо полями) - ну просто тормоза... Загружать нужно не локально (//127.0.0.1:2812/test/), локально тоже быстро работает. Хотя простой (терминалка) пример test_dbf.prg загружает базу с мемо-полями - просто на ура, очень быстро ! Алгоритм один и тот же. Харбор тоже одинаков. Разница: терминалка и МиниГуи.

Pasha: Прогнал тест с транзакциями: Локально - 3 сек По сетке - 6 сек Если без транзакций: Локально - 13 сек По сетке - 29 сек

Andrey: Pasha пишет: Прогнал тест с транзакциями: По сетке может будет и быстро. Я гонял тесты на внешний сервер. Даже на 2 внешних сервера. Результат тормоза...

PSP: Андрей, есть утилита iperf. Она позволяет очень объективно оценить канал связи между двумя компьютерами. Если есть доступ к управлению удаленными серверами, проверь с помощью этой утилиты эффективность канала. Может соединение получается медленное. В инете полно инфы. А так, вот простой пример: http://windowsnotes.ru/programs/izmeryaem-skorost-seti-programmoj-iperf/

Andrey: PSP пишет: Может соединение получается медленное. Спасибо БОЛЬШОЕ ! Буду тестить, а то действительно может и не там грабли нашёл....

Andrey: Установил последний LetoDb (с zip) на другой сервер. Прогнал тесты и вот результаты: База 60тыс.записей с кучей мемо-полей общим размером 118Мб загружается на сервер в 85 транзакций за 2:30 Я в ауте... Установил первый сервер LetoDB и попал на Тот тест который долго проходил на первом сервеое LetoDB из 5000 записей и размером 3.3 Мб за 5 минут проходит на новом сервере всего за 8 транзакции - 2 сек. Вот такие тесты.... Все проблемы в установке первого сервера, хотя он и работает нормально. Правда порт там длинный (30200), и нельзя ввести это число в утилиту manage.exe ! Просьба увеличить кол-во знаков ввода в утилиту manage.exe. Как бороться/настроить Win2008 под моим первым LetoDB ? Кто может подсказать, что там нужно подкрутить ?

Dima: Andrey пишет: Просьба увеличить кол-во знаков ввода в утилиту manage.exe. Есть сырец http://www.kresin.ru/down/letodb/letodb-2.15-b3.src.zip , исправь и собери. Andrey пишет: Тот тест который долго проходил на первом сервеое LetoDB из 5000 записей и размером 3.3 Мб за 5 минут Чем отличаются сервера что такая большая разница ?

Andrey: Dima пишет: Есть сырец http://www.kresin.ru/down/letodb/letodb-2.15-b3.src.zip , исправь и собери. Да нет у меня hwgui, не установлен. Да и другие тоже потом с этим (недостаток знака) столкнуться. Первый сервер в Москве на Win2008 Sever порт 30200 Второй в моём городе на Win2012 Server порт 2812 Сегодня-завтра попробую установить сервер на Linux'e у знакомого. Не знаю почему такая большая разница. Понял что дело не в LetoDB, а в самом сервере на котором он стоит. Само LetoDB - отличная база ! Спасибо авторам !!!

Dima: Номер порта не при чем я думаю. Возможно файер встроенный что то блочит или еще что.

Andrey: Сервер LetoDb установлен на CentOS release 6.5 (Final) - сам сервак на clodo.ru 1) База 5000 записей и размером 3.3 Мб проходит за 8 транзакции - 2 сек. 2) База 60 тыс.записей с кучей мемо-полей общим размером 118Мб загружается на сервер за 85 транзакций - 4:19 программа чуток подтормаживает, но всё равно классная скорость. Вывод однозначен, в ТОРМОЗАХ при загрузке базы - виновата сама машина первого сервера, а не LetoDB.

Pasha: Andrey пишет: Вывод однозначен, в ТОРМОЗАХ при загрузке базы - виновата сама машина первого сервера, а не LetoDB. Установлен ли в letodb.ini на сервере параметр Optimize = 1 Желательно его установить.

Andrey: Pasha пишет: Установлен ли в letodb.ini на сервере параметр Optimize = 1 Желательно его установить. Нет не установлен. Установлю и попробую. Спасибо !

Pasha: Просьба увеличить кол-во знаков ввода в утилиту manage.exe. Увеличил

Andrey: Pasha пишет: Увеличил Спасибо Большое !

Andrey: Dima пишет: Понял что дело не в LetoDB, а в самом сервере на котором он стоит. Финиш... Не трогал первый сервер несколько дней, решил попробовать отправить заново тест DbftoServer.ехе с танзакциями. И о чудо тест прошёл за 2 сек. Пример пересобирал с уже новой библиотекой, с zip-ом (т.е. последняя). Библиотеку собирал через Харбор из последнего МиниГуи. Библиотека собралась без ошибок. Думаю дай попробую старую библиотеку ( letodb-2.15-b3.src.zip с сайта http://www.kresin.ru/letodb.html ) Пересобралась библиотека с предупреждениями: lib\.hbmk\win\bcc\rddsys.c: source\client\letocl.c: source\client\leto1.c: source\client\letomgmn.c: source\common\blowfish.c: source\common\common_c.c: source\common\hbip.c: TLIB 4.5 Copyright (c) 1987, 1999 Inprise Corporation /P32 lib\rddleto.lib -+ lib\.hbmk\win\bcc\rddsys.obj -+ lib\.hbmk\win\bcc\letocl.obj -+ lib\.hbmk\win\bcc\leto1.obj -+ lib\.hbmk\win\bcc\letomgmn.obj -+ lib\.hbmk\win\bcc\blowfish.obj -+ lib\.hbmk\win\bcc\common_c.obj -+ lib\.hbmk\win\bcc\hbip.objWarning: 'rddsys' not found in library Warning: 'letocl' not found in library Warning: 'leto1' not found in library Warning: 'letomgmn' not found in library Warning: 'blowfish' not found in library Warning: 'common_c' not found in library Warning: 'hbip' not found in library И пример с этой библиотекой пять ушёл в ТОРМОЗА - закачка файла 6:24 !!! Вернул новую библиотеку rddleto.lib, опять пересобрал пример. И облом... Сервак теперь не работает ! Пример теперь уже новый тормозит на закачке базы с транзакциями. После работы, буду перегружать сервер... Одни непонятки... А как получить номер версии библиотеки клиента rddleto.lib ? Почему на сервере лог-файл читаешь написано: Leto DB Server v.2.15b3, а у себя в программе запрашиваешь LETO_GETSERVERVERSION() - пишет Leto DB Server v.2.15 ?

Pasha: Одни непонятки... А параметр Optimize = 1 установлен ? и сервер letodb после этого перегружался ? Надо сделать стоп-старт для службы letodb А как получить номер версии библиотеки клиента rddleto.lib ? Вызвать dbInfo(DBI_RDD_VERSION)

Andrey: Pasha пишет: А параметр Optimize = 1 установлен ? Да, установил сразу же. Стоп-старт тоже было сделано ! Pasha пишет: Вызвать dbInfo(DBI_RDD_VERSION) Сделал: #include "rddleto.ch" #include "letocdp.ch" #include "dbinfo.ch" cMsg := "Соединение для " + cPathServer + " - УСПЕШНО !;;" cMsg += " Номер коннекта:" + HB_NtoS(LETO_GETCURRENTCONNECTION()) + ";" cMsg += " Версия сервера:" + LETO_GETSERVERVERSION() + ";" cMsg += "IP адрес клиента:" + LETO_GETLOCALIP() + ";" cMsg += " Путь клиента:" + LETO_PATH() + ";" cMsg += " Версия клиента:" + dbInfo(DBI_RDD_VERSION) + ";" <<-- 66 строка Выдаёт ошибку: Error DBCMD/2001 Файл не открыт: DBINFO Called from DBINFO(0) Called from MYLETODB(66) Что не так делаю ?

Pasha: dbInfo требует открытой рабочей области, вестимо

Andrey: Pasha пишет: dbInfo требует открытой рабочей области, вестимо Т.е. не открыв базу, я не могу узнать версию клиента LetoDB ? А если по умолчанию будет стоять драйвер DBFCDX ? Как тогда я узнаю версию клиента LetoDB ? Очень неудобно. А нельзя сделать функцию типа LETO_GETCLIENTVERSION() ?

Pasha: Да, неудобно то, что у функции rddInfo() нет соответствующей команды, и приходится использовать dbInfo. А для dbInfo неважно, какой драйвер стоит по умолчанию. Надо, чтобы вызов был из рабочей области, которая использует нужный rdd Отдельную функцию - конечно, можно добавить

Andrey: Pasha пишет: Отдельную функцию - конечно, можно добавить Спасибо БОЛЬШОЕ ! Проблема при создании индекса. Делаю так: MsgDebug(cIndex,cTag,cFileIndx,cFilterTo) INDEX ON &cIndex TAG(cTag) TO (cFileIndx) FOR &cFilterTo ADDITIVE В окне перед созданием индекса вывожу имя файла cFileIndx -> D:\TEMP\tmp_base_leto_1.cdx А на создании индекса программа рушиться и пишет: Error LETO/1006 Ошибка создания: D:\LETODB.Server\D:\TEMP\tmp_base_leto_1.cdx (DOS Error 123) Как задать создание индекса на локальном компе ? Или на сервере LetoDB, можно как то задавать ? Т.е. база на LetoDB, а индекс строю на локальном компе (он временный, на один раз).

Pasha: Локально индекс не создается. А для создания индекса на сервере надо указать путь относительно DataPath

Andrey: Спасибо Pasha !

Andrey: Создал 3 индекса на сервере. Как переключаться на них ? Вот код который перестал работать: SELECT WTOVAR DBSETORDER(1) GOTO TOP SEEK(nKod) DO WHILE WTOVAR->KOD == nKod .... SKIP ENDDO Подскажите пожалуйста как переделать код для LetoDB !

Pasha: Как бы не видно сам код. Как индекс создается, остается ли он открытым, непонятно.

Andrey: Pasha пишет: Как индекс создается, остается ли он открытым, непонятно. Индексные файлы на сервере создаются (визуально вижу). База и индексы открыты. Может дело еще в одной базе, более подробный код: SELECT WSPISOK // база локальная открыта DBFCDX монопольно DO WHILE !EOF() ProcessMessages() // Чтобы форма НЕ замирала nKod := WSPISOK->KOD SELECT WTOVAR DBSETORDER(1) GOTO TOP SEEK(nKod) // MsgDebug( FOUND() ) DO WHILE WTOVAR->KOD == nKod // расчёт .... SKIP ENDDO SELECT WSPISOK WSPISOK->KOD1 := ... // запись расчётов SKIP ENDDO До переделки под Leto - этот код считал. Нашёл - SEEK(nKod) перестал работать !!! FOUND() Всегда возвращает .F. Почему ? Чем можно заменить ?

Pasha: Понятнее не стало. В цикле: DBSETORDER(1) GOTO TOP SEEK(nKod) DO WHILE WTOVAR->KOD == nKod ... Кстати, go top здесь лишний. Можно проверить, что за индекс открыт. После DBSETORDER(1) выдать ? OrdKey() ? OrdName() ? OrdFor() и тому подобное

Dima: Всего кода я конечно не видел Andrey пишет: SELECT WSPISOK Возможно после нужен WSPISOK->(Dbgotop()) Если это рабочий код а не просто пример его надо переделать. [pre2] WSPISOK->(Dbgotop()) DO WHILE !WSPISOK->(EOF()) ProcessMessages() // надеюсь тут нет смены Алиаса ? nKod := WSPISOK->KOD WTOVAR->(DBSETORDER(1)) // лучше вынести до DO WHILE !WSPISOK->(EOF()) если там где ...не меняется ордер If WTOVAR->(dbSEEK(nKod)) WTOVAR->(dbOrderInfo(DBOI_SCOPEBOTTOM,,,nkod)) DO WHILE !WTOVAR->(eof()) .... WTOVAR->(DbSKIP()) ENDDO WTOVAR->(dbOrderInfo(DBOI_SCOPEBOTTOMCLEAR)) // упс я забыл добавить...забил се мозк Minigui ;) endif WSPISOK->KOD1 := ... // запись расчётов WSPISOK->(DBSKIP()) ENDDO [/pre2]

Andrey: Pasha пишет: Кстати, go top здесь лишний. Согласен. Но на всякий случай... Можно проверить, что за индекс открыт. После DBSETORDER(1) SELECT WTOVAR DBSETORDER(1) SEEK(nKod) lRet := FOUND() MsgDebug(lRet,nMaster,ALIAS(), OrdKey(), OrdName(), OrdFor() ) DO WHILE WTOVAR->KOD == nKod Поставил. И сразу обнаружил фигню: Возвращает: F, 33, WTOVAR, "STR(KOD)+STR(KVIP)", "tovarvip", "!DELETED()" Наверно где то в коде по другому срабатывает... У меня есть в коде "STR(KOD)+STR(KVIP)", но эта выборка идёт после выбора из Tbrowsa oBrw1 после расчета количества KOD ! Там вообще другой prg и строиться новое окно с Tbrowse oBrw3. cFileTag := PATH_TEMP + "tmp_tovar_leto_tag.cdx" DEFINE WINDOW Form_Tov3 ; .... SELECT WTOVAR DBSetOrder( 1 ) goto TOP // файл для SCOPE cTag := 'tovarvip' cKey := 'STR(KOD)+STR(KVIP)' lUnq := .F. //OrdCreate( cFileTag, cTag, cKey, &( '{||'+cKey+'}' ), lUnq) OrdCreate( , cTag, cKey, &( '{||'+cKey+'}' ), lUnq) DEFINE TBROWSE oBrw3 ; ...... cFileTag использовал для локальной отладки, а потом убрал вообще. Ничего не понимаю, как вновь созданный индес может менять своё значение "KOD" на "STR(KOD)+STR(KVIP)"...

Andrey: Dima пишет: Если это рабочий код а не просто пример его надо переделать. Рабочий ! В отдельной программе работает. Dima пишет: ProcessMessages() // надеюсь тут нет смены Алиаса ? Стандартная функция минигуи DO EVENTS Не меняет алиас.

Andrey: Блин..... Нашёл в папке чужой индексный файл, который совпадает с именем базы.... Ларчик открывается вообще просто.... Терпеть не могу это автооткрытие индексов !!! Опять попал на старые Всегда отключал его и делал имена файлов индексов отличные от имени базы! Как для LetoDB отключить открытие индексов автоматом вместе с базой ? На всякий случай....

Andrey: Pasha пишет: выдать ? OrdKey() ? OrdName() ? OrdFor() и тому подобное Приведи пожалуйста еще функции какие можно использовать для Leto... А то от стандартного CDX перестали работать.

Dima: Andrey пишет: Как для LetoDB отключить открытие индексов автоматом вместе с базой ? Ни как , обсуждали уже. И Павел отказался это исправлять.

Andrey: Dima пишет: Ни как , обсуждали уже. И Павел отказался это исправлять. Понял, Спасибо !

Andrey: Как пройтись по папкам на LetoDB и получить список всех файлов вида "/папка/папка/файл" ? Сделал код, но он кривоватый, не учитывает вложенные папки... aDir := {} aDir0 := Leto_Directory( M->cPubPathLeto , "D" ) aFile0 := Leto_Directory( M->cPubPathLeto + "*.*" ) IF LEN(aDir0) > 0 FOR nI := 1 TO LEN(aDir0) cPath := aDir0[nI,1] IF cPath == "." .OR. cPath == ".." ELSE aFile := Leto_Directory( M->cPubPathLeto + cPath + "/*.*" ) FOR nJ := 1 TO LEN(aFile) AADD( aDir, "/"+cPath + "/" + aFile[nJ,1] ) NEXT ENDIF NEXT ENDIF IF LEN(aFile0) > 0 FOR nI := 1 TO LEN(aFile0) AADD( aDir, aFile0[nI,1] ) NEXT ENDIF aFile := ASORT(aDir)

Dima: Andrey http://clipper.borda.ru/?1-4-0-00001051-000-0-0-1427848859

Andrey: Спасибо Dima ! То что надо !

Andrey: Сделал еще 2 проекта-теста на МиниГуи. Копирование файлов через Leto_MemoRead(), Leto_MemoWrit() и создание zip-архива с отправкой на LetoDB. Исправил 2 предыдущих проекта. Общий архив - https://cloud.mail.ru/public/GiWn/cEYHCiBVT Комментарии и критика приветствуются !

Andrey: alkresin пишет: Andrey пишет: цитата: А какой максимальный размер можно передать через cBuf ? Насколько я помню, ограничений нет, но все в пределах разумного... У меня получается передавать 3-5 Мб через cBuf по интенету. В локалке наверно можно и больше. Какой размер нужно передавать чтобы на всех машинах (включая и слабые компы) не вылетала программа ? Если файл размером 29Мб - программа не вылетает, только не копирует файл и всё. Может кто еще попробует... cBuff := HB_MemoRead(cFile) - сколько может считать эта функция ? Какое ограничение по размеру нужно ставить при копировании файла ?

alkresin: Попробуйте определять размер файлов с помощью leto_FileSize() и потом, если надо, передавать его по частям c помощью leto_Fileread()

Andrey: alkresin пишет: Попробуйте определять размер файлов с помощью leto_FileSize() и потом, если надо, передавать его по частям c помощью leto_Fileread() А какой размер МАХ для передачи нужно делать ? И еще вопрос: как определить по базе - кем она открыта: DBFCXD или LETO ?

Pasha: И еще вопрос: как определить по базе - кем она открыта: DBFCXD или LETO ? Надо выдать leto_mgGetTables(), и затем искать в полученном массиве, открыта ли таблицы на сервере letodb.

Andrey: Pasha пишет: Надо выдать leto_mgGetTables(), и затем искать в полученном массиве, открыта ли таблицы на сервере letodb. Спасибо !

Andrey: Выходит ошибка при старте локального сервера (Win8.1) Leto DB Server v.2.15b3 : 06/18/15 11:44:18: Server has been closed. 06/18/15 11:44:29: LetoDB service has had some problems: 1063 Что за ошибка ? Через службы сервер запускается.

Andrey: Не работает мой код для Leto на нескольких серверах, работает только на одном. Что делать, куда "копать" ? Имею сервер Leto DB Server v.2.15b3 - letodb.exe 998912 04.06.15 установлен на всех компах. 1) Windows Server 2008 - код работает ТОЛЬКО на этом сервере. 2) Windows Server 2012 R2 - код не работает. 3) Windows 8.1 Prof - сервер локальный - код не работает. Код примерно такой: SELECT WZAIV nSelZaiv := SELECT() DBSETORDER(1) SELECT WMASTER nSelMast := SELECT() DO WHILE ! (nSelMast)->( EOF() ) aCalc := { 0, 0, 0, 0, 0, 0, 0 } nMaster := WMASTER->KMASTER SELECT WZAIV DBSETORDER(1) // это на всякий случай GOTO TOP // убирал для пробы - всё без разницы SEEK(nMaster) lRet := FOUND() //MsgDebug(lRet,nMaster,ALIAS(), OrdKey(), OrdName(), OrdFor() ) DO WHILE WZAIV->KMASTER == nMaster IF WZAIV->KDATE == 1 aCalc[1]++ .......... SKIP ENDDO SELECT WMASTER WMASTER->ZPR := aCalc[1] ............ SELECT(nSelMast) dbSkip(1) ProcessMessages() // ОБЯЗАТЕЛЬНО ! ENDDO Не работает SEEK(nMaster) lRet := FOUND() lRet для компа 2) и 3) возвращает .F. Ладно бы на всех не работало, это я бы на косячил, но на ОДНОМ 1) же работает ....

Pasha: dbSeek() не найдет ключ в следующих случаях: 1. Открыт не тот индекс 2. Такого ключа в индексе нет 3. Ключ в индексе есть, но он не попадает в установленный фильтр/scope вроде все. Еще, конечно, может быть битый индекс.

Andrey: Pasha пишет: Еще, конечно, может быть битый индекс. Я же его делаю каждый раз, после открытия базы. Значит битым не может быть. Буду делать отдельный тест тогда...

Pasha: alkresin пишет: Попробуйте определять размер файлов с помощью leto_FileSize() и потом, если надо, передавать его по частям c помощью leto_Fileread() Я смотрю, в hbip.c есть такой фрагмент: [pre2] /* Set internal socket send buffer to 64k, * this should fix the speed problems some users have reported */ hb_ipSetBufSize( hSocket, 0xFFFFFF, 0xFFFFFF ); [/pre2] где задается размер буфера для передачи. Надо полагать, что данные все равно передаются фрагментами по 64k ? Кстати, в чем там задается размер ? 0xFFFFFF это вроде не 64k

Andrey: Pasha пишет: где задается размер буфера для передачи. Надо полагать, что данные все равно передаются фрагментами по 64k ? Кстати, в чем там задается размер ? 0xFFFFFF это вроде не 64k Видно сложный вопрос, раз нет ответа... Очень жалко...

Andrey: Сделал тестовый пример. Алгоритм простой - выборка из базы по SEEK и SKIP - суммирование по 7 полям и запись этих 7 полей в локальную базу на компе пользователя. Итого 13 раз нужно просчитать по базе. Убрал лишний вывод на экран. Наверно тормоза МиниГуи были из-за него. Теперь скорость просто чумовая: База 100 000 записей с мемо-полями: ~120 Мб Расчет по простому индексу 13 позиций: 1) локальная DBFCDX - 00:00:00 2) локальная LETO - 00:00:01 3) интернет LETO - 00:01:49 База 1 000 000 записей с мемо-полями: ~1,2 Гб Расчет по простому индексу 13 позиций: 1) локальная DBFCDX - 00:00:06 2) локальная LETO - 00:00:09 3) интернет LETO - 00:18:15 Открыть в TBROWSE миллион записей из базы, которая находиться неизвестно где (на просторах интернета) - это просто пипец (как говорит Аллочка из Универа) Вывод: связка Harbour + LetoDB + MiniGui - отличная вещь в работе. Это при том, что используется простой старый алгоритм, без переделки под Leto ! 2 вопроса к форумчанам: 1) Протестируйте кто нибудь по сетке это пример. Ну нет у меня сетки под рукой. Там путь просто набить и будет работать. 2) Как улучшить алгоритм для Leto ? Пример тут - https://cloud.mail.ru/public/DkoH/nSzvMbeCh

Andrey: Как проверить на сервере LetoDB - подключена ли функция LTON ?

Pasha: Например, задать фильтр: set filter to LTON(<lExpr>)==1 и затем вызвать: leto_IsFiltOptim() Если вернет .t. - значит, функция выполняется на сервере

Andrey: Pasha пишет: set filter to LTON(<lExpr>)==1 и затем вызвать: leto_IsFiltOptim() Если вернет .t. - значит, функция выполняется на сервере Что-то сложноватая проверка.... А нет ли более простой ? По типу hb_IsFunction( cFunc ) Программу ставишь и помнить надо - есть на сервере та или иная функция. Затратно как то. А так у себя в коде поставил проверку - нет такой функции на сервере и уже понятней станет. Это сейчас, когда ставишь сервер, помнишь, а потом забудется и будешь потом

Pasha: Должна сработать функция (хотя она немного не для этих целей): leto_UdfExist("LTON") или (для соединения, которое не является текущим): leto_UdfExist("//localhost:2812/LTON")

Andrey: Pasha пишет: leto_UdfExist("LTON") Сработало ! Спасибо ! Pasha пишет: Можно еще добавить перед внешним циклом: leto_SetSkipBuffer(<побольше>) Тогда и он будет выполняться быстрее, так как запросов к серверу будет меньше. А сколько <побольше> ? Диапазон подскажи...

Pasha: Прикидывайте сами. Размер буфера зависит только от размера и содержимого записей. Запись передается подпакованная, без лишних пробелов. Пусть размер записи 100 байт. Передаваемый с сервера размер (он непостоянный) - пусть будет 40 байт. Тогда, если установлен leto_SetSkipBuffer(1000), размер буфера с содержимым 1000 записей будет примерно 40000 байт. Из этих соображений и надо исходить. Соображение примерно те же, что и при передаче больших файлов. leto_SetSkipBuffer устанавливается для каждой рабочей области отдельно. Перед большим циклом его можно установить большим, а после выполнения цикла - вернуть стандартное значение: leto_SetSkipBuffer(10) Для циклов, внутри которых выполняется обновление данных с блокировкой каждой записи, skip-буфер сбрасывается, поэтому его увеличивать нет смысла.

PSP: Паша, а сколько времени "живет" этот буфер? 1 сек? С какого момента отсчет начинается? Что произойдет, если это время закончится раньше, чем закончится выборка в цикле? Что произойдет, если это время закончится в момент передачи уже выбранных данных с сервера клиенту?

Pasha: Да, 1 секунда. По окончании которой данные с сервера будут запрашиваться повторно. Отсчет времени идет после получения данных с сервера, так что время отклика сервера для проверки актуальности буфера роли не играет.

PSP: Вот в такой обычной конструкции увеличение чего нужно использовать: skip- или seek- буфера? Или и того, и другого? DBSeek( <...> ) WHILE <...> .... DBSkip() END

Pasha: Для [индексно]последовательной выборки (а это как раз она) надо использовать skip-буфер. Для индексной, а это seek, причем seek по частичному ключу не катит, надо использовать seek-буфер. Пример - ну скажем, есть список абонентов с адресами, которые выбираются по справочнику населенных пунктов и улиц. Если для этих справочников задействовать seek-буфер, то наиболее часто используемые элементы справочника окажутся в seek-буфере, и в конце концов число обращений к ним (операций seek) резко уменьшится.

PSP: Всё понятно. Спасибо за объяснение.

Andrey: Использую bat-файл с командой: "C:\Program Files\CVSNT\cvs.exe" -d:pserver:anonymous@letodb.cvs.sourceforge.net:/cvsroot/letodb co -P -r rel-1-mt letodb При старте выдаёт окно с ошибкой: Но потом вроде нормально загружает обновления. Так должно быть или нет ?

PSP: Andrey в заголовке окна слово "advert" по-моему явно указывает на рекламу.

Andrey: PSP пишет: в заголовке окна слово "advert" по-моему явно указывает на рекламу. Да кто же знает, что это за хрень... Раздражает, вот и спрашиваю.

Andrey: Пытаюсь собрать сервер Лето по инструкции: Для сборки сервера с поддержкой функций leto_Zip и leto_Unzip необходимо установить макрос __ZIP: hbmk2 -env:__ZIP=yes letodb.hbp Нужно подключить функцию из tools: LTON на сервере letodb Для этого надо в source\server.prg добавить строку: request lton в letodb.hbp добавить строку: -lhbct и пересобрать сервер letodb Вот letodb.hbp # # $Id: letodb.hbp,v 1.8.2.6 2015/05/26 15:01:20 ptsarenko Exp $ # #-env:__BM=yes -env:__ZIP=yes -inc -obin/letodb -iinclude -n -w -q0 -es2 #-warn=max -prgflag={unix}-D__LINUX_DAEMON__ -prgflag={unix}-D__LINUX__ {__BM}-prgflag=-D__BM #-prgflag={win}-D__WIN_DAEMON__ -prgflag={win}-D__WIN_SERVICE__ -cflag={allmsvc}-D_CRT_SECURE_NO_DEPRECATE -cflag={win}-D__WIN_SERVICE__ {__BM}-cflag=-D__BM -gui -mt {__BM}-lrddbm {__ZIP}-lhbziparc {__ZIP}-lhbmzip {__ZIP}-lminizip -lhbct source/server/server.prg source/server/errorsys.prg source/server/errint.c {unix}source/server/leto_lnx.c {win}source/server/leto_win.c source/server/letoacc.c source/server/letovars.c source/server/letofunc.c source/server/letolist.c source/server/leto_2.c {__BM}source/server/letobm.prg {__ZIP}source/server/letozip.prg source/common/blowfish.c source/common/common_c.c source/common/common.prg {__BM}source/common/letoserv.prg В make_b32.bat прописал: @echo off SET HB_PATH=Z:\MiniGUI\Harbour Сделал.... Затем запустите make_b32.bat, и исполняемый файл сервера letodb.exe будет создан в каталоге bin/, а библиотека rdd rddleto.lib - в каталоге lib/. Не создаётся.... Вот make_b32.log MAKE Version 5.2 Copyright (c) 1987, 2000 Borland bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\letocl.obj source\client\letocl.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\client\letocl.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\leto1.obj source\client\leto1.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\client\leto1.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\letomgmn.obj source\client\letomgmn.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\client\letomgmn.c: Warning W8060 source\client\letomgmn.c 742: Possibly incorrect assignment in function HB_FUN_LETO_SETSEEKBUFFER bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\hbip.obj source\common\hbip.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\common\hbip.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\common_c.obj source\common\common_c.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\common\common_c.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\blowfish.obj source\common\blowfish.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\common\blowfish.c: Z:\MiniGUI\Harbour\bin\harbour.exe -iinclude;Z:\MiniGUI\Harbour\include -n -q0 -w -es2 -gc0 -d__WIN_SERVICE__ source\client\rddsys.prg -oobj\b32\rddsys.c bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\rddsys.obj obj\b32\rddsys.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland obj\b32\rddsys.c: IF EXIST "lib\rddleto.lib" del "lib\rddleto.lib" > NUL TLIB "lib\rddleto.lib" /0 @MAKE0000.@@@ TLIB 4.5 Copyright (c) 1987, 1999 Inprise Corporation +obj\b32\letocl.obj & +obj\b32\leto1.obj & +obj\b32\letomgmn.obj & +obj\b32\hbip.obj & +obj\b32\common_c.obj & +obj\b32\blowfish.obj & +obj\b32\rddsys.obj & + IF EXIST "lib\leto.lib" del "lib\leto.lib" > NUL TLIB "lib\leto.lib" /0 @MAKE0001.@@@ TLIB 4.5 Copyright (c) 1987, 1999 Inprise Corporation +obj\b32\letocl.obj & +obj\b32\hbip.obj & +obj\b32\common_c.obj & +obj\b32\blowfish.obj & + Z:\MiniGUI\Harbour\bin\harbour.exe -iinclude;Z:\MiniGUI\Harbour\include -n -q0 -w -es2 -gc0 -d__WIN_SERVICE__ source\server\server.prg -oobj\b32\server.c bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\server.obj obj\b32\server.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland obj\b32\server.c: Z:\MiniGUI\Harbour\bin\harbour.exe -iinclude;Z:\MiniGUI\Harbour\include -n -q0 -w -es2 -gc0 -d__WIN_SERVICE__ source\common\common.prg -oobj\b32\common.c bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\common.obj obj\b32\common.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland obj\b32\common.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\letofunc.obj source\server\letofunc.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\server\letofunc.c: Warning W8012 source\server\letofunc.c 1348: Comparing signed and unsigned values in function leto_CheckClientVer bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\letolist.obj source\server\letolist.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\server\letolist.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\leto_2.obj source\server\leto_2.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\server\leto_2.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\letoacc.obj source\server\letoacc.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\server\letoacc.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\letovars.obj source\server\letovars.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\server\letovars.c: bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\leto_win.obj source\server\leto_win.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\server\leto_win.c: Warning W8004 source\server\leto_win.c 104: 'State' is assigned a value that is never used in function leto_ServiceControlHandler Warning W8004 source\server\leto_win.c 262: 'dwWaitTime' is assigned a value that is never used in function HB_FUN_LETO_SERVICEDELETE bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\errint.obj source\server\errint.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland source\server\errint.c: Z:\MiniGUI\Harbour\bin\harbour.exe -iinclude;Z:\MiniGUI\Harbour\include -n -q0 -w -es2 -gc0 -d__WIN_SERVICE__ source\server\errorsys.prg -oobj\b32\errorsys.c bcc32 -c -Iinclude;Z:\MiniGUI\Harbour\include -d -tWM -D__WIN32__ -D__WIN_SERVICE__ -oobj\b32\errorsys.obj obj\b32\errorsys.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland obj\b32\errorsys.c: IF EXIST "bin\letodb.exe" del "bin\letodb.exe" > NUL bcc32 @MAKE0002.@@@ Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland Turbo Incremental Link 5.66 Copyright (c) 1997-2002 Borland Error: Unresolved external '_HB_FUN_LTON' referenced from Z:\LETODB CVS\LETODB\OBJ\B32\SERVER.OBJ ** error 1 ** deleting bin\letodb.exe Библиотека есть - Z:\MiniGUI\Harbour\lib\hbct.lib Что не так делаю ?

Pasha: make_b32 собирает только клиентскую библиотеку, по правилам из makefile.bc Сервер (да и желательно клиент) надо собирать с помощью hbmk2 Прмерно так: set path=%path%;\bcc55\bin <hbpath>\hbmk2 -env:__ZIP=yes letodb.hbp <hbpath>\hbmk2 rddleto.hbp

Andrey: Понял. Спасибо ! Я просто по доке читал и слепо выполнял, что там написано.

Andrey: Павел. Собрал по твоим рекомендациям алгоритм. Скорость просто потрясающая ! Тестирование АЛГОРИТМА-2 только для LETO. База в интернете, сервер - Leto DB Server v.2.15b3 Суммирование по 7 полям - 13 раз, т.е. обращение к серверу производиться всего 13 раз. Расчет по базе 100 000 записей (~120 Мб) - 00:00:01 Расчет по базе 1 000 000 записей (~1,2 Гб) - 00:00:08 Для сравнения. Расчет по локальной базе на DBFCDX - 6 сек.

Andrey: Делаю так: // ---------- по @aRetCalc вернуть массив расчёта ---------------- lResult := Leto_udf('UDF_Test', cAliasMain, nOrd, aMast, @aRetCalc ) MsgDebug(aRetCalc) // возвращает {} А в UDF_Test делаю создание массива aRetCalc и запись в него значений. Не работает возврат по ссылке. Или не должно работать ?

Pasha: Переменные по ссылке не поддерживаются. Надо возвращать сам массив.

Andrey: Понял. Спасибо !

Andrey: Разобрался наконец то с UDF-функциями. Сделал 3-ий алгоритм. Тестирование АЛГОРИТМА-3 только для LETO. База в интернете, сервер - Leto DB Server v.2.15b3m1 (m1-моя модификация) Расчёт по базе происходит на сервере, клиенту возвращается результат через массив. Расчет по базе на сервере 100 000 записей (~120 Мб) - 00:00:00 Расчет по базе на сервере 1 000 000 записей (~1,2 Гб) - 00:00:05 Для сравнения, расчет по АЛГОРИТМУ-1 по локальной базе (локальный комп) из 1 000 000 записей на DBFCDX - 6 сек. Пример и описание как делать (в конце файлов исходников Algorithm2.prg, Algorithm3.prg), здесь - https://cloud.mail.ru/public/6Tw4/JGGSA941Q Комментарии и критика приветствуется.

Andrey: Сделал еще отдельный тест на сервере 5 000 000 записей (~5,9 Гб) База в интернете, сервер - Leto DB Server v.2.15b3m1 Расчёт по АЛГОРИТМУ-2 - 00:00:34 Расчёт по АЛГОРИТМУ-3 - 00:00:26 Для сравнения, расчет по АЛГОРИТМУ-1 по локальной базе (локальный комп) из 5 000 000 записей на DBFCDX - 00:15:17

Pasha: Запись в базу: WMASTER->ZPROSR := aResult[nI,2] WMASTER->ZNEVIP := aResult[nI,3] WMASTER->KV1 := aResult[nI,4] WMASTER->KV2 := aResult[nI,5] WMASTER->KV3 := aResult[nI,6] WMASTER->KV4 := aResult[nI,7] WMASTER->KV5 := aResult[nI,8] тоже можно делать на сервере в функции udf_test. Будет еще быстрее.

Andrey: Pasha пишет: тоже можно делать на сервере в функции udf_test Так база WMASTER на локальном компе находиться. Специально созданная для временного показа.

Andrey: Имею сайт на MySql и базу LETODB на одном сервере. С MySql не работал вообще, из-за этого и спрашиваю. Можно ли сделать UDF-функцию которая бы открывала базу на MySql и копировала запись с базы LETODB ? Какие подводные камни могут быть ?

alex_II:

Andrey: alex_II пишет: Будет ли letoDB работать на Windows 8.1 x64? Да будет ! Даже он уже работает на Windows Server 2012 64bit !

alex_II: Спасибо, сегодня попробую

alex_II: Пр сборке создаются 2 библиотеки, одна (rddleto.lib) добавляется в клиентскую часть, а про вторую (leto.lib) ничего не сказано. Подскажите пожалуйста для чего нужна leto.lib ?

PSP: alex_II пишет: Подскажите пожалуйста для чего нужна leto.lib ? 2013-12-13 20:40 UTC+0300 Alexander Kresin (alex/at/belacy.belgorod.su) + source/client/letocl.c + added pure C leto client layer. Together with common_c.c, hbip.c and blowfish.c it will be linked to leto.lib - the client library for letodb. It doesn't contain Harbour related code and can be used in applications, written on different languages.

alex_II: Понял. Спасибо

Pasha: Andrey пишет: Имею сайт на MySql и базу LETODB на одном сервере. С MySql не работал вообще, из-за этого и спрашиваю. Можно ли сделать UDF-функцию которая бы открывала базу на MySql и копировала запись с базы LETODB ? Какие подводные камни могут быть ? Можно, конечно, собрать сервер letodb с клиентом mysql. Но, КМК, этого делать не стоит. Это приведет к возможной нестабильности сервера. Все-таки это не функции letodb. Лучше все делать обычным способом: либо с клиента копировать данные с одного сервера БД на другой (пусть физически сервера БД располагаются на одной машине), либо на сервере запускать харбор-программу, которая локально коннектилась бы и к letodb, и к mysql, и копировала бы данные.

Andrey: Спасибо Pasha !

Andrey: Как смотреть логи letodb.log и letodb_0.log у себя на компе, если не имею доступа к серверу ? И как сделать удалённо перезапуск сервера ?

Pasha: Никак, и первое, и второе. Но если сам сервер расположен внутри папки, указанной в letodb.ini - DataPath, то логи можно смотреть через файловые операции letodb

PSP: Andrey пишет: И как сделать удалённо перезапуск сервера ? Можно, к примеру, использовать программу sc.exe https://support.microsoft.com/ru-ru/kb/251192

Sergy: Andrey пишет: Как смотреть логи letodb.log и letodb_0.log у себя на компе, если не имею доступа к серверу ? И как сделать удалённо перезапуск сервера ? Если там нет RDP - какой тогда смысл поднимать на нем какую-бы то ни было БД ?

Andrey: Sergy пишет: Если там нет RDP - какой тогда смысл поднимать на нем какую-бы то ни было БД ? 1) Под Windows Server: Админ сети не хочет давать мне удалёнку... 2) Под Linux : Сервер расположен на сайте у провайдера, там своя специфика которую не знаю...

Sergy: Если админ не хочет "давать удаленку" - пусть сам с БД и муздыкается. Если он умеет удалять гланды через задницу - пусть делает. По поводу сервера у провайдера - вообще тогда непонятно, как он разрешил запускать посторонние демоны, но не разрешает управление ими.

Softlog86: Если Сервер обрабатывает задачи не только вашего офиса(организации) , тогда понятна упрямость админа . И вообще , "кто он такой?" . Sergy всё верно написал . нет RDP - нет поддержки . А ссылка на то что такие БД и программные средства должны быть надёжны и обходиться без возможной отладки в виде перезагрузки и прочих действий с Сервером ..... ну так и стоимость таких программ в разы выше да и не факт что будет функциональнее "самодельных" :)

SergKis: Если админ даст запускать letodb не как сервис, можно попробовать написать менеджер над лето сервером, используя режимы запуска letodb.exe test - запуск сервера letodb.exe stop - останов сервера letodb.exe reload - перезагрузка letoudf.hrb сервера и, например, наличие в доступном каталоге (тем же лето переписать) файла letodb.new - замена версии сервера, т.е. letodb.exe stop Rename letodb.new, letodb.exe letodb.exe start для letoudf.hrb сделать похожий механизм. а log журналы читать в массив и возвращать на клиента в udf функции. Конечно, с RDP проще, но ... (у нас так работает версия лето 0.9 ~ 4 года)

Andrey: Pasha пишет: то логи можно смотреть через файловые операции letodb Нашёл чем смотреть: start dbedit -f -letodb=//127.0.0.1:2812/ Классная вещь !

Andrey: Собрал сервер с опцией -env:__ZIP=yes Как сделать следующее: 1) Проверить есть ли функция ZIP в составе сервера ? 2) Распаковка переданного zip-файла в папку на LetoDb ? 3) Упаковка файла в zip-архив на сервере ?

Pasha: 1. leto_UDFExist("leto_Zip") 2. leto_UDF("leto_UnZip", [cDirName], cZip, ...) где cZip - содержимое архива 3. leto_UDF("leto_Zip", [cDirName], ...) функция вернет содержимое архива остальные параметры соответствуют параметрам функций hb_ZipFile/hb_UnZipFile.

Andrey: Спасибо Pasha !

Andrey: Pasha пишет: leto_UDF("leto_Zip", [cDirName], ...) функция вернет содержимое архива А если файл будет большой ? Сколько по максимуму в Мб может вернуть файл ? Какое ограничение для себя нужно знать ?

Pasha: Мы об этом уже говорили, когда обсуждали ограничение на размер файлов при их передаче. Здесь дело обстоит точно так же. Ограничения на размер архива нет, но все в пределах разумного.

Andrey: Pasha пишет: Мы об этом уже говорили, когда обсуждали ограничение на размер файлов при их передаче. Честное слово не запомнил максимальную цифру для передачи файла ! Я запомнил типа в "пределах разумного". Напомните пожалуйста.

Pasha: Ограничения на размер файла нет

Andrey: Всем привет. Тестирую сервер на медленном соединение... Не очень приятно видеть как TBROWSE медленно перерисовывает окно и саму таблицу. Есть ли возможность определять в своем коде скорость соединения ? А уже из этой скорости строить по другому прорисовку....

Pasha: Andrey пишет: Не очень приятно видеть как TBROWSE медленно перерисовывает окно и саму таблицу. TBrowse забрасывает сервер запросами RecCount. Чтобы избежать этого, после соединения с сервером надо задать настройку: RddInfo( 101, .F.,, 1 ) // RDDI_REFRESHCOUNT И конечно, размер skip-буфера надо определить не меньше, чем количество строк в TBrowse. После таких телодвижений число запросов к серверу сократится в десятки раз, что положительно скажется на скорости. Можно попробовать открыть файл через dbedit на медленном соединении, там все эти настройки учтены.

Andrey: Pasha пишет: TBrowse забрасывает сервер запросами RecCount. Чтобы избежать этого, после соединения с сервером надо задать настройку: Я уточняю - TBROWSE из МиниГуи ! Я имею ввиду свой пример TestCalcDbf.exe из LetoDB_2. Я такой высылал тебе. Сделал настройку для него - стало чуток веселей .... Pasha пишет: И конечно, размер skip-буфера надо определить не меньше, чем количество строк в TBrowse. Сделал - еще до этого... IF M->nPubDriver == 1 // Leto nSkip := oBrw3:nRowCount() // кол-во строк показа LETO_SETSKIPBUFFER( nSkip ) RddInfo( 101, .F.,, 1 ) // RDDI_REFRESHCOUNT ENDIF Pasha пишет: Можно попробовать открыть файл через dbedit на медленном соединении, там все эти настройки учтены. Пробовал, скорость открытия тоже медленная - 15 записей на экране открывает по 0,6 сек, видна перерисовка каждой стоки. Но потом шустро ходит по ячейкам в таблице. В МиниГуи у меня очень вальяжно ходит курсор...

SergKis: Andrey Попробуй Browse с двойной буферизацией, возможно будет быстрее. Проверь за сколько будет отбор 100,500,1000 записей (задавать руками) и организовывать в массив или memio отбор и потом вешать tbrowse. По кнопкам next, prev получать еще порцию и просматривать ограниченное число записей. Можно попробовать "ручное" управление skip буф. (временем сброса\чтения), но Паша может объяснит лучше последовательность команд, я не пробовал.

Dima: SergKis Сергей у него TS , поэтому 2-ой буферизации там нет в принципе.

SergKis: Dima Я знаю, но предлагаю пробнуть, но мои тесты на медленном соединении подсказывают, что если и изменится прорисовка, то немного.

Andrey: SergKis пишет: но мои тесты на медленном соединении подсказывают Как у себя в программе определить что соединение МЕДЛЕННОЕ ? Чтобы потом юзеру повесить табличку - что с таким интернетом работать не будет !!! Да и себе понять ....

Pasha: Как у себя в программе определить что соединение МЕДЛЕННОЕ ? Если средствами letodb - то можно выдать на сервер команду leto_memoread(), если на сервере есть файл приличного размера, который можно прочитать, или leto_memowrite(), и засечь время ответа с сервера. По этому времени можно и оценивать скорость соединения.

Dima: Andrey пишет: Как у себя в программе определить что соединение МЕДЛЕННОЕ ? Еще как вариант можно заюзать HB_PING , сырец я выкладывал. Время так же можно засечь до него и после (можно и в цикле сделать от 1 до 10 к примеру ) .

Andrey: Dima пишет: Еще как вариант можно заюзать HB_PING , сырец я выкладывал. Время так же можно засечь до него и после (можно и в цикле сделать от 1 до 10 к примеру ) . Давай заново выложи пожалуйста....

Dima: Andrey пишет: Давай заново выложи пожалуйста.... Да без проблем

PSP: Всё это, т.е. измерение скорости канала, конечно хорошо. Но вот как быть, к примеру, в такой ситуации: канал в офисе - 50 Мбит/с, т.е. весьма неплохо )) Но! Кто-то из работников часто пользуется торрентами, забивая втихаря весь канал и шлюз в инет. Естественно, в момент максимальной нагрузки, "измерение" скорости канала даст весьма плачевные результаты. Работник, который получит такой результат, зная, что канал-то широкий и измерения не соответствуют действительности, обратится с вопросами либо к руководству, либо к тому, кто эту "измерялку" сделал )) Вот тут-то и начинается основное веселье по поиску того, куда, бля, делись эти 50 Мбит/с ))) Посему, КМК )), не нужно использовать T(S)Browse для просмотра таблиц, находящихся дальше, чем локальная сеть. Если есть нужда использовать данные, находящиеся в инете, придется переделать логику программы, чтобы не сильно зависеть от скорости канала. Кстати, в случае с letodb, udf-функции - изящное и простое решение этого вопроса. Вот, к примеру, нужно показать 1000 записей. Размер каждой - 200 байт. Всего = 200000 байт или 195.3 кБайта. При скорости в 10 Мбит/с, т.е. 1.25 Мбайта/с, т.е. 1280 кБайт/с, передача 195-ти кБайт займет всего 0.15 секунды. Ну, пусть 0.5 секунды. Записав эти данные в локальную базу (еще 0.5 секунды), можно ее показать с помощью T(S)Browse. И никаких тормозов. ))) Вроде, в рассчетах не ошибся. Вы спросите, а как взять эти 1000 записей? Отвечу: посредством udf-функции ))) updt: Пост подредактировал.

Dima: PSP пишет: Вот тут-то и начинается основное веселье по поиску того, куда, бля, делись эти 50 Мбит/с ))) +1

Andrey: PSP пишет: Вы спросите, а как взять эти 1000 записей? Отвечу: посредством udf-функции ))) Не совсем понятно.... Передавать в виде массива или по другому ? ---- udf-функции это КЛАССНО !

PSP: Andrey пишет: Передавать в виде массива или по другому ? Массивом, конечно.

Andrey: PSP пишет: Массивом, конечно. Понял, спасибо ! PSP пишет: не нужно использовать T(S)Browse для просмотра таблиц, находящихся дальше, чем локальная сеть. Если есть нужда использовать данные, находящиеся в инете, придется переделать логику программы, чтобы не сильно зависеть от скорости канала. Кстати, в случае с letodb, udf-функции - изящное и простое решение этого вопроса. Спасибо ! Буду знать теперь ! Dima пишет: Да без проблем Спасибо ! Жалко проверить уже негде... Медленный провайдер уже не доступен.

Dima: Andrey пишет: Медленный провайдер уже не доступен. Сегодня да , а завтра снова может возникнуть новенький. Я бы приготовился заранее к этому моменту , но тебе виднее.

SergKis: Andrey пишет: Медленный провайдер уже не доступен. Берешь мобильный интернет Bilain, MTC и вечерком (Skype уже еле тянет) отдалившись от вышки на ~30-40 км ... и будет тебе "счастье"

nbatocanin: Здравствуйте! Есть ли способ, чтобы определить текущее nUser для Leto_MgKill(nUser)? Я хочу использовать Leto_MgKill для всех пользователей, но я не хочу, чтобы "убить" активного пользователя. Спасибо, Ненад

Pasha: Функция leto_mgGetUsers возвращает массив с параметрами пользователей: ip-адрес, имя хоста, название приложения. В этом массиве можно найти свои параметры. Это способ, конечно же, ненадежен, так как с одного и того же хоста одно и тоже приложение можно запустить не один раз. Другой способ: сделать на сервере udf-функцию: Function UDF_UserNumber(nUserStru) Return nUserStru и вызывать ее с клиента посредством leto_UDF("UDF_UserNumber") Этот способ не очень красив. Можно, конечно, добавить номер клиента в качестве еще одного элемента массива, возвращаемого функцией leto_mgGetInfo, чтобы номер клиента можно было бы получить штатными средствами letodb

nbatocanin: Было бы хорошо, чтобы сделать функцию Leto_GetCurrentID(). Я использую следующую процедуру: aInfo := Leto_MgGetUsers() FOR i := 1 TO Len(aInfo) Leto_MGKill (aInfo[i,1]) NEXT Это работает, потому что Leto_MGKill не убить текущего пользователя.

alex_II: Не могу в условии индексации использовать пользовательскую функцию. Можно ли обойти это ограничение? UDF-функцию также не видит

Pasha: Индексация происходит на сервере, так что клиентские пользовательские функции там недоступны. Надо добавить эту функцию как раз м модуль udf, причем не как static function, а как function. Тогда эта функция будет видна.

alex_II: как static я функцию не описываю вот udf модуль: #include "dbinfo.ch" #include "set.ch" #ifdef __LINUX__ #define DEF_SEP '/' #define DEF_CH_SEP '\' #else #define DEF_SEP '\' #define DEF_CH_SEP '/' #endif FUNCTION UDF_Init() SET AUTORDER TO 1 RETURN NIL FUNCTION UDF_Metka(nUserStru, cClientAlias) LOCAL rc := .F. LOCAL cArea := Alias() LOCAL cServerAlias := leto_Alias(nUserStru, cClientAlias) dbSelectArea(cServerAlias) DBGoTop() DBSeek(cArea->ls) rc := Found() dbSelectArea(cArea) RETURN rc в логе получаю сообщение: 16.11.2015 14:57:25: Error BASE/1001 Undefined function: LETO_UDF Arguments: ( [ 1] = Type: C Val: UDF_Metka, [ 2] = Type: C Val: amt)

Pasha: Если в условии for команды index on используется пользовательская функция, то ее надо просто добавить в модуль hrb, причем без параметра nUserStru. В условии for ее надо просто вызвать, не задействуя механизм leto_udf. Пример: index on .. tag .. to .. for Pole=MyFunc() сама функция: function myfunc ... return ...

alex_II: Понял, буду пробовать Спасибо

alex_II: Pasha пишет: просто добавить в модуль hrb Поспешил написать что всё понятно Что это и где этот модуль?

Pasha: Надо создать файл letoudf.prg, скомпилировать его с параметром -gh, и получившийся модуль letoudf.hrb скопировать в папку, где находится сервер letodb.exe. Сервер при старте загружает этот модуль. Но я тут заметил, что пользовательская функция обращается к другим рабочим областям. Тогда так просто ее написать не получится.

Pasha: Добавил функцию leto_ClientID() для получения nUserStru на сервере Таким образом, функцию для индексации for можно сделать в udf-модуле примерно так: FUNCTION UDF_Metka(cClientAlias) LOCAL nUserStru := leto_ClientID() LOCAL cServerAlias := leto_Alias(nUserStru, cClientAlias) LOCAL xIs := Field->Is RETURN (cServerAlias)->(DBSeek(xls))

alex_II: Pasha пишет: Добавил функцию leto_ClientID() Спасибо за помощь Буду пробовать

nbatocanin: Паша, это может быть использовано в SET FILTER на сервере?

Pasha: Да. Функция leto_ClientID() возвращает nUserStru клиента, в потоке которого она выполняется. Затем этот nUserStru можно использовать для других функций.

Andrey: Всем привет ! Как на сервере LetoDB сделать подключение к базам работающим в режиме файл-сервера ? Т.е. базы открыты другими программами в режиме SHARED. Хочу сделать функцию для LetoDB в letoudf.prg, чтобы функция открывала чужие базы, что-то подсчитала и передавала результат через массив, для программы расчета.

Pasha: Andrey пишет: Как на сервере LetoDB сделать подключение к базам работающим в режиме файл-сервера ? Т.е. базы открыты другими программами в режиме SHARED. Хочу сделать функцию для LetoDB в letoudf.prg, чтобы функция открывала чужие базы, что-то подсчитала и передавала результат через массив, для программы расчета. Надо открывать такие файлы обычной командой use ... via DBFCDX new shared выборку делать обычными командами skip/seek/etc по окончанию выборки не забыть закрыть. А еще лучше все файлы открывать через letodb, даже локально на сервере. Это будет эффективнее, да и вопроса такого не возникнет, как совмещать.

Andrey: Pasha пишет: А еще лучше все файлы открывать через letodb, даже локально на сервере. Это будет эффективнее, да и вопроса такого не возникнет, как совмещать. Согласен. Только есть уже работающие 7 программ которые открывают эти файлы (переделывать их... ну просто головная боль). Мне нужен режим совместимости для новой программы с LetoDB и старыми программами.

Andrey: Открываю коннект к базе: nConnection := LETO_CONNECT( cPathServer ) .... nConnection = 1 Открываю таблицу и остаюсь в ней. Далее в другой программе делаю другой коннект (пути одинаковы пока, потом будут разные) nConnection := LETO_CONNECT( cPathServer ) nConnection тоже равен 1 !!! Так должно быть ? Или я что-то не допонимаю.... А как сделать чтобы nConnection были разными ?

PSP: Andrey пишет: Так должно быть ? Еще эта функция вернёт -1, если сервер недоступен. Это её основное (и, имхо, единственное) назначение: определить доступность сервера. Но ты можешь посмотреть исходники. Мне, если честно, лень)))

Andrey: PSP пишет: Еще эта функция вернёт -1, если сервер недоступен. Это её основное (и, имхо, единственное) назначение: определить доступность сервера. Но ты можешь посмотреть исходники. Мне, если честно, лень))) Да я знаю что при -1 сервер недоступен. Я просто считал, что если один коннект уже есть, то следующий должен быть 2. А этого нет. Если я делаю LETO_DISCONNECT(nConnection), то программа падает с виндовой ошибкой. Так как nConnection одинаков. Пробовал делать LETO_SETCURRENTCONNECTION( nConnection ) - без толку. Возвращает тоже 1. Т.е. как сделать, чтобы новый коннект был другим ? nConnection := LETO_CONNECT( cPathServer , ....) IF nConnection > 0 LETO_SETCURRENTCONNECTION( nConnection ) nI := LETO_GETCURRENTCONNECTION() MsgDebug(TIME() + " " + HB_NtoS(nI) + " - connect" ) ???

Dima: Andrey пишет: Т.е. как сделать, чтобы новый коннект был другим ? Наверное это просто не большая не доработка Leto и которую Павел сможет исправить.

Andrey: Dima пишет: Наверное это просто не большая не доработка Leto и которую Павел сможет исправить. А в ADS как делается ?

Dima: Andrey пишет: А в ADS как делается ? Там юзается AdsConnect() , затем AdsConnection()для получения хендла , но я не чекал разный ли он , но проверить можно.

Pasha: nConnection - это номер соединения только на одном клиенте, то есть в рамках одного приложения. Чтобы получить номер клиента на сервере, надо сделать вызов: RddInfo( RDDI_CLIENTID,,, [nConnection] )

Andrey: Pasha пишет: то есть в рамках одного приложения. Я запускал сразу несколько разных тестовых программ к 2 серверам, везде nConnection = 1. Только соединение делал простое, только путь и всё. nConnection := LETO_CONNECT( cPathServer )

Andrey: Pasha пишет: Чтобы получить номер клиента на сервере, надо сделать вызов: RddInfo( RDDI_CLIENTID,,, [nConnection] ) А как потом у себя в программе закрыть только нужное соединение, не закрывая другого ? LETO_DISCONNECT(nConnection)

Pasha: Я запускал сразу несколько разных тестовых программ к 2 серверам, везде nConnection = 1. Только соединение делал простое, только путь и всё. nConnection := LETO_CONNECT( cPathServer ) Одна клиентская программа ничего не знает про то, к каким серверам коннектились другие, поэтому в каждой программе nConnection нумеруются с единицы. Точно так же сервер не знает, к каким еще серверам был коннект у его клиентов. Поэтому есть номера соединений на клиенте - это nConnection, и номера клиентов на сервере - это RDDI_CLIENTID А для закрытия соединения - да, надо использовать nConnection Но можно еще и указывать адрес сервера: LETO_DISCONNECT( [ cConnString | nConnection ] ) --> nil

Andrey: Pasha пишет: А для закрытия соединения - да, надо использовать nConnection Но можно еще и указывать адрес сервера: LETO_DISCONNECT( [ cConnString | nConnection ] ) --> nil Если в одной программе адрес и пути одинаковы, то несколько nConnection не получиться ? Как тогда можно организовать в одной программе ОДНОВРЕМЕННО из клиента допустим следующее: 1) показ базы TEST с сервера 2) обработку базы TEST (добавление записей, ну по мелочи что-то)

Pasha: Зачем с одной программы делать несколько коннектов к одному и тому же серверу letodb ? leto_connect() работает следующим образом: При первом вызове соединение устанавливается, и возвращается его номер 1. При повторном вызове с такой же строкой коннекта определяется, что такой коннект уже есть, новый коннект не устанавливается, и возвращается тот же номер коннекта, т.е. 1.

Andrey: Pasha пишет: При повторном вызове с такой же строкой коннекта определяется, что такой коннект уже есть, новый коннект не устанавливается, и возвращается тот же номер коннекта, т.е. 1. Чуток понял - как коннект выполняется... А как тогда решить разрывать коннект или нет ? Использовать Public переменную для определения, есть ли открытая база или нет ?

PSP: Andrey пишет: А как тогда решить разрывать коннект или нет ? А зачем его разрывать?

Andrey: PSP пишет: А зачем его разрывать? Ну не хотелось бы его держать постоянно открытым... Хотя наверно и можно. Не знаю, не делал таких программ, вот и спрашиваю как делать. При запуске коннектиться к базе и держать постоянно открытым соединение ?

PSP: Andrey пишет: Ну не хотелось бы его держать постоянно открытым... Хотя наверно и можно. Не знаю, не делал таких программ, вот и спрашиваю как делать. При запуске коннектиться к базе и держать постоянно открытым соединение ? Постоянно будет открыто только tcp-соединение. Файлы базы данных можешь закрывать, если хочешь. К ним это соединение не имеет отношения.

Andrey: PSP пишет: Постоянно будет открыто только tcp-соединение. А какие ресурсы в программе задействует открытие tcp-соединения ? Что положительного и отрицательного в постоянно открытом tcp-соединение ? Или при входе в программу делать один коннект и не заморачиваться ? А как быть если программа использует доступ к двум разным серверам, один на Linux, а второй на Win2008 ? Не одновременный доступ, второй сервер используется периодически - раз в месяц. Чем дальше в лес, тем больше вопросов...

Softlog86: Протоколу TCP не важно какой сервер ....

PSP: Andrey пишет: А какие ресурсы в программе задействует открытие tcp-соединения ? Никакие. Соединение устанавливает операционная система. Программа просто им пользуется, как и остальными ресурсами компьютера. Что положительного и отрицательного в постоянно открытом tcp-соединение ? Ничего отрицательного в этом нет. К примеру, браузер в процессе работы может открывать сотни(!!!) соединений с разными ресурсами, чтобы отобразить корректно html-страницу. Или при входе в программу делать один коннект и не заморачиваться ? Вот именно. А как быть если программа использует доступ к двум разным серверам, один на Linux, а второй на Win2008 ? Не одновременный доступ, второй сервер используется периодически - раз в месяц. Два разных сервера, - два соединения.

Andrey: PSP СПАСИБО !

Andrey: Вопрос по событиям в LetoDB. Есть ли возможность подписаться на событие добавления/редактирования/удаления записи в LetoDb ?

Pasha: Можно установить глобальный триггер: letodb.ini есть параметры: Trigger = <cFuncName> - Глобальная функция letodb RDDI_TRIGGER PendingTrigger = <cFuncName>- Глобальная функция letodb RDDI_PENDINGTRIGGER EnableSetTrigger = 0 - если 1, разрешает изменять установки триггера с помощью dbInfo( DBI_TRIGGER, ... ) Можно с клиента сделать вызов для рабочей области: dbInfo(DBI_TRIGGER, ...) и передали серверу имя функции - триггера. Саму функцию надо реализовать в модуле letiudf.prg Как работает триггер см.описание Sx_SetTrigger Особенность letodb в том, что триггер выполняется на сервере.

Andrey: Pasha пишет: Как работает триггер см.описание Sx_SetTrigger А где его взять ?

Haz: Andrey пишет: А где его взять ? Гуглить справку по SIX https://www.google.ru/search?q=Sx_SetTrigger&oq=Sx_SetTrigger&aqs=chrome..69i57.1959j0j8&sourceid=chrome&ie=UTF-8

SergKis: Andrey Вот пример [pre2] ////////////////////////////////////////////////////////////////// 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]

Andrey: SergKis пишет: Вот пример Спасибо ! А как на клиенте получать и опрашивать событие ?

Pasha: Andrey пишет: А как на клиенте получать и опрашивать событие ? На клиенте хочется получать событие, что другой клиент выполнил какое-то обновление БД ? Как ты себе это представляешь ? Это значит, что сервер должен в ответ на запрос с одного клиента начать отправлять пакеты всем клиентам, которые к нему подключены. А клиенты должны висеть в ожидании такого пакета. Как бы letodb работает совсем в другом режиме. Наверное надо сделать по другому. Пусть триггер в letodb ведет какой-то лог изменений, а клиенты пусть опрашивают этот лог, и выбирают из него то, что им надо.

Andrey: Pasha пишет: На клиенте хочется получать событие, что другой клиент выполнил какое-то обновление БД ? В идеале - да ! Хочется такого. В jave есть такое, мне показывали... Подробнее напишу чуть позже, уточню...

PSP: Andrey, делаешь отдельное приложение, которое постоянно выполняется на компьютере, где установлен сервер, и ведет некий журнал, в который записывает эти события. Клиентские же приложения могут в отдельном потоке периодически проверять этот журнал. Так им можно передать эти события.

Dima: PSP пишет: делаешь отдельное приложение, которое постоянно выполняется на компьютере, где установлен сервер лучше в виде сервиса наверное.

PSP: Dima пишет: лучше в виде сервиса наверное. Конечно лучше)

Andrey: PSP пишет: делаешь отдельное приложение, которое постоянно выполняется на компьютере, где установлен сервер, и ведет некий журнал, в который записывает эти события. А зачем это делать ? Есть же событие в базе добавление/удаление/изменение - вот пускай LetoDb его выдает. Это же сервер, а не что-нибудь. В PostgreSQL такое же есть (LISTEN/NOTIFY) ! https://postgrespro.ru/doc/sql-notify.html Вот и хочу получать такие события на клиенте, которые подписались на такие события сервера Leto.

Andrey: А вообще возможно ли получение сообщения от сервера Leto клиенту ?

SergKis: Andrey пишет:возможно ли получение сообщения от сервера Leto клиенту ? 1. Создаешь переменную(ые) с клиента или в udf_init сервера : LETO_VARSET( cGroupName, cVarName, xValue[, nFlags[, @xRetValue]] ) --> lSuccess или на сервере LETO_VARSET( nUserStru, cGroupName, cVarName, xValue[, nFlags ) --> lSuccess 2. в триггере, если переменная(ые) есть, заполняешь, на клиенте проверяешь изменилось или нет значение

Dima: SergKis пишет: LETO_VARSET Кстати нормальный вариант

Andrey: По доке: Crypt_Traffic = 0 - если 1, то данные, передаваемые по сети, шифруются; Каким алгоритмом шифруется и насколько снижается скорость выполнения при шифрации /дешифрации ?

Pasha: Используется симметричный криптоалгоритм blowfish. Он должен быть быстрым. Насколько быстро выполняется шифровка-дешифровка - я не проверял, так как для своих программ шифровку не использую.

Andrey: Как сделать для своих программ на МиниГуи отдельную dll-ку LetoDB ? И как в своей программе подключать эту dll-ку ?

PSP: Andrey пишет: Как сделать для своих программ на МиниГуи отдельную dll-ку LetoDB ? И как в своей программе подключать эту dll-ку ? Какие причины так делать?

Andrey: PSP пишет: Какие причины так делать? Да научиться хочу. У многих других баз работа через dll (PostgreSQL, ADS)... Тем более dbedit был раньше с несколькими dll-ками.

Dima: PSP пишет: Какие причины так делать? +1 Тоже не понял смысла...

Haz: Dima пишет: Тоже не понял смысла... присоединяюсь. не ясно зачем ? обычно dll используется для часто используемых вызовов , чтоб не плодить их в разных программах. Но возможно это давняя мечта - собрать dll http://hwgui.borda.ru/?1-1-0-00000023-000-0-0#004 С тех пор не раз видел ( вроде и на этом форуме ) примеры сборки. Но главный вопрос - зачем ??? Andrey пишет: У многих других баз работа через dll (PostgreSQL, ADS). с версиями этих DLL при смене версии сервера ADS - отдельное развлечение на удаленке и хрен поменяешь пока не срубишь задачу которая ее держит

PSP: https://ru.wikipedia.org/wiki/DLL_hell

Andrey: Спасибо БОЛЬШОЕ всем за разъяснения !

Pasha: В скрипте rddleto.hbp надо сделать некоторые изменения: Строки # static lib -hblib # shared lib, dll or so #-hbdynvm #-shared Заменить на # static lib #-hblib # shared lib, dll or so -hbdynvm -shared Возможно, изменить имя библиотеки, это команда -olib/${hb_plat}/${hb_comp}/rddleto Для использования dll в программе надо при сборке указать соответствующую lib

Andrey: Pasha, СПАСИБО ! Меня отговорили от dll-ки !

nbatocanin: Попробуем Leto_CliendID(). Это функция в letoudf.hrb: FUNCTION Test LOCAL nUserStru := leto_ClientID() RETURN nUserStru При запуске сервера, получаем: Leto DB Server v.2.17b1 ! INIT: DataPath=d:\base, ShareTables=0, MaxUsers=500, MaxTables=5000, CacheRecords=50 05/07/16 15:48:16: Error BASE/6101 Unknown or unregistered symbol: LETO_CLIENTID ERROR! SETHRBERROR - pUStru not found!!!!!!!!!!!!!!!!!!!! Почему?

nbatocanin: Как использовать функцию LetoOrdCreate? Я написал в клиенте это: Leto_UDF("LetoOrdCreate", "IN001", "Upper(a_naziv)") Но сервер выдает сообщение об ошибке: Error DBFNTX/1003 Open error: d:\base\xapp\36\in001.ntx

Pasha: nbatocanin пишет: Попробуем Leto_CliendID(). Это функция в letoudf.hrb: FUNCTION Test LOCAL nUserStru := leto_ClientID() RETURN nUserStru При запуске сервера, получаем: Leto DB Server v.2.17b1 ! INIT: DataPath=d:\base, ShareTables=0, MaxUsers=500, MaxTables=5000, CacheRecords=50 05/07/16 15:48:16: Error BASE/6101 Unknown or unregistered symbol: LETO_CLIENTID ERROR! SETHRBERROR - pUStru not found!!!!!!!!!!!!!!!!!!!! Почему? Я добавил request для этой функции. Попробуйте сейчас.

Pasha: nbatocanin пишет: Как использовать функцию LetoOrdCreate? Я написал в клиенте это: Leto_UDF("LetoOrdCreate", "IN001", "Upper(a_naziv)") Но сервер выдает сообщение об ошибке: Error DBFNTX/1003 Open error: d:\base\xapp\36\in001.ntx Эта функция предназначена для вызова на сервере. С клиента используйте стандартную команду INDEX ON / функцию ordCreate

nbatocanin: Эта функция предназначена для вызова на сервере. С клиента используйте стандартную команду INDEX ON / функцию ordCreate Надо изменить readme.txt: 9. Server-side functions These functions can be run from the client by function leto_udf, and also from the functions defined in a file letoudf.hrb. Сделал функцию в letoudf.hrb: [pre2]FUNCTION Udf_ParSort (nUserStru, cExp) LOCAL cLetoPar, cFile cFile := "002" cLetoPar := Leto_Alias (nUserStru, "Partneri") SELECT (cLetoPar) LetoOrdCreate (nUserStru, cFile, cExp) LetoOrdListAdd (nUserStru, cFile) RETURN cFile[/pre2] Но функция не работает, сервер регистрирует ошибку: Error DBFNTX/1003 Open error: d:\base\xapp\36\002.ntx

nbatocanin: Я добавил request для этой функции. Попробуйте сейчас. работает, спасибо!

Pasha: nbatocanin пишет: Сделал функцию в letoudf.hrb: FUNCTION Udf_ParSort (nUserStru, cExp) LOCAL cLetoPar, cFile cFile := "002" cLetoPar := Leto_Alias (nUserStru, "Partneri") SELECT (cLetoPar) LetoOrdCreate (nUserStru, cFile, cExp) LetoOrdListAdd (nUserStru, cFile) RETURN cFile Но функция не работает, сервер регистрирует ошибку: Error DBFNTX/1003 Open error: d:\base\xapp\36\002.ntx Попробуйте после сегодняшнего обновления

nbatocanin: Сейчас функция LetoOrdCreate работает (должен быть написать дополнительный параметр cTag): LetoOrdCreate (nUserStru, cFile, cExp, cTag) Но как я могу использовать этот индекс? LetoOrdListAdd не активирует этот индекс. Я попробовал OrdSetFocus, SET INDEX... безуспешно.

Pasha: letoOrdListAdd активирует (формирует соответствующие структуры данных) этот индекс на сервере, но клиент об этом ничего не знает. Чтобы активировать его и на клиенте, изменить функцию letoOrdListAdd, чтобы она передавала информацию об открытом индексе клиенту, и сделать на клиенте обработку этой информации. Это требует небольшой доработки letodb. Можно обойтись и без такой схемы, просто создавая и открывая индекс стандартными средствами rdd с клиента до вызова udf-функции. Кстати, почему вы выбрали такую схему создания индекса в udf-функции, а не непосредственно с клиента ?

nbatocanin: У меня есть таблица (tbrowse) с колоннами: A: p_id B: p_naziv C: SumPar(p_id) Функция SumPar(p_id) для каждого p_id: [pre2]FUNC SumPar (p_id) SELECT Table2 s := 0 SEEK p_id WHILE p_id == s_doc s += s_val SKIP END DO SELECT Partner RETURN s [/pre2] Я хочу, чтобы отсортировать столбец C. Когда не используется LetoDB может написать: cExp := "SumPar(p_id)" INDEX ON &cExp TO (cTmp) Но с LetoDB, это невозможно.

Andrey: nbatocanin пишет: Но с LetoDB, это невозможно. Возможно ! Нужно добавить функцию "SumPar(p_id)" в LetoDB и пересобрать сервер, т.е. эта функция будет уже на сервере. Или сделать отдельную функцию UDF.

Pasha: nbatocanin пишет: У меня есть таблица (tbrowse) с колоннами: ... Такую задачу можно решить самыми стандартными средствами, не задействуя механизм udf Надо создать постоянный индекс для table2 по полю s_doc В качестве 3-й колонки для tbrowse использовать выражение: table2->(leto_Sum('s_val',, table1->p_id))

nbatocanin: Такую задачу можно решить самыми стандартными средствами, не задействуя механизм udf Надо создать постоянный индекс для table2 по полю s_doc В качестве 3-й колонки для tbrowse использовать выражение: table2->(leto_Sum('s_val',, table1->p_id)) Можно ли сортировать (INDEX ON) по данной колонке? Мне нужно индексирование по функциям потому что у меня есть и другие функции. Эти функции не существуют в Letodb.

Pasha: nbatocanin пишет: Можно ли сортировать (INDEX ON) по данной колонке? Если создан индекс по полю s_doc. то естественно, записи будут упорядочены (отсортированы) по этому полю. Мне нужно индексирование по функциям потому что у меня есть и другие функции. Эти функции не существуют в Letodb. Это, насколько я понимаю, уже другой вопрос. Свои пользовательские функции можно самому добавить в source\server\server.prg перед сборкой сервера. Это касается простых функций. Если функции при этом выполняют какие-то выборки из других рабочих областей, вопрос конечно сложнее.

nbatocanin: Я не объяснил правильно, извините. Tbrowse имеет несколько функций, например: A: p_id B: p_naziv C: SumPar(p_id) D: KomName(p_id) [pre2]FUNC KomName (p_id) SELECT Komerc SEEK p_id cName := Komerc->k_name SELECT Partner RETURN cName[/pre2] Мне нужно сортировать произвольную колонку выбраную пользователем. Например, это начальная таблица: [pre2]A B C D ---------------------------- 1 AB Comm 200 A6 2 D-Trd 1300 B1 3 XY Trade 500 A1 [/pre2] Пользователь выбирает колонку C: [pre2]A B C D ---------------------------- 2 D-Trd 1300 B1 3 XY Trade 500 A1 1 AB Comm 200 A6 [/pre2] выбирает колонку D: [pre2]A B C D ---------------------------- 3 XY Trade 500 A1 1 AB Comm 200 A6 2 D-Trd 1300 B1 [/pre2] Поэтому мне нужен индекс по функции. Когда не используется LetoDB, решение очень простое: cExp := "SumPar(p_id)" INDEX ON &cExp TO (cTmp)

SergKis: nbatocanin пишет:Когда не используется LetoDB, решение очень простое Не сложным будет, если делать запрос к базе, например в массив, тогда по умолчанию получите все колонки с сортировкой в tsb, как в примере minigui\samples\advanced\tsb_array_2\demo.prg (dblclick на header колонки). При такой технологии, не важно будет с какой базой работаете dbf\sql\ads\leto\...

nbatocanin: К сожалению, я не использую MiniGUI. Кроме того, эти таблицы могут быть очень большими (50.000+), не знаю как быстро это решение работает. Но, если я не найти другое решение, буду скопировать всю таблицу в DBFNTX, где работает INDEX по функциям. Это не хорошее решение, но не имеют другую идею.

SergKis: nbatocanin пишет: я не использую MiniGUI MiniGUI это, как пример, подключенного блока кода сортировки (туда\обратно) массива по dblclick на колонку, организовать такое у себя, мне думается не очень сложно. буду скопировать всю таблицу можно копировать узкую запись (не все колонки, а нужные для сортировки) а потом показывать все колонки по ссылке (recno)

Andrey: nbatocanin пишет: Поэтому мне нужен индекс по функции. Когда не используется LetoDB, решение очень простое: cExp := "SumPar(p_id)" INDEX ON &cExp TO (cTmp) Да делай что предлагают ! Это же просто: Pasha пишет: Свои пользовательские функции можно самому добавить в source\server\server.prg перед сборкой сервера. Это касается простых функций. Если функции при этом выполняют какие-то выборки из других рабочих областей, вопрос конечно сложнее. nbatocanin пишет: FUNC KomName (p_id) SELECT Komerc SEEK p_id cName := Komerc->k_name SELECT Partner RETURN cName Для этой функции можно сделать по другому: FUNC KomName (cAlias,p_id) Local nSel := SELECT() //SELECT Komerc Select(cAlias) SEEK p_id cName := Komerc->k_name SELECT(nSel) RETURN cName

nbatocanin: можно копировать узкую запись (не все колонки, а нужные для сортировки) а потом показывать все колонки по ссылке (recno) Я уже пытался это решениеи, оно работает: SELECT (::cTmp) ZAP [pre2]SELECT (::cAlias) Order ("") GO TOP WHILE !Eof() x := &cExp (::cTmp)->(DBAppend()) (::cTmp)->t_rec := RecNo() (::cTmp)->t_val := x SKIP END DO SELECT (::cTmp) SET RELATION TO t_rec INTO (::cAlias) GO TOP [/pre2] Но, с этим решением возникает проблема фильтров других рабочих зон. Например: SET FILTER TO Upper(Partner->p_naziv) == "XY TRADE"

Andrey: nbatocanin пишет: Кроме того, эти таблицы могут быть очень большими (50.000+), не знаю как быстро это решение работает. Это очень маленькие базы для LetoDB. Я тестировал на 5 млн. записей и приводил свои тесты: Сделал тестовый пример. Алгоритм простой - выборка из базы по SEEK и суммирование по 7 полям и запись этих 7 полей в локальную базу на компе пользователя. Итого 13 раз нужно просчитать по базе. База 100 000 записей с мемо-полями: 120 Мб Расчет по простому индексу 13 позиций: 1) локальная DBFCDX - 00:01:10 2) локальная LETO - 00:01:11 3) интернет LETO - 00:04:37 Если база считается интернет + LETO, иногда подвисает при записи в локальную базу результатов. Странно всего 7 полей записать, а подвисает так что в окне программы пишет (программа не отвечает). Может и не там происходит подвисание. База 1 000 000 записей с мемо-полями: 1,2 Гб Расчет по простому индексу 13 позиций: 1) локальная DBFCDX - 00:06:38 2) локальная LETO - 00:06:35 3) интернет LETO - 00:34:45 Если база считается интернет + LETO, то постоянно подвисает при записи результатов. При суммировании строка показывается (мастер+ кода) быстро. Убрал лишний вывод на экран. Наверно тормоза МиниГуи были из-за него. Теперь скорость просто чумовая: База 100 000 записей с мемо-полями: ~120 Мб Расчет по простому индексу 13 позиций: 1) локальная DBFCDX - 00:00:00 2) локальная LETO - 00:00:01 3) интернет LETO - 00:01:49 База 1 000 000 записей с мемо-полями: ~1,2 Гб Расчет по простому индексу 13 позиций: 1) локальная DBFCDX - 00:00:06 2) локальная LETO - 00:00:09 3) интернет LETO - 00:11:32 Открыть в TBROWSE миллион записей из базы, которая находиться неизвестно где (на просторах интернета) - это просто пипец (как говорит Аллочка из Универа) Вывод: связка Harbour + LetoDB + MiniGui - отличная вещь в работе. Это при том, что используется простой старый алгоритм, без переделки под Leto ! Тестирование АЛГОРИТМА-2 только для LETO. База в интернете, сервер - Leto DB Server v.2.15b3 Суммирование по 7 полям - 13 раз, т.е. обращение к серверу производиться всего 13 раз. Расчет по базе 100 000 записей (~120 Мб) - 00:00:01 Расчет по базе 1 000 000 записей (~1,2 Гб) - 00:00:08 Для сравнения, расчет по локальной базе из 1 000 000 записей на DBFCDX - 6 сек. Тестирование АЛГОРИТМА-3 только для LETO. База в интернете, сервер - Leto DB Server v.2.15b3m1 (m1-моя модификация) Расчёт по базе происходит на сервере, клиенту возвращается результат через массив. Расчет по базе на сервере 100 000 записей (~120 Мб) - 00:00:00 Расчет по базе на сервере 1 000 000 записей (~1,2 Гб) - 00:00:05 Для сравнения, расчет по АЛГОРИТМУ-1 по локальной базе из 1 000 000 записей на DBFCDX - 6 сек. Сделал еще отдельный тест на сервере 5 000 000 записей (~5,9 Гб) База в интернете, сервер - Leto DB Server v.2.15b3m1 Расчёт по АЛГОРИТМУ-2 - 00:00:34 Расчёт по АЛГОРИТМУ-3 - 00:00:26 Для сравнения, расчет по АЛГОРИТМУ-1 по локальной базе (локальный комп) из 5 000 000 записей на DBFCDX - 00:15:17 Пример здесь - https://cloud.mail.ru/public/DPUw/QtKxEiyro

Dima: Andrey пишет: Для этой функции можно сделать по другому: FUNC KomName (cAlias,p_id) Local nSel := SELECT() //SELECT Komerc Select(cAlias) SEEK p_id cName := Komerc->k_name SELECT(nSel) RETURN cName Можно же проще однако. Плохая практика указывать явно Select а затем делать Seek (ИМХО) [pre2] FUNC KomName (cAlias,p_id) Local ret:="" if (cAlias)->(Dbseek( p_id )) ret := (cAlias)->k_name endif RETURN ret [/pre2]

Haz: Dima пишет: Можно же проще однако. можно еще [pre2] cRet := if( (cAlias)->(Dbseek( p_id )), (cAlias)->k_name ,'' ) [/pre2] или [pre2] cRet := (cAlias)->( if( Dbseek( p_id ), F->k_name ,'' )) [/pre2]

Dima: Haz пишет: можно еще Можно :) Я чисто написал что бы ясно было товарищу из Болгарии и товарищу с Дмитрова.

Haz: Dima пишет: Я чисто написал что бы ясно было тоже кстати не люблю явное указание SELECT не знаю почему но всегда использую (cAlias)-> может потому что не нужно помнить в какой рабочей области живем сейчас

Dima: Haz пишет: может потому что не нужно помнить в какой рабочей области живем сейчас Аналогично !

nbatocanin: Функция KomName не важна, я писал только в качестве примера. Мне нужна функция, которая может быть использована в INDEX ON. Pasha пишет: Свои пользовательские функции можно самому добавить в source\server\server.prg перед сборкой сервера. Это касается простых функций. Если функции при этом выполняют какие-то выборки из других рабочих областей, вопрос конечно сложнее. Как это работает? Я изменил server.prg и добавил простую функцию TEST которая возвращает "Abc" (добавил ту же функцию на клиенте). Это работает. Но, как Паша написал, когда я пытаюсь получить доступ к базе данных, сервер отвечает с ошибкой: Breakdown at: 2016.05.13 22:42:19 Unrecoverable error 6005: Exception error:%s

nbatocanin: Вот как я решил проблему (упрощенно): - создал p_tmp (C5) в таблице Partneri. - Создан TMP на станции через драйвер DBFNTX. Эта таблица имеет два поля: t_val (C40), t_rec (N10), INDEX ON t_val. Программа вычисляет значение выражения для каждой строки, затем написал результат у tabelu TMP. [pre2]SELECT ("Partneri") GO TOP WHILE !Eof() Tmp->(DBAppend()) Tmp->t_rec := RecNo() Tmp->t_val := Upper(&cExp) SKIP END DO SELECT Tmp GO TOP n := 1 WHILE !Eof() Partneri->(DBGoto(Tmp->t_rec)) Partneri->p_tmp := HB_NumToHex (n++,5) SKIP END DO SELECT Partneri INDEX ON p_tmp TAG "TmpPar" TO (cTmp) [/pre2] Я заметил, что есть ошибкa в команде INDEX ON (DBFNTX): INDEX ON p_tmp TO (cTmp) Команда работает, если cTmp = "test". Но если cTmp = "//192.168.99.10:2807/App/test", то команда не работает. INDEX ON p_tmp TAG "test" TO (cTmp) Всегда работает.

Andrey: nbatocanin пишет: Команда работает, если cTmp = "test". Но если cTmp = "//192.168.99.10:2807/App/test", то команда не работает. Попробуй делать индексный файл без пути к нему. Просто cTmp := "test" Для индексов не нужно указывать путь "//192.168.99.10:2807/App/". Я не указываю пути к индексу.

nbatocanin: Попробуй делать индексный файл без пути к нему. Просто cTmp := "test" Для индексов не нужно указывать путь "//192.168.99.10:2807/App/". Я не указываю. Так работать, но мне нужно указывать путь, индекс находится в другом директорию. INDEX ON TAG "xy"... всегда работает.

Andrey: nbatocanin пишет: Так работать, но мне нужно указывать путь, индекс находится в другом директорию. Этот вопрос уже к разработчикам Leto. Хотя зачем делать индексы в другой директории ? Это же не локальная или не сетевая программа... Небольшое ограничение Leto. Привыкнуть просто и всё !

nbatocanin: Это временный индекс, я не хочу мешать с другими данными.

Andrey: nbatocanin пишет: Это временный индекс, я не хочу мешать с другими данными. Кто мешает его удалять при выходе из программы или после использования ? Я удаляю при выходе из своей программы.

Haz: Andrey пишет: Я удаляю при выходе из своей программы. Лучше удалять при входе, так гарантированно удалится весь мусор если даже прога слетела аварийно. или двойная проверка - при выходе и при входе

Dima: Лучше делать MEM: базу для этих случаев .

nbatocanin: Лучше делать MEM: базу для этих случаев . MEM таблицы имеют ограничение около 500-600,000. Кроме того, мне нужен Letodb индекс. Я думаю, что это невозможно.

Dima: nbatocanin пишет: Кроме того, мне нужен Letodb индекс Тогда да , MEM не подходит. Насчет ограничения MEM .... стресс тест я ему не устраивал , пока все работает ровно. А откуда такая инфа про ограничение в около 500-600,000 (я так понял записей) ?

nbatocanin: Лучше удалять при входе, так гарантированно удалится весь мусор если даже прога слетела аварийно. или двойная проверка - при выходе и при входе Это не так просто (хотя и возможно). Если программа позволяет использовать несколько старт на той же рабочей станции (USER-1, USER-2...), как вы знаете, какие таблицы следует исключить?

nbatocanin: А откуда такая инфа про ограничение в около 500-600,000 (я так понял записей) ? Да, 600,000 записей. Я попробовал, а потом подтвердили на Harbour-users. Конечно, зависит от объема доступной памяти.

Haz: nbatocanin пишет: (USER-1, USER-2...), как вы знаете, какие таблицы следует исключить? я обычно времянки делаю в домашнем каталоге пользователя , там же сохраняю настройки персонально под пользователя

Петр: nbatocanin пишет: Это не так просто (хотя и возможно). Если программа позволяет использовать несколько старт на той же рабочей станции (USER-1, USER-2...), как вы знаете, какие таблицы следует исключить? А зачем вам это знать? Пусть программа сама формирует имена временных файлов, сохраняет их и удаляет STATIC aTmpFiles INIT PROCEDURE InitTmpFiles aTmpFiles := {} .. RETURN PROCEDURE Main LOCAL cTmp .. // конечно здесь можно и директорию указать и префикс присвоить, например, привязав его к текущему пользователю, // и расширение для текущего RDD получить автоматом, но в этом случае - это не столь существенно FClose( hb_FTempCreateEx( @cTmp, NIL, "tmp_", ".cdx" ) ) AAdd(aTmpFiles, cTmp) .. FErase(cTmp) INDEX ON &cExp TO (cTmp) .. RETURN EXIT PROCEDURE DeleteAllTmpFile() LOCAL tmpFile .. FOR EACH tmpFile IN aTmpFiles FErase(tmpFile) NEXT .. RETURN

nbatocanin: А зачем вам это знать? Пусть программа сама формирует имена временных файлов, сохраняет их и удаляет Я не уверен, что мы понимаем друг друга - простите, мое знание русского языка очень плохое. Моя программа работает таким образом: создаются автоматически временны файлов и система заботится об их удалении. Вот как выглядят мои программы: [pre2]T_BEGIN T_DEFINE TMP TMP FIELD t_id NUMERIC 10,0 TMP FIELD t_naz CHAR 35 TMP INDEX Upper(t_naz) TAG Tmp T_CREATE TMP ALIAS Tmp T_USE Partneri T_Order ("Par_Naz") T_RELATION TO p_id INTO Kartice T_FLock ("Partneri") ... T_END [/pre2] Когда программа выполнить команду T_END, удаляет все временные файлы, закрывает DBF, возвращает оригинал Order... Если какой-либо из этих команд не выполняется (например T_Flock()), система возвращается в прежнее состояние. Временные файлы создаются/удаляются на рабочей станции. Теперь мне нужно создать временные файлы на сервере (которые LetoDB, к сожалению, не поддерживается). Вот почему мне нужен индекс в другую папку. Если смешать временные файлы и основные данные, могут возникнуть проблемы, например, в случае различных авария.

digikv: Ненаде погледај пример који сам закачио. Сваки клијент креира своје привремене датотеке на серверу које се не поклапају ни са једним другим клијентом и на крају извршавања програма се бришу. Овде сам поставио и питање за грешку у leto_groupby. Цео пример ради са мојим сервером који се налази видљив на интернету. [url=https://drive.google.com/open?id=0B2TAOFowS46iTzg1a3N0Vk9Hdkk[/url]

Andrey: nbatocanin пишет: Теперь мне нужно создать временные файлы на сервере (которые LetoDB, к сожалению, не поддерживается). Да не нужно создавать временный индексы на локальной станции. Создавай в той же папке что и база на Leto. Какая разница где создавать индексы. Это не принципиально для программы. Просто сделай чтобы программа запоминала список индексов, КОТОРЫЕ ПОТОМ НУЖНО УДАЛИТЬ - и всех делов то...

nbatocanin: Да не нужно создавать временный индексы на локальной станции. Создавай в той же папке что и база на Leto. Какая разница где создавать индексы. Это не принципиально для программы. Просто сделай чтобы программа запоминала список индексов, КОТОРЫЕ ПОТОМ НУЖНО УДАЛИТЬ - и всех делов то... Я уже делаю именно так. Почему я хочу отделить мои временные файлы? Если смешать временные файлы и основные данные, могут возникнуть проблемы, например, в случае различных авария. Теперь все временные файлы в //192.168.99.1/Temp. В случае повреждения, я могу легко найти их, анализировать и удалить. Это не так легко, если они находятся между основными данными. Но все это не имеет ничего с командой INDEX которая теперь содержит ошибку, которая должна быть исправлена.

Петр: nbatocanin пишет: Я не уверен, что мы понимаем друг друга - простите, мое знание русского языка очень плохое. Я думаю, что не только язык приводит нас к непониманию nbatocanin пишет: Моя программа работает таким образом: создаются автоматически временны файлов и система заботится об их удалении. Вот как выглядят мои программы: T_BEGIN T_DEFINE TMP TMP FIELD t_id NUMERIC 10,0 TMP FIELD t_naz CHAR 35 TMP INDEX Upper(t_naz) TAG Tmp T_CREATE TMP ALIAS Tmp T_USE Partneri T_Order ("Par_Naz") T_RELATION TO p_id INTO Kartice T_FLock ("Partneri") ... T_END Из этого фрагмента нельзя даже понять зачем вы создали TMP. В harbour для создания временных файлов (таблиц) есть специальная функция hb_dbCreateTemp. От того, что обыкновенную таблицу вы назвали Tmp она временной не стала. Вы можете легко найти свою "временную" таблицу с помощью hb_dbExists() и после анализа удалить с помощью hb_dbDrop(). и т.д. и т.п. nbatocanin пишет: Но все это не имеет ничего с командой INDEX которая теперь содержит ошибку, которая должна быть исправлена. Команду INDEX не правильно обрабатывает препроцессор?

nbatocanin: Из этого фрагмента нельзя даже понять зачем вы создали TMP. Эта программа только показывает как работает моя система для временных файлов. Вместо того: [pre2] cFile := HB_DbCreateTemp() DBCreate (..) ... HB_FErase (cFile) [/pre2] Я пишу: [pre2] T_BEGIN T_DEFINE TMP ... T_CREATE TMP ALIAS Tmp ... T_END [/pre2] И тогда все T_ команды препроцессор переводит в DBCreateTemp, DBCreate... Я надеюсь, что я сейчас лучше объяснить? Команду INDEX не правильно обрабатывает препроцессор? Нет. Препроцессор не может знать содержимое переменной cTmp. Я думаю, что проблема в том, что препроцессор в зависимости от опций TAG транслируется в две команды: [pre2] INDEX ON p_tmp TAG "test" TO (cTmp) => ordCreate( (cTmp), "test", "p_tmp", {|| p_tmp}, ) INDEX ON p_tmp TO (cTmp) => dbCreateIndex( (cTmp), "p_tmp", {|| p_tmp}, if( .F., .T., NIL ) ) [/pre2] OrdCreate работает правильно, DBCreateIndex не работает нормально в этом случае.

Pasha: В letodb.ini добавил: Default_Driver = NTX Сделал простой тест: proc main request leto, DBFCDX Local cPath := "//127.0.0.1:2812/" RddSetDefault ("LETO") Leto_Connect (cPath) USE (cPath + 'account') SHARED NEW index on kors to (cPath+'apt10/a.ntx') retu nil в результате в папке apt10 создался индекс a.ntx, как и заказывали, т.е. все отработало так, как и в DBFNTX

Петр: nbatocanin пишет: OrdCreate работает правильно, DBCreateIndex не работает нормально в этом случае. FUNCTION dbCreateIndex( cOrderBagName, cKeyExpr, bKeyExpr, lUnique, cOrderName ) RETURN ordCreate( cOrderBagName, cOrderName, cKeyExpr, bKeyExpr, lUnique ) nbatocanin пишет: Эта программа только показывает как работает моя система для временных файлов. Вместо того: cFile := HB_DbCreateTemp() DBCreate (..) ... HB_FErase (cFile) ?????????????????? hb_dbCreateTemp( <cAlias>, <aStruct>, <cRDD>, <nConnection> ) -> <lSuccess> Generates and opens a new dbf with a temporary filename in exclusive mode, deletes it automatically when closed. All indexes created for this table, should ideally have the temporary flag on, so those get deleted too, when closed.

nbatocanin: USE (cPath + 'account') SHARED NEW index on kors to (cPath+'apt10/a.ntx') retu nil в результате в папке apt10 создался индекс a.ntx, как и заказывали, т.е. все отработало так, как и в DBFNTX Вы правы, я сделал что-то неправильно :( Извините.

nbatocanin: Петр пишет: ?????????????????? hb_dbCreateTemp( <cAlias>, <aStruct>, <cRDD>, <nConnection> ) -> <lSuccess> Я смешал с командой HB_FTempCreate, извините. Система, которую я использую есть "framework" для таких команд, как dbCreateTemp. Я не использую dbCreateTemp, потому что, когда я делал эту систему команда не существует. Но система работает точно так же, как вы писали, только обеспечивает более чистый код и более простое управление. Система поддерживает несколько команд, а не только временных файлов (FLock, RELATION, Order, FILTER..).

nbatocanin: Я нашел ошибку. Паша, пожалуйста, попробуйте эту программу: [pre2] DBRoot := "//192.168.99.10:2807/xApp" a := {{"t_name", "C", 30, 0}} DBCreate (DBroot + "\test", a) USE Test EXCLUSIVE ALIAS Test NEW Test->(DBAppend()) Test->t_name := "Bbbbbbb" Test->(DBAppend()) Test->t_name := "Aaaaaaa" Test->(DBAppend()) Test->t_name := "Ccccccc" cTmp := DbRoot + "\temp\test02" INDEX ON Upper(t_name) TO (cTmp) GO TOP Browse() // B, A, C ??? cTmp := DbRoot + "\temp\test03" INDEX ON Upper(t_name) TAG "test" TO (cTmp) GO TOP Browse() // A, B, C [/pre2]

Pasha: Поправил

nbatocanin: работает, большое спасибо! NB

Andrey: Сервер LetoDb начал аварийно падать... Прошёл год, не знал ни каких проблем... Падает так, что ручками заново приходиться поднимать... Сервер стоит на CentOS release 6.7 (Final), версия чуть-чуть подправленная. Вот что в логе: Leto DB Server v.2.15b3.1 ! INIT: DataPath=/apps/letodb/data, ShareTables=0, MaxUsers=500, MaxTables=5000, CacheRecords=10 07/30/16 15:12:59: /apps/letodb/bin/letoudf.hrb has been loaded. 30.07.2016 16:08:47: Error DBFCDX/1011 Write error: /apps/letodb/data/dbasezaivki/18_sklad.dbf ERROR! SETHRBERROR - pUStru not found!!!!!!!!!!!!!!!!!!!! leto_errInternal!!!!!!!!!!!!!!!!!! База 18_sklad.dbf то пишется, то не пишется... Индекс тоже делается через раз. Что за ошибка такая ? Подскажите в чем дело ?

Pasha: Andrey пишет: Сервер LetoDb начал аварийно падать... Прошёл год, не знал ни каких проблем... Падает так, что ручками заново приходиться поднимать... Сервер стоит на CentOS release 6.7 (Final), версия чуть-чуть подправленная. Вот что в логе: Leto DB Server v.2.15b3.1 ! INIT: DataPath=/apps/letodb/data, ShareTables=0, MaxUsers=500, MaxTables=5000, CacheRecords=10 07/30/16 15:12:59: /apps/letodb/bin/letoudf.hrb has been loaded. 30.07.2016 16:08:47: Error DBFCDX/1011 Write error: /apps/letodb/data/dbasezaivki/18_sklad.dbf ERROR! SETHRBERROR - pUStru not found!!!!!!!!!!!!!!!!!!!! leto_errInternal!!!!!!!!!!!!!!!!!! База 18_sklad.dbf то пишется, то не пишется... Индекс тоже делается через раз. Что за ошибка такая ? Подскажите в чем дело ? Мне практически невозможно ответить на этот вопрос. Сервер судя по всему не пересобирался, раз версия там 2.15b3.1. Что менялось ? Непонятно. Какой код приводит к ошибке ? Фиг его знает. Мне пару месяцев назад товарищ из-за рубежа начал жаловаться на падение сервере с ошибкой leto_errInternal!!!!!!!!!!!!!!!!!! Ничего, кроме этого сообщения в letodb.log я от него так и не добился. Ну и естественно, ничем не смог помочь. Это как раз похожий случай. Помочь я смогу, если буду иметь максимум информации о ситуации, приводящей к ошибке.

Andrey: Pasha пишет: Это как раз похожий случай. Помочь я смогу, если буду иметь максимум информации о ситуации, приводящей к ошибке. Паша, случай у меня оказывается тяжёлый. Третий день прога падает. Юзера задолбали, это еще выходные ... Сайт уже давно не трогал, программу отправки на сайт тоже не трогал. Тестировал прогу и сам сервер на Windows-машине, всё отлично идёт ! Беда оказывается в самой CentOS release 6.7 Она зараза работает даже когда на диске места нет ! Т.е. сервер LetoDB работает, когда приходит время записывать в базу, если десяток байт есть - пишет, нет - то не пишет и "падает" ! Всё просто оказывается - нет места на диске под базы и индекс. На Винде такой проблемы нет (там диски большие), а на CentOS есть (лимит на машину у провайдера). Было бы ОЧЕНЬ хорошо при сваливании такой ошибке в log-файле писалось сколько места на диске есть !!! Тогда хоть понимать будем в чём ошибка. Функции же нет в LetoDB - сколько свободного места на диске. Сделай тогда пожалуйста отладку в лог-файл в следующей версии сервера.

Pasha: Andrey пишет: Она зараза работает даже когда на диске места нет ! Фантастика какая-то. А разве тек бывает ? При современных то гигантских дисках. Да еще на сервере.

Dima: Pasha пишет: Фантастика какая-то. А разве тек бывает ? Паш так он же и пишет про лимит у прова , может там всего 20 метров арендовано ......

Петр: Andrey пишет: Было бы ОЧЕНЬ хорошо при сваливании такой ошибке в log-файле писалось сколько места на диске есть !!! Тогда хоть понимать будем в чём ошибка. Функции же нет в LetoDB - сколько свободного места на диске. Если внимательно разглядывать код server.prg, то можно найти REQUEST .. hb_DiskSpace Из чего следует, что выполнение строки типа leto_udf( "hb_diskSpace", leto_GetAppOptions(1), HB_DISK_FREE ) должно привести к желаемому результату. Как использовать результат и где его использовать вы можете придумать сами. Вариантов море, leto_udf(), leto_LockLock() помогут вам.

SergKis: Петр пишет leto_udf( "hb_diskSpace", leto_GetAppOptions(1), HB_DISK_FREE ) должно привести к желаемому результату В таком виде вряд ли, leto_GetAppOptions - ф-я сервера, попробовать так можно cPth := leto_udf( "leto_GetAppOptions", 1) и потом nFre := leto_udf( "hb_diskSpace", cPth, HB_DISK_FREE )Вариантов море, leto_udf(), leto_LockLock() помогут вам. это да

Петр: SergKis пишет: В таком виде вряд ли, leto_GetAppOptions - ф-я сервера, попробовать так можно cPth := leto_udf( "leto_GetAppOptions", 1) Ну да, увлекся, спасибо

Andrey: Dima пишет: пишет про лимит у прова , может там всего 20 метров арендовано ...... 2 Gb - забили архивами и базы место порядочно забирают... Больше года вообще не трогал сервер, падал несколько раз (5-7). А так работает сервер - как часы. SergKis пишет: cPth := leto_udf( "leto_GetAppOptions", 1) и потом nFre := leto_udf( "hb_diskSpace", cPth, HB_DISK_FREE ) Блин, опять городить огород. Ну есть же для Лето файловые функции, не ужели туда добавить как функцию нельзя ?

PSP: Имхо, не только (или не столько) свободное место на диске нужно контролировать, как обрабатывать ошибки записи/чтения. Они могут быть вызваны не только переполнением диска. Только вот что-то не соображу, как, используя letodb, это сделать. BEGIN/RECOVER/END работает с leto, кто знает?

SergKis: SergKis пишет cPth := leto_udf( "leto_GetAppOptions", 1) так не будет работать (и это правильно !!!) вызывать можно только UDF функцию, т.е. надо написать в letoudf.prg FUNCTION UDF_DiskSpace( nUserStru, cDrive, nProp ) If cDrive = Nil; cDrive := leto_GetAppOptions(1) EndIf If nProp = Nil; nProp := HB_DISK_FREE EndIf RETURN hb_diskSpace( cDrive, nProp )

Петр: SergKis пишет: так не будет работать (и это правильно !!!) Так работать будет !!! и это - не правильно

SergKis: Петр пишет Так работать будет !!! cPth := leto_udf( "leto_GetAppOptions", 1) не работает, проверил, еси бы рабтало, то опасно leto_udf( "fErase", myFile) и не нужны leto_ferase(...) и ключ ini EnableFileFunc = 0

Петр: SergKis пишет: не работает, проверил, ? leto_UDFEXIST( "leto_GetAppOptions" ) ? leto_UDFEXIST( "UDF_DiskSpace" ) ? leto_UDFEXIST( "STR" ) ? leto_UDF( "leto_GetAppOptions", 1 ) ? leto_UDF( "UDF_DiskSpace", "C:\", HB_DISK_FREE ) ? leto_UDF( "leto_GetAppOptions", 1 ) ? leto_UDF( "UDF_DiskSpace", "D:\", HB_DISK_FREE ) ? leto_UDF( "leto_GetAppOptions", 1 ) ? leto_UDF( "STR", 1 ) Проверьте еще

SergKis: Проверил [pre2] cPath := "//127.0.0.1:2812/temp/" cIP := "//127.0.0.1:2812/" ? "1. leto_GetAppOptions:", Leto_Udf("leto_GetAppOptions", 1) ? "2. leto_GetAppOptions:", Leto_Udf(cIP+"leto_GetAppOptions", 1) ? "3. leto_GetAppOptions:", Leto_Udf("leto_GetAppOptions") ? "4. leto_GetAppOptions:", Leto_Udf(cIP+"leto_GetAppOptions") результат: 1. leto_GetAppOptions: NIL 2. leto_GetAppOptions: NIL 3. leto_GetAppOptions: NIL 4. leto_GetAppOptions: NIL др. вызовUDF If ! leto_file(cPath+"test3"+".cdx") USE ( cPath+"test3" ) New INDEX ON NUM TAG KOD FOR ! deleted() INDEX ON NAME TAG NAM FOR ! deleted() INDEX ON NUM TAG DEL FOR deleted() USE ? "File has been created", cPath+"test3"+".cdx" EndIf USE ( cPath+"test3" ) New SHARED If OrdCount() > 0 OrdSetFocus(2) EndIf t := Seconds() dbGotop() If ! EOF() aEval({10,20,30,40,50,60,70,80,90,99}, {|nR,nE,lR| dbGoto(nR), lR := RLock(), iif(lR, dbDelete(), ), ; iif(lR, dbUnLock(), ) }) Leto_Udf("UDF_DeleteRecs", , , "DEL", , .F.) dbGotop() ENDIF ? "File has been opened", Used(), select(), Alias(), "Append UDF_...", IndexOrd(), OrdCount() j := Valtype(NUM) For i := 1 To 100 cRecBuf := Leto_Udf("UDF_AppendRec", 'NUM', {'KOD', 'DEL'}, 100) Leto_ParseRec( cRecBuf ) // ? i, RecNo(), NUM IF RLock() REPL NAME with "Name_"+strzero(RecNo(), 7), ; INFO with "Info_"+iif(j == "N", hb_ntos(NUM), NUM)+"-"+j dbUnLock() ENDIF ? i, RecNo(), NUM, NAME Next leto_commit() ? "File appendet", LastRec(), "time", seconds() - t USE ? "Ok" результат TEST_DBF ( //127.0.0.1:2812/temp/ ) File = test3.dbf File has been created //127.0.0.1:2812/temp/test3.dbf File has been created //127.0.0.1:2812/temp/test3.cdx File has been opened .T. 1 TEST3 Append UDF_... 2 3 1 1 101 Name_0000001 2 2 102 Name_0000002 3 3 103 Name_0000003 4 4 104 Name_0000004 5 5 105 Name_0000005 6 6 106 Name_0000006 7 7 107 Name_0000007 8 8 108 Name_0000008 9 9 109 Name_0000009 10 10 110 Name_0000010 11 11 111 Name_0000011 12 12 112 Name_0000012 13 13 113 Name_0000013 14 14 114 Name_0000014 15 15 115 Name_0000015 16 16 116 Name_0000016 17 17 117 Name_0000017 ... 97 97 197 Name_0000097 98 98 198 Name_0000098 99 99 199 Name_0000099 100 100 200 Name_0000100 File appendet 100 time 8.89 Ok [/pre2]

SergKis: PS Используемые UDF немного модифицированы под себя [pre2] FUNCTION UDF_AppendRec( nUserStru, cFieldName, xOrder, xMin ) LOCAL nPos := FieldPos( cFieldName ), nLen LOCAL xKey, lApp, lOver := .F., xOrd1, xOrd2, nDec IF ! Empty( xOrder ) IF Valtype(xOrder) == "A" xOrd1 := xOrder[1] xOrd2 := xOrder[2] ELSE xOrd1 := xOrder ENDIF OrdSetFocus( xOrd1 ) ENDIF IF leto_TableLock( nUserStru, 1 ) dbGoBottom() xKey := FieldGet( nPos ) IF Empty(xKey) .and. ! Empty(xMin) xKey := xMin IF ValType( xKey ) == "C" nLen := hb_FieldLen( nPos ) If left(xKey, 1) == "0" .and. ! "." $ xKey xKey := left(xKey+space(nLen), nLen) Else xKey := right(space(nLen)+xKey, nLen) EndIf ENDIF ENDIF IF ValType( xKey ) == "N" xKey ++ IF hb_FieldType(nPos) $ 'NF' lOver := xKey > Val( Replicate( "9", hb_FieldLen( nPos ) ) ) ELSEIF hb_FieldLen( nPos ) == 2 lOver := xKey > 0x7FFF ELSEIF hb_FieldLen( nPos ) == 4 lOver := xKey > 0x7FFFFFFF ENDIF ELSEIF ValType( xKey ) == "C" nLen := Len(xKey) If "." $ xKey nDec := nLen - RAt(".", xKey) xKey := Str( Val(xKey) + 1, nLen, nDec ) ElseIf left(xKey, 1) == "0" xKey := StrZero( Val(xKey) + 1, nLen ) Else xKey := Str( Val(xKey) + 1, nLen ) EndIf lOver := ('*' $ xKey) ENDIF IF lOver lApp := .F. ELSE lApp := ( UDF_Append( nUserStru, xOrd2 ) != Nil ) ENDIF IF lApp FieldPut( nPos, xKey ) dbCommit() ENDIF leto_TableUnLock( nUserStru, 1 ) ELSE lApp := .F. ENDIF RETURN if( lApp, leto_rec( nUserStru ), Nil ) FUNCTION UDF_Append( nUserStru, xOrder ) LOCAL lApp, lSetDel, xRet IF xOrder != Nil OrdSetFocus( xOrder ) ENDIF lSetDel := Set( _SET_DELETED, .f. ) dbGoTop() Set( _SET_DELETED, lSetDel ) IF Deleted() .and. Empty( OrdKeyVal() ) IF( lApp := leto_RecLock( nUserStru ) ) dbRecall() ENDIF ELSE dbAppend() IF ( lApp := ! NetErr() ) leto_RecLock( nUserStru, RecNo() ) ENDIF ENDIF IF lApp xRet := leto_rec( nUserStru ) ENDIF RETURN xRet [/pre2]

Петр: Не на то обратили внимание. Вы не получили правильного результата, но функция на сервере выполнялась! Вот протестируйте (не забудьте letodb_0.log посмотреть) ? leto_UDFEXIST( "STR" ) ? leto_UDF( "STR" ) ? leto_UDF( "STR", 20, 10, .t. )

SergKis: протестил [pre2] ? "1. leto_GetAppOptions:", Leto_Udf("leto_GetAppOptions", 1) ? "2. leto_GetAppOptions:", Leto_Udf(cIP+"leto_GetAppOptions", 1) ? "3. leto_GetAppOptions:", Leto_Udf("leto_GetAppOptions") ? "4. leto_GetAppOptions:", Leto_Udf(cIP+"leto_GetAppOptions") ? "5. STR exist :", leto_UDFEXIST( "STR" ) ? "6. STR :", leto_UDF( "STR" ) ? "7. STR, 20, 10, .t. :", leto_UDF( "STR", 20, 10, .t. ) ? "8. STR, 20, 10, 2 :", leto_UDF( "STR", 20, 10, 2 ) результат 1. leto_GetAppOptions: NIL 2. leto_GetAppOptions: NIL 3. leto_GetAppOptions: NIL 4. leto_GetAppOptions: NIL 5. STR exist : .T. 6. STR : NIL 7. STR, 20, 10, .t. : 0.0000000000 8. STR, 20, 10, 2 : 0.0000000000 letodb_0.log 127.0.0.1 LENOVO demo.exe 08/02/16 00:07:14: Error BASE/1099 Argument error: STR Arguments: ( [ 1] = Type: N Val: 0, [ 2] = Type: U) 08/02/16 00:08:52: Error BASE/1099 Argument error: STR Arguments: ( [ 1] = Type: N Val: 0, [ 2] = Type: U) [/pre2]

SergKis: PS. Leto DB Server v.2.17b2

SergKis: Проверил на сборке с hwg, получил 1. leto_GetAppOptions: . 2. leto_GetAppOptions: . 3. leto_GetAppOptions: . 4. leto_GetAppOptions: . 5. STR exist : .T. 6. STR : NIL 7. STR, 20, 10, .t. : 1.0000000000 8. STR, 20, 10, 2 : 1.0000000000 letodb_1.log 127.0.0.1 LENOVO Demo.exe 08/02/16 08:33:36: Error BASE/1099 Argument error: STR Arguments: ( [ 1] = Type: N Val: 1, [ 2] = Type: U) Если эти команды должны работать (я думал только из hrb и это мне нравилось), то надо копать у себя клиентов letodb 1. letodb server - borland, hb3.2 из последней MiniGui 2. letodb client - msvc, hb2.0 + MiniGui 2.07 + LV866 ( EN+LV+RU ) 2. letodb client - msvc, hb3.2 + Hwg 2.19.b6 + LV866 ( EN+LV+RU ) Udf функции из letoudf.hrb - работают ok! Петр спасибо за помощь

Pasha: Петр пишет: не работает, проверил, еси бы рабтало, то опасно leto_udf( "fErase", myFile) и не нужны leto_ferase(...) и ключ ini EnableFileFunc = 0 Такие опасные функции вызвать нельзя, поскольку они не прилинкованы к серверу. Вызывать можно функции, указанные в request, ну и некоторые другие. Я пересматривал этот набор - он вполне безопасен. Впрочем, я уже добавил параметр EnableUDF в letodb.ini, для возможности отключения udf. Пока не сбросил это изменение - есть некоторые проблемы с правами доступа git

SergKis: Pasha пишет Впрочем, я уже добавил параметр EnableUDF в letodb.ini, для возможности отключения ud

SergKis: Pasha пишет Такие опасные функции вызвать нельзя, поскольку они не прилинкованы к серверу. Тут не все просто, если реализовывать запрос на сервере типа (через udf) SELECT R_1,R_2,R_3,... AS Kod,Name,Cena,... From TablM01 WHERУ R_1=... ORDER BY upper(Name) то fErase, fRename вполне могут понадобитьсяб, а возможность их вызова с клиента должна быть исключена, т.е. hb_IsFunction("fErase") - не выполнять requect подключение leto_Udf("UDF_fErase") - можно выполнять при наличии в hrb мне как то так видится, при установке флага



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