:: Visual Foxpro, Foxpro for DOS
Эксперименты с FLUSH в VFP9
Sergey Fadeev

Сообщений: 106
Откуда: Чебоксары
Дата регистрации: 07.02.2005
Возникли тут некоторые ошибки с VFP9. Проект, который без проблем работал на VFP8SP1, вдруг стал неправильно читать после редактирования одиночную запись (она там всего одна) в свободной таблице. Новых возможностей не использовал (SET ENGINEBEHAVIOR 70). Сессия на том участке кода одна (глобальная), но таблица открывается дважды (в разных областях и процедурах) под разными алиасами. Так вот во 2-ой процедуре под другим алиасом читается старое содержимое записи, которая до этого уже была изменена в 1-ой процедуре. Ошибка довольно странная - если перед вызовом 2-ой процедуры (где будет повторно открыта таблица) в вызывающей процедуре поставить команды SELE <алиас таблицы> и BROWSE, то 2-ая процедура прочитает новое содержимое записи, если не ставить - прочитает старое. Видимо, передергивание указателя записи вызывает запись в какой-то другой, более низколежащий буфер, который потом успешно читается 2-ой процедурой. Повторяю, раньше (в VFP8SP1) этой проблемы не существовало. Попытка повторить ошибку в маленьком тесте не привела к успеху. Попытка сделать усеченный (рафинированный) тестовый пример из реального проекта также не привела к успеху - ошибка исчезла. Поэтому показать здесь ничего не могу...
Проблема решена постановкой любой из команд FLUSH FORCE или =SYS(1104) в начале 2-ой процедуры - ДО команд работы с таблицей.

Но это так - вступление. Может кого-то заинтересует. Может кто-то тоже на такое же наткнется и будет знать, как действовать...
А написать я хотел вот что. Данная ошибка сподвигла меня на то, чтобы поэкспериментировать с командой FLUSH. Тем более, что в VFP9 эта команда расширена.
Написал я программку, которая в цикле создает друг за другом одинаковые свободные таблицы по 100 тыс. записей с мемо-полями и не закрывает их. Сначала создаст и заполнит 1-ую таблицу, после чего переходит к созданию и заполнению 2-ой и т.д. Я сижу в FAR'е и наблюдаю за созданием и ростом этих таблиц (Ctrl+R - обновление содержимого панели с файлами).
И вот какие любопытные наблюдения получились (Windows 2000 SP4, ОЗУ 512 Мбайт):

1. Между созданиями и заполнениями таблиц нет никаких команд.
Порядок сброса буферов произвольный. Вполне возможна ситуация, когда создается уже 4-ая таблица (она появилась в каталоге), а 1-ая еще не заполнилась (определяю по размеру файла на диске) - т.е. сброс ее буферов еще не завершился. Причем в какие-то моменты времени 2-ая таблица может по размеру быть больше, чем 1-ая (сброс ее буферов идет с опережением предыдущей таблицы).
Мемо-поля всегда сбрасываются в первую очередь! Стандартная ситуация - размер .fpt уже полный, а размер .dbf не достигает даже 20% от итогового.

2. После заполнения очередной таблицы перед переходом к созданию следующей стоит команда FLUSH.
Такого растягивания на 4 таблицы, как в предыдущем случае, уже нет. Максимум, что возможно - у предыдущей таблицы осталось заполнить небольшой хвостик, а следующая таблица уже создана. Да и то это наблюдается не всегда. Мемо-поля всегда заполняются строго в первую очередь.

3. После заполнения очередной таблицы перед переходом к созданию следующей стоит команда FLUSH FORCE.
Таблицы появлялись и заполнялись строго по очередности. Никогда не было такого, что появлялась следующая таблица, а предыдущая еще не до конца заполнена. Но заполнение мемо-полей идет раньше основной таблицы.

Отсюда выводы:
FLUSH без FORCE действительно не всегда реально сбрасывает буфера на диск. Если действительно требуется сброс на диск, то следует использовать FLUSH FORCE. FLUSH без FORCE скорее всего действительно работает только на уровне самого VFP, а ОС может это дело и проигнорировать.
Следует учесть, что мемо-поля в промежутках между FLUSH [FORCE] из-за особенностей внутреннего алгоритма VFP всегда заполняются быстрее. Поэтому в случае внезапного сбоя вполне возможна ситуация, когда файл .fpt содержит больше информации, чем файл .dbf. Не вижу в этом ничего особенно страшного, т.к. все равно в файле .dbf хранятся всего лишь указатели на .fpt. Т.е. "бесхозное" содержимое .fpt не будет видно из программ. Единственное, что может досаждать - впустую занятое место в файле. Но .fpt файлы этим грешат и по другим причинам, поэтому тем, кому не нужна излишняя "припухлость" .fpt файлов, все равно надо периодически их "утрамбовывать". Естественно, что сказанному выше следует верить с оговоркой на возможные погрешности из-за неизвестного мне алгоритма приращения размера .fpt файлов...
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi Sergey!

Цитата:
Возникли тут некоторые ошибки с VFP9
Если всё-же сможешь сделать пример демонстрирующий ошибку - не поленись и
выложи. Такое 100% возникает при работе РАЗНЫХ процессов - т.е. фокс с одной
стороны действительно не сбрасывает "вновь записанное" на диск, а с другой -
не читает данные с диска (чего снова то читать, если я буквально секунду
назад их считывал) - обсуждалось это и примеры были в FIDO - поищи через
Google - причём ИМЕННО в VFP9 был исправлен один баг - при повторном
открытии (USE AGAIN или просто USE в другой DS) таймер "запрета на
считывание с диска" не обнулялся, а выставлялся в максимум (2-й параметр SET
REFRESH) - т.е. в течении тех самых 5-ти секунд (это по умолчанию) нельзя
было получить с диска "свежие" данные - независимо от того когда РЕАЛЬНО они
последний раз считывались - именно SYS(1104) и помогал - вкупе конечно с
"инициацией считывания" - например GO RECNO() или Refresh на "связанный"
грид.
Чтобы такое происходило в рамках ОДНОЙ программы - для меня новость. Тем
паче что физически то файл открывается лишь один раз, и его кэш-структуры
как я понимаю тоже едины на все USE AGAIN копии-курсоры... (про фоксовую
буферизацию не говорим - буфера там конечно разные)...
Вообще поток IO операций можно посмотреть в fileMon - правда не для таких
больших объёмов - а то погрязнешь в логе
А насчёт fpt - там очень простая структура - BlockSize рулит размером
минимального блока... Гораздо опаснее наличие в dbf ссылки на
"несуществующий" блок (за пределами fpt файла) чем наличие "левого" блока -
это ты правильно подметил...




------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Naomi

Сообщений: 1796
Дата регистрации: 09.10.2003
Еще один момент на тему FLUSH FORCE. Если открыто много таблиц, надо обязательно указывать алиас таблицы, для которой надо выполнить FLUSН. В противном случае, система будет зависать (надо же, не могу подобрать хороший аналог performance degradation). Мы хотели перейти на VFP9 месяца 3 назад. Но я в save методе добавила FLUSH FORCE (потом сообразила, что сделала я это напрасно, т.к. используется транзакция). Так вот, торможение было ужасным. Мой коллега выявил, что происходило это из-за FLUSH FORCE (что интересно, кстати, VFP8 compiler это просто игнорировал). К сожалению, эта моя ошибка дорогого стоила - мы отказались от VFP9 на 3+ месяца. Переходим на 9 со след. недели...
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Владимир11111
Уважаемые знатоки FoxPro!
Очень интересна проблема , поднятая Сергеем.
Если не в тягость, обсудите для посетителей форума.
Очень полезно всем.
С уважением, Владимир.
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
PaulWist
Автор

Сообщений: 14618
Дата регистрации: 01.04.2004
Надя.

Какие еще подводные камни нащупали в 9-ке?




------------------
Есть многое на свете, друг Горацио...
Что и не снилось нашим мудрецам.
(В.Шекспир Гамлет)
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
PaulWist
Автор

Сообщений: 14618
Дата регистрации: 01.04.2004
До кучи к FLUSH FORCE, эта команда не является панацеей, к сожалению фоксовский обработчик ошибок не принимает статуса её завершения и отловить событие отработала команда или нет - не представляется возможным, обсуждение эксперимента было здесь forum.foxclub.ru




------------------
Есть многое на свете, друг Горацио...
Что и не снилось нашим мудрецам.
(В.Шекспир Гамлет)
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Naomi

Сообщений: 1796
Дата регистрации: 09.10.2003
Еще проблема с listbox in container. Не получается мышью выделить. Мы эту проблему обнаружили в MereMortals Biz Obj Builder. На UT не так давно обсуждалось...

Еще проблема с BuilderX & BuilderB... И известный баг в RI builder (typo).

Вроде бы все, но мы еще не начали использовать VFP9 в полной мере по моей вине. К счастью, должны перейти со след. недели.
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
piva

Сообщений: 18655
Откуда: Курган
Дата регистрации: 24.03.2004
Не пока SP1 не выйдет - ничего переводить под 9-ку - не буду




------------------
Часто бывает так, что есть над чем задуматься, а нечем.
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Sergey Fadeev

Сообщений: 106
Откуда: Чебоксары
Дата регистрации: 07.02.2005
Igor Korolyov
Если всё-же сможешь сделать пример демонстрирующий ошибку - не поленись и выложи.
Ох и муторное же дело из большого проекта пытаться выкусить рафинированный образец. Там ведь десятки других переменных, объектов, открытых таблиц - черт знает, где они могут перекрестно влиять друг на друга и на менеджеры памяти и буферов таблиц. Но попробую сделать еще одну попытку...

Nadya_N
В противном случае, система будет зависать (надо же, не могу подобрать хороший аналог performance degradation).
Русский программистский аналог - "система будет [сильно] тормозить".
"Тормозить" - работать, но медленно. Но ведь все-таки работать! .
"Зависнуть" - это уже хуже, т.к. управление компьютером окончательно потеряно - поможет только перезагрузка.
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Равиль

Сообщений: 6549
Откуда: Уфа
Дата регистрации: 01.08.2003
Интересные наблюдения. Еще одна возможная причина такого поведения - особенности фокса при работе с таблицами в гриде.
Может не в тему, но как-то участвовал в обсуждении одной странной ошибки (109) возникающей при редактировании таблицы в гриде.
Сейчас порылся в поиске и увидел, что она снова поднималась matod forum.foxclub.ru - действительно обескураживающее сообщение, особенно для случая эксклюзивно открытой таблицы. Интуитивное решение проблемы нашлось - явный благополучный сброс буфера перед переходом на другую запись, но причина осталась непонятной.
Поработал в 9-ке и кое-что прояснилось благодаря ошибке (2072), гласящей о том, что нельзя дескать переходить на другую запись, пока не сброшены изменения в текущей. Вопрос - а почему нельзя то? Думаю, что возможная причина в том, что грид является контейнером контролов для которых можно прописать Valid, но эти контролы связаны с полями таблицы. С другой стороны, ничто не мешает изменить эти значения из-вне грида (например командами replace), а как обеспечить согласованность этих изменений и соблюсти валидность в гриде? Вот видимо поэтому фокс и устанавливает временный признак блокировки записи неким "another user" на время редактирования в гриде и снимает его при сбросе буфера, в большистве случаев сброс происходит благополучно и незаметно, а при неудаче ловим в ранних версиях (109), а в 9-ке наконец есть комментарий (2072), правда без объяснения, почему необходим сброс буфера при переходе на другую запись в гриде даже при 5-й буферизации.
Может кто-то сможет прояснить ситуацию ?




------------------
Тяжело согнать курсором муху с монитора ...
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi Nadya_N!

Этого и стоит ожидать - процесс сброса ВСЕХ буферов это недешевая
операция...




------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Sergey Fadeev

Сообщений: 106
Откуда: Чебоксары
Дата регистрации: 07.02.2005
Цитата:
Если всё-же сможешь сделать пример демонстрирующий ошибку - не поленись и выложи.
Удалось сделать такой пример. И немножко поиграть с условиями возникновения ошибки.
Просьба к знатокам VFP - посмотрите, пожалуйста, чего я там понаписал. А то я на VFP не так давно и поэтому не так уверенно себя чувствую, как в FPD. Может я чего-то там "не включил", может какие-то установки по умолчанию приводят к такому эффекту. Если я виноват - посоветуйте, что "включить". Если это действительно ошибка VFP9 - может кто-нибудь, кто общается с Алексеем Цингаузом (насколько я понял, этими вещами занимается он), напишет ему.

А вот собственно сам пример:
** Критичные для появления или подавления ошибки участки кода
** выделены логическими блоками if ... endif.
** Блок if .t. нужен для появления ошибки. Если его условие
** поменять на .f. - ошибка исчезнет.
** Блок if .f. способен подавить ошибку. Если его условие
** поменять на .t. - ошибка исчезнет.
set echo off
set enginebehavior 70
clear all
close all
** Форма нам нужна, т.к. без формы (в этой же или
** в отдельной процедуре) те же самые команды, что
** и в форме, работают без ошибок.
_myForm=NewObject("myForm")
define class myform as form
proc Init()
set safe off
create table table1 (string1 c(5))
insert into table1 (string1) values ('1')
if .t.
** Если не переоткрыть таблицу в расшаренном
** режиме, то ошибка исчезнет. Видимо, в
** монопольном режиме движок работы с данными
** работает несколько иначе. Возможно, что
** именно расшаренный режим создает "левый"
** дополнительный буфер, который не всегда
** обновляется и из которого потом читает
** данные SQL-выборка.
use in table1
if .t.
use table1 share
else
use table1 excl
endif
endif
if .t.
** Этот хотя бы скрытый объект на форме нужен.
** Без него ошибки не будет.
This.NewObject('string1','textbox')
with This.string1
.Enabled=.f.
.Visible=.f.
if .t.
** У этого скрытого объекта должна быть
** связь с таблицей, иначе ошибки не будет.
.ControlSource = "table1.string1"
endif
if .t.
** Вот для чего нужен скрытый объект на форме!
** Не будет этой команды - не будет и ошибки.
** Это место - самое удивительное и непонятное для меня.
** Как может присвоение значения контролу так поменять
** поведение движка работы с данными?
** Присвоение значения включает "левый" буфер,
** но значение записывается НЕ В НЕГО, а в другое
** место! Не будь этой команды, "левый" буфер
** не включился бы. Поразительно...
.Value=''
endif
endwith
endif
** Сейчас сделаем изменение в таблице,
** которое будет видимым только частично:
** при прямом обращении к полю таблицы оно видно,
** а вот SQL-выборка его не увидит.
sele table1
replace string1 with ''
if .f.
** Команда, устраняющая ошибку.
** Странно, но в данном случае команда =sys(1104) не способна
** справиться с ошибкой, хотя в моем реальном проекте
** она выручала.
flush
endif
** Нижеследующая SQL-выборка читает из "левого" буфера.
** Поэтому она не видит запись с пустым значением string1.
sele *;
from table1;
into cursor cursor1;
where empty(string1)
if reccount('cursor1')>0
wait window 'Ошибки нет'
else
wait window 'Ошибка ЕСТЬ!'
endif
use in cursor1
use in table1
return .f.
endproc
enddefine
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Sergey Fadeev

Сообщений: 106
Откуда: Чебоксары
Дата регистрации: 07.02.2005
Забыл добавить. В принципе роль "левого" буфера может выполнять более низкоуровневый буфер (упомянутый в моем первом сообщении), который не успел обновиться...
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi Sergey!

Это явно НЕ относится к первоначальной ситуации - тут ты сам, вполне
намеренно поставил фокс в заведомо ошибочную ситуацию. У тебя есть контрол
привязанный к полю - в него введено ОДНО значение (причём отличное от
изначального и оно НЕ сброшено в таблицу - вообще для невидимого и
неактивного текстбокса это весьма проблематично сделать). Потом "напрямую"
ты вводишь ДРУГОЕ значение в это-же поле, но поскольку ты не сделал
txt.Refresh или по иному не обновил привязанный контрол, ты и имеешь
описанный конфликт. Я не знаю почему FLUSH действует на буфер контрола -
возможно это так и задумано... Также очень странно, но добавление к запросу
опции WITH (BUFFERING=.T.) тоже решает проблему, хотя я и не думал что это
должно работать с неявными буферами контролов...
А вот про какие ещё неявные буферы идёт речь (те что существуют БЕЗ наличия
привязанных контролов с модифицированным Value) я пока так и не понял...
P.S. Ну очень запутанный и нетривиальный код (в плане его работы, а не
синтаксиса конечно) - неужели реально где-то необходима такая сложная и
нестандартная совокупность/последовательность действий?
P.P.S. set enginebehavior 70 лучше убрать (пуская и не сразу, а постепенно
исправляя те ошибки, про которые фокс раньше не беспокоился) - это
"аварийная" установка... С "новыми возможностями" она не имеет ничего
общего - это лишь исправление старых ошибок.
P.P.P.S. Да. ещё - приведенный код работает одинаково и на VFP7SP1 (без SET
конечно) и на VFP8SP1 - поскольку тут ошибка именно в логической
последовательности действий с контролами а не с чем-то ещё...




------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
MichaelD

Сообщений: 7578
Дата регистрации: 14.05.2005
Hi, Igor & All!

Только чтобы отделить один вопрос от другого: "сохранение изменений" != "получение последних изменений"...

1) О "получение последних изменений":

Цитата:
фокс с одной стороны действительно не сбрасывает "вновь записанное" на диск, а с другой -
не читает данные с диска (чего снова то читать, если я буквально секунду назад их считывал) - обсуждалось это и примеры были в FIDO - поищи через Google
Да именно в этом контексте и обсуждалось... на стороне клиента, стремящегося "на автомате" получить "свежие данные"

Цитата:
выставлялся в максимум (2-й параметр SET REFRESH) - т.е. в течении тех самых 5-ти секунд (это по умолчанию) нельзя было получить с диска "свежие" данные - независимо от того когда РЕАЛЬНО они последний раз считывались
ну т.е. второй параметр у SET REFRESH "рулит авто-обновлениями" данных... а "принудительное" обновление можно осуществить явно:

Цитата:
именно SYS(1104) и помогал - вкупе конечно с "инициацией считывания" - например GO RECNO() или Refresh на "связанный" грид.
SYS(1104) + GO RECNO() + AnyObject.Refresh() -> приводили к явному получению+отображению свежих данных...

2) О "сохранение изменений":

после любой попытки "удачно завершённой транзакции" (TableUpdate()=.T.) следует попросить систему "освободить буфера" -> FLUSH [FORCE or Calls the Windows API FlushFileBuffers], ... это следует видимо ДЕЛАТЬ ВСЕГДА (если у вас сохранение не на VFP-COM+ сервере, где МОЖЕТ БЫТЬ? могут быть послабления ;) )



Отредактировано (26.08.05 11:04)


------------------
С уважением,
Михаил Дроздов, Пермь, Россия
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Sergey Fadeev

Сообщений: 106
Откуда: Чебоксары
Дата регистрации: 07.02.2005
Цитата:
Это явно НЕ относится к первоначальной ситуации - тут ты сам, вполне намеренно поставил фокс в заведомо ошибочную ситуацию.
Согласен - приведенный пример ОЧЕНЬ СИЛЬНО отличается от реального кода. Я специально все ОЧЕНЬ СИЛЬНО упростил, т.к. тянуть кучу процедур, полей и файлов смысла не было.

Цитата:
У тебя есть контрол привязанный к полю - в него введено ОДНО значение (причём отличное от изначального и оно НЕ сброшено в таблицу - вообще для невидимого и неактивного текстбокса это весьма проблематично сделать).
В реальном коде этот textbox видимый, активный, далеко не последний в списке редактируемых полей и в него ручками вбивали информацию, которая, тем не менее, не видна в выборке. В реальном коде не видны и изменения в полях, НЕ ПРИВЯЗАННЫХ к каким-либо контролам, сделанные только через REPLACE. Просто я здесь для упрощения не стал вводить 2 поля - думал, что и так будет понятно.
Специально для этого случая переделал тестовый пример.

Цитата:
Потом "напрямую" ты вводишь ДРУГОЕ значение в это-же поле, но поскольку ты не сделал txt.Refresh или по иному не обновил привязанный контрол, ты и имеешь описанный конфликт.
Во-первых, см. переделанный тестовый пример. Та же ситуация с полем, несвязанным с каким-либо контролом. У меня в реальном проекте все так и происходило. Так что txt.Refresh здесь непричем.
Во-вторых, я понимаю, если бы игнорировалось значение, присвоенное только через .Value='' (или введенное ручками в видимом контроле). Но как объяснить, что игнорируется и команда REPLACE для поля, которое на форме НИКАК НЕ ПРЕДСТАВЛЕНО? Причем новое значение, введенное через REPLACE, видно при обращении к этому полю и невидимо для SQL-выборки!
Очень жаль, что ты увидел только эффект контрола и не заметил REPLACE, делающий то же самое и точно так же игнорируемый. Присвоение контролу в примере нужно только для появления эффекта ошибки (об этом я писал), а НЕ ДЛЯ ТОГО, чтобы сказать - смотрите, .Value='' не обновляет таблицу. Именно на REPLACE я и обращал внимание...

Цитата:
Я не знаю почему FLUSH действует на буфер контрола - возможно это так и задумано...
Для меня это тоже непонятно...

Цитата:
Также очень странно, но добавление к запросу опции WITH (BUFFERING=.T.) тоже решает проблему, хотя я и не думал что это должно работать с неявными буферами контролов...
Так вот что надо включить для подстраховки! Спасибо за подсказку! Прочитал теперь о этой новой фиче в хелпе. Хотя значение по умолчанию для WITH (BUFFERING=lExp) меня несколько обескураживает. Не таким оно должно быть на самом деле. Т.е. в общем случае все те многочисленные изменения, которые успевает сделать какая-либо программа, будут попросту по умолчанию не видны САМОЙ ЖЕ ПРОГРАММЕ в ее SQL-выборках. Я понимаю, когда невидимы изменения, сделанные другими пользователями в сети, до тех пор, пока они не сбросят изменения на сетевой диск. Но чтобы были невидимы СОБСТВЕННЫЕ, причем ПРЯМЫЕ изменения в ТАБЛИЦЕ!...

Цитата:
А вот про какие ещё неявные буферы идёт речь (те что существуют БЕЗ наличия привязанных контролов с модифицированным Value) я пока так и не понял...
Посмотри обновленный пример. Там поле теперь вообще не привязано к контролу, но ситуация все та же...

Цитата:
P.S. Ну очень запутанный и нетривиальный код (в плане его работы, а не синтаксиса конечно) - неужели реально где-то необходима такая сложная и нестандартная совокупность/последовательность действий?
Как я уже сказал, все очень сильно переделано специально под пример - чтобы было все рядом, вместе, в одном методе и без показа чего-либо на экране. Конечно же, никто не будет все пихать в Init() и делать формы без видимых объектов. В реальном проекте все распихано по соответствующим методам, событиям, процедурам и логика там другая. Я просто сконцентрировал в одном месте последовательность команд, приводящую к ошибке...

Цитата:
set enginebehavior 70 лучше убрать
Убрал из примера. В проекте буду убирать постепенно...

Цитата:
Да. ещё - приведенный код работает одинаково и на VFP7SP1 (без SET конечно) и на VFP8SP1 - поскольку тут ошибка именно в логической последовательности действий с контролами а не с чем-то ещё...
У меня под рукой VFP8SP1 уже не было - поэтому не проверял. Сейчас сходил, проверил на чужой машине - действительно, и в VFP8SP1 такая же ситуация! А в реальном проекте VFP8SP1 работал корректно! Значит, что-то я слишком уж сильно упростил - наверное, выкинул какие-нибудь несущественные на первый взгляд команды, которые "гасили" ошибку в VFP8SP1. Там действительно очень тяжело изначально догадаться, какая же команда уберет ошибку...

Вот измененный пример:

** Критичные для появления или подавления ошибки участки кода
** выделены логическими блоками if ... endif.
** Блок if .t. нужен для появления ошибки. Если условие хотя бы
** одного такого блока поменять на .f. - ошибка исчезнет.
** Блок if .f. способен подавить ошибку. Если условие хотя бы
** одного такого блока поменять на .t. - ошибка исчезнет.
set echo off
clear all
close all
** Форма нам нужна, т.к. без формы (в этой же или
** в отдельной процедуре) те же самые команды, что
** и в форме, работают без ошибок.
_myForm=NewObject("myForm")
define class myform as form
proc Init()
set safe off
create table table1 (str4box c(5), str4base c(5))
insert into table1 (str4box, str4base) values ('1','1')
if .t.
** Если не переоткрыть таблицу в расшаренном режиме,
** то ошибка исчезнет. Видимо, в монопольном режиме
** движок работы с данными работает несколько иначе.
** Возможно, что именно расшаренный режим:
** - или создает некий буфер, который не всегда
** обновляется, и из которого потом читает данные
** SQL-выборка.
** - или вовремя не обновляет некий существующий буфер,
** из которого потом читает данные SQL-выборка.
use in table1
if .t.
use table1 share
else
use table1 excl
endif
endif
if .t.
** Этот (можно даже скрытый) объект на форме нужен.
** Без него ошибки не будет.
This.NewObject('textbox1','textbox')
with This.textbox1
.Enabled=.t.
.Visible=.t.
if .t.
** У этого объекта должна быть
** связь с таблицей, иначе ошибки не будет.
.ControlSource = "table1.str4box"
endif
if .t.
** Вот для чего нужен объект на форме!
** Не будет этой команды - не будет и ошибки.
** Это место - самое удивительное и непонятное для меня.
** Как может присвоение значения контролу так поменять
** работу с буферами? Поразительно...
.Value=''
endif
endwith
endif
** Сейчас сделаем изменение в таблице,
** которое будет видимым только частично:
** при прямом обращении к полю таблицы оно видно,
** а вот SQL-выборка его не увидит.
sele table1
replace str4base with ''
if !empty(str4base)
** Сюда мы в любом случае не должны зайти.
** Это просто проверка на прямое чтение из поля.
wait window 'Странно, что значение в поле не записалось...'
endif
if .f.
** Команда, устраняющая ошибку.
** Странно, но в данном случае команда =sys(1104) не способна
** справиться с ошибкой, хотя в моем реальном проекте
** она выручала.
flush
endif
** Нижеследующая SQL-выборка читает из необновленного буфера.
** Поэтому она не видит запись с пустым значением str4box.
if .t.
sele *;
from table1;
into cursor cursor1;
where empty(str4base)
else
** В VFP9 появилась возможность читать данные из буфера.
** Это устраняет ошибку, но требует явного задания, т.к.
** по умолчанию читаются данные с диска. Т.е. в общем случае
** все те многочисленные изменения, которые успевает сделать
** какая-либо программа, будут попросту по умолчанию не видны
** САМОЙ ЖЕ ПРОГРАММЕ в ее SQL-выборках. Я понимаю, когда
** невидимы изменения, сделанные другими пользователями в сети,
** до тех пор, пока они не сбросят изменения на сетевой диск.
** Но чтобы были невидимы СОБСТВЕННЫЕ, причем ПРЯМЫЕ изменения
** в ТАБЛИЦЕ!
sele *;
from table1;
with (buffering=.t.);
into cursor cursor1;
where empty(str4base)
endif
if reccount('cursor1')>0
wait window 'Ошибки нет'
else
wait window 'Ошибка ЕСТЬ!'
endif
use in cursor1
use in table1
return .f.
endproc
enddefine
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi Sergey!

Да, запутал ты дело слегка ;) Ну зато тема интересная.
Наличие "активного" текстбокса действительно тут ключевой момент.
Цитата:
В реальном коде не видны и изменения в полях, НЕ ПРИВЯЗАННЫХ к
каким-либо контролам
Поскольку дело не в "поле", а в "записи".
Цитата:
Просто я здесь для упрощения не стал вводить 2 поля - думал, что и
так будет понятно
Нет, не понятно. Теперь значительно лучше Хотя для ПОЛНОТЫ понимания
нужно было-бы и саму форму сделать видимой - т.к. без этого никак не
"обновить" текстбокс...
Цитата:
Та же ситуация с полем, несвязанным с каким-либо контролом
Да, поскольку не сохранилась ВСЯ запись. Это можно наблюдать, если запустить
параллельно второй экземпляр фокса, и организовать в нём принудительное
чтение данной таблицы (например SET REFRESH 1, 1), а сам тестовый пример
запустить пошагово.
Цитата:
Но как объяснить, что игнорируется и команда REPLACE для поля,
которое на форме НИКАК НЕ ПРЕДСТАВЛЕНО
Оно не игнорируется, просто не происходит ЗАПИСИ данных на диск (точнее в
кэш ОС но тут это без разницы), об этом ниже.
Цитата:
Причем новое значение, введенное через REPLACE, видно при обращении к
этому полю
Да, прямые обращения запрашивают данные из фоксового КЭША - т.е. даже то что
на диск ещё не сохранено, то будет видно таким (xBase) командам.
Цитата:
и невидимо для SQL-выборки
Да верно! Потому что на самом деле SQL запрос читает данные с ДИСКА (опять
же точнее говоря - из кэша ОС), даже имеющийся внутренний фоксовый кэш (даже
не буфер при включенной буферизации!) игнорируется. Я заметил REPLACE, но не
мог понять почему и он ведёт себя неадекватно, теперь же увидел
Цитата:
Присвоение контролу в примере нужно только для появления эффекта
ошибки
ИМЕННО! При таком присвоении возникает БЛОКИРОВКА!Добавь в отладчике в
watch окно ISRLOCKED() и проследи за его поведением! Это IMHO ключевой
момент. .Value = ... взводит блокировку, и потом НИКТО эту блокировку не
снимает! А фокс из-за этого и не производит записи данных на диск, поскольку
запись блокирована.
Цитата:
Именно на REPLACE я и обращал внимание...
Дело в том, что REPLACE обновляющий ОДНУ запись работает по примерно
следующей схеме:
- блокирует запись
- вносит изменения
- разблокирует
При этом фокс обычно производит физическую запись (ну точнее переправляет
данные из своих внутренних кэширующих структур ОС - сама ОС тоже может
некоторое время держать данные в памяти - но это уже другой вопрос) в момент
разблокирования.
ОДНАКО если блокировка была наложено ДО выполнения REPLACE, то он не
блокирует (т.к. 2 раза заблокировать нельзя) и не разблокирует запись!
Соответственно никакой записи и не происходит!
Теперь понятна роль FLUSH - он производит разблокирование, и данные из кэша
уходят дальше.
WITH (BUFFERING=.F.) действующее по умолчанию наверное более корректно
определить как "читать данные с диска или из кэша ОС" - т.е. отметить что не
только "мимо" буферизации, но и мимо внутреннего фоксового кэша таблицы.
Цитата:
Хотя значение по умолчанию для WITH (BUFFERING=lExp) меня несколько
обескураживает. Не таким оно должно быть на самом деле
Нет именно таким - это обеспечивает совместимость со старыми версиями -
первостепенное отличие VFP от прочих сред
Цитата:
Т.е. в общем случае все те многочисленные изменения, которые успевает
сделать какая-либо программа, будут попросту по умолчанию не видны САМОЙ ЖЕ
ПРОГРАММЕ в ее SQL-выборках
Не совсем так... Все те изменения которые остались в буфере (при включенной
буферизации - при этом лишь для режимов 4 и 5 это будут "многочисленные
изменения"), и те, которые остались в заблокированных записях (обычно это
ОДНА запись - если принудительно не блокировать несколько записей).
Поскольку авто-блокировки (в т.ч. и та которую мы рассматриваем - т.е.
накладываемая при .Value = ...) снимаются например при перемещении на другую
запись...
Цитата:
Но чтобы были невидимы СОБСТВЕННЫЕ, причем ПРЯМЫЕ изменения в
ТАБЛИЦЕ!...
В том то и дело что они не "прямые", а идут сначала в буфер (если он есть),
потом в фоксовый кэш (а SQL запросы идут МИМО этого кэша) и лишь в конце (в
частности при снятии блокировки или при FLUSH) уходят далее - в файловый кэш
ОС - уже оттуда как я понимаю они будут видны ВСЕМ прогам работающим на
данной машине - однако вовсе не факт что они будут записаны на диск, а в
случае сетевого файла - отправлены на сервер в его кэширующие структуры...
Кстати говоря поможет любая команда или настройка, снимающая блокировку,
наложенную "вводом данных в контрол". Я например ВЕЗДЕ перед командами
работы с таблицами, перевожу фокус с текущего контрола формы, на специальный
"пустой" текстбокс, и конечно проверяю перешёл ли реально фокус, или его
"заблокировал" код в Valid или LostFocus - это вызывает срабатывание всех
стандартных процедур - в частности и запись содержимого контрола в курсор, и
видимо разблокировку записи...
Тебе поможет GO RECNO(), UNLOCK, перевод фокуса не другой контрол (конечно
форма должна быть для этого видима, и в ДАННОМ примере нужно будет добавить
This.Show() + DOEVENTS + This.textbox2.SetFocus())
Также помогает в ДАННОМ ПРИМЕРЕ SET AUTOSAVE ON (не уверен что в реальной
программе поможет, особенно после прочтения невнятного описания этой
настройки в хелпе )

P.S. Если бы на данный вопрос обратил внимание Aleksey Tsingauz и дал более
точное описание процесса, с точки зрения внутренней организации работы фокса
с таблицами и с контролами - было бы конечно ещё лучше




------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Sergey Fadeev

Сообщений: 106
Откуда: Чебоксары
Дата регистрации: 07.02.2005
Привет, Игорь!

Спасибо за пояснения. Но хотелось бы высказать свои мысли тоже.

Цитата:
Хотя для ПОЛНОТЫ понимания нужно было-бы и саму форму сделать видимой - т.к. без этого никак не "обновить" текстбокс...
Тут без разницы - делай ее видимой или невидимой - все равно ошибка. Просто мне показалось, что будет нагляднее, если код будет не срабатывать даже без визуальных выкрутасов и без отвлечения на что-либо - вот я и сделал форму невидимой.

Цитата:
Потому что на самом деле SQL запрос читает данные с ДИСКА (опять же точнее говоря - из кэша ОС), даже имеющийся внутренний фоксовый кэш (даже не буфер при включенной буферизации!) игнорируется.
Вот это как раз и плохо.

Цитата:
Теперь понятна роль FLUSH - он производит разблокирование, и данные из кэша уходят дальше.
На мой взгляд, это неправильно - не должен этим FLUSH заниматься. Если я хочу сбросить буфера, то я должен иметь такую возможность без всяких побочных эффектов типа снятия блокировок.

Цитата:
Цитата:
Хотя значение по умолчанию для WITH (BUFFERING=lExp) меня несколько обескураживает. Не таким оно должно быть на самом деле
Нет именно таким - это обеспечивает совместимость со старыми версиями - первостепенное отличие VFP от прочих сред
Как уже говорил, я пришел в VFP после многих лет работы в FPD. В FPD я, делая REPLACE какого-либо поля, никаким способом уже не мог прочитать старое значение из этого поля. Хоть SQL, хоть не SQL. И считаю это правильным решением. И поэтому значение по умолчанию WITH (BUFFERING=lExp) логичнее было бы сделать таким, как в FPD. Как раз для обеспечения совместимости со старыми версиями.

В VFP сделали много наворотов для буферизации. Насколько я догадываюсь, для удобства. Удобно не работать с копиями полей в переменных памяти, а работать напрямую с полями. И только по определенной команде фиксировать эти изменения. Т.е., как я понимаю, создается некая виртуальная среда данных, некий "воздушный замок", в котором значения полей еще нереальны. И будут ли они реальны - зависит от решения программиста. Т.е. форма оперирует со своим контекстом, со своей виртуальной средой. Так вот хотелось бы, чтобы эта концепция была логичной. Если мы работаем в некой виртуальной среде данных, то и ВСЕ команды работы с данными ВНУТРИ ЭТОЙ ФОРМЫ должны работать в контексте этой формы.

Цитата:
Я например ВЕЗДЕ перед командами работы с таблицами, перевожу фокус с текущего контрола формы, на специальный "пустой" текстбокс, и конечно проверяю перешёл ли реально фокус, или его "заблокировал" код в Valid или LostFocus - это вызывает срабатывание всех стандартных процедур - в частности и запись содержимого контрола в курсор, и видимо разблокировку записи...
Мда... Что-то мне такие "обходные маневры" не нравятся. По-моему, это уже признак того, что что-то в концепции напутано. Мне кажется, что интерфейс (контролы, их буфера и их Valid(), LostFocus()), блокировку и работу с движком данных не стоило вязать в такой узел, раз приходится ТАК перестраховываться.
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi Sergey!

Цитата:
Цитата:
Потому что на самом деле SQL запрос читает данные с
ДИСКА
Вот это как раз и плохо.
Это исторически сложилось - на самом деле в FPD всё было точно так-же.
Единственное заметное отличие состоит в том, что FPD перед исполнением
SELECT выполняет запись на диск данных из своего кэша - для заблокированных
явно и модифицированных записей (для НЕзаблокированных явно записей это и не
нужно т.к. для них REPLACE выполняет автоблокировку...)
Цитата:
На мой взгляд, это неправильно - не должен этим FLUSH
заниматься
1) Должен - это его прямая обязанность.
2) Я неправильно выразился - не FLUSH вызывает снятие блокировки, а
наоборот - снятие блокировки с записи вызывает её запись на диск (это не
совсем FLUSH т.к. записывается только текущая запись). Явно наложенные
блокировки FLUSH не трогает, так что повода для беспокойства нету!
Цитата:
Как уже говорил, я пришел в VFP после многих лет работы в FPD. В FPD
я, делая REPLACE какого-либо поля, никаким способом уже не мог прочитать
старое значение из этого поля.
Ты не прав - единственное видимое отличие - SELECT в FPD вызывает перед
своим исполнением неявный FLUSH. А так - как и в VFP SELECT читает данные с
диска, а не из кэша. Это легко увидеть если запустить 2 копии FPD и в одной
менять данные в таблице, ЗАПИСЫВАЯ их принудительно (тот же FLUSH), а в
другой:
a) делать "прямое" чтение данных из заранее открытой таблицы.
b) делать SQL запрос из этой-же таблицы.
Результаты будут РАЗЛИЧНЫЕ.
Цитата:
В VFP сделали много наворотов для буферизации
Рассматриваемая проблема не имеет отношения к буферизации - это проблема
(или особенность) работы системы кэширования считанных с диска данных.
Про буферизацию ты в общем правильно понимаешь
Цитата:
ВСЕ команды работы с данными ВНУТРИ ЭТОЙ ФОРМЫ должны работать в
контексте этой формы
Сложно сказать - поведение SELECT SQL было таковым ещё в FPD (происходило
чтение с диска, даже мимо файлового кэша фокса) - сделать "по умолчанию"
отличающееся поведение в VFP - значит нарушить принцип обратной
совместимости. Поэтому и было оставлено старое поведение. ХОТЯ я считаю что
опцию WITH (BUFFERING=...) и соответствующую настройку SET SQLBUFFERING
(которая конечно по умолчанию ДОЛЖНА быть установлена в OFF) можно было
ввести гораздо раньше, и не тянуть 10 лет Ну да видимо не очень сильно
было это востребовано сообществом...
Цитата:
Мда... Что-то мне такие "обходные маневры" не нравятся
Это не обходные манёвры - насколько я понимаю так работают все среды - пока
что-то "набивается" в поле - ни о каком сохранении данных речи не идёт - и
лишь при выходе из поля, или нажатии каких-то специальных клавиш происходит
"сброс" данных из буфера контрола куда-то дальше... В фоксе это "осложнено"
ещё и явной привязкой контрола к произвольному источнику данных. Если для
переменной памяти особых проблем привязка не вызывает (хотя ТОЖЕ порой
работа контрола выглядит "странно"), то для поля таблицы - совсем другое
дело - поскольку запись в таблицу ДОЛЖНА быть обрамлена блокировкой, то без
этого никак нельзя... Ну если конечно не отказываться от привязки
контролов - т.е. от задания ControlSource




------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Эксперименты с FLUSH в VFP9
Владимир Максимов

Сообщений: 14098
Откуда: Москва
Дата регистрации: 02.09.2000
Цитата:
Как уже говорил, я пришел в VFP после многих лет работы в FPD. В FPD я, делая REPLACE какого-либо поля, никаким способом уже не мог прочитать старое значение из этого поля. Хоть SQL, хоть не SQL. И считаю это правильным решением. И поэтому значение по умолчанию WITH (BUFFERING=lExp) логичнее было бы сделать таким, как в FPD. Как раз для обеспечения совместимости со старыми версиями.
В FPD не было такого понятия как "буфер таблиц" (точнее, были, но не явные и практически неуправляемые пользователем). Т.е. говорить о какой-то совместимости в этом плане не имеет смысла. Не с чем "совмещать". Если же в VFP не использовать буфер таблиц, т.е. работать "как в FPD", то совместимость налицо. Все работает также как и в FPD.

Между FPD 2.6 и VFP9 (где появилось WITH (BUFFERING=lExp) ) было 4,5 версий Visual FoxPro. ВСЕ они в команде Select-SQL не умели работать с буфером. С точки зрения пользователей старых версий VFP - все логично. Совместимость обеспечена. Не надо забывать, что Microsoft отказался от поддержки FPD уже лет 10 тому. Для него "старая" версия - это в лучшем случае VFP6.

Цитата:
Мда... Что-то мне такие "обходные маневры" не нравятся. По-моему, это уже признак того, что что-то в концепции напутано. Мне кажется, что интерфейс (контролы, их буфера и их Valid(), LostFocus()), блокировку и работу с движком данных не стоило вязать в такой узел, раз приходится ТАК перестраховываться.
Нет. Тут как раз все логично. Причем именно такая логика была еще в FPD при редактировании в BROWSE-окне. Пока не вышел из поля, изменения этого поля не попали в таблицу.

Применительно к VFP. Есть на форме TextBox. Ты начинаешь модифицировать его данные. В какой момент, то, что ты внес должно быть сброшено в поле таблицы? После каждого символа? Не думаю. Логично завершить ввод поля целиком и только потом сбросить то, что ввели.

А вот теперь и начинается то, что ты называешь "сложностями". Как определить тот момент, когда ввод данных в TextBox завершен? Т.е. можно начинать все те операции по сбросу буфера. А как увязать завершение ввода в одно поле и завершение ввода всей записи?

Чтобы описать все "подводные камни" и возможные "рабочие ситуации", которые могут возникнуть при определении момента завершения ввода в поле и запись надо написать огромную "простыню". Разработчики FoxPro проделали огромную работу по их обработке. Но всего учесть не смогли. Даже не потому, что не заметили, а потому, что не всегда ситуация однозначна. Т.е. не всегда можно однозначно сказать, что вот он момент завершения ввода. Поэтому "правило хорошего тона" - это дать FoxPro понять, что процесс ввода был завершен. Таким индикатором и является явный переход на другой объект ввода.
Ratings: 0 negative/0 positive


Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

On-line: 26 vech  (Гостей: 25)

© 2000-2024 Fox Club 
Яндекс.Метрика