-
Отключить режим Quick edit в окне Properties терминала (см. скриншот ниже)
Использовать другой терминал (например, git bash)
Запускать nodejs через Task Scheduler операционной системы.
Показаны сообщения с ярлыком проблема. Показать все сообщения
Показаны сообщения с ярлыком проблема. Показать все сообщения
28 янв. 2022 г.
Зависание nodejs при запуске через окно терминала Windows
Есть неприятная особенность в окне терминала Windows. Если включен режим Quick Edit (Быстрая вставка или Быстрое редактирование в русскоязычной версии интерфейса), то в определенный момент терминал может перейти в режим ожидания клавиатурного ввода от пользователя. Выйти из этого режима можно нажав любую клавишу, но пока это не будет сделано, сервер node будет висеть и не отвечать на запросы по сети.
У данной проблемы есть следующие решения:
5 мар. 2018 г.
Конкретизируем следующий шаг
- Есть разбор предложения "покажи всех клиентов из минска". сейчас он рисует дерево, где слова зеленые, а остальные узлы белые.
- Наша задача: найти сопоставления для сущностей и, там где найдено, мы будем закрашивать узлы дерева красным цветом.
- Для этого нам понадобится логическая ER модель базы данных. Текущая реализация строит ее на основе физической структуры, но это не совсем то, что нам надо. например, в физической модели будет присутствовать таблица GD_CONTACT, хотя такой сущности нет в логической модели, где мы имеем семь сущностей: Папка, Группа, Организация, Банк, Подразделение, Физическое лицо и Сотрудник предприятия. Все они базируются на одной физической таблице GD_CONTACT.
- Предполагается, что мы добавим в БД (в дополнительные таблицы или в существующую таблицу AT_RELATIONS) информацию, которая позволит нам правильно извлекать логическую структуру БД.
- Причем, если такая информация для таблицы не задана, то мы берем ее физическую структуру и на основе нее создаем сущность в логической модели.
- Возвращаемся к нашему предложению: Сначала мы встречаем существительное "клиентов" -- "клиент" в ед. числе именительного падежа. Через заданный синонимический ряд "клиент - организация" мы можем установить соответствие этого существительного с сущностью "Организация" из логической ER модели базы данных.
- Переходим к фразе "из минска". Тут возможны несколько вариантов сопоставления. а) Мы наделяем слова в нашем словаре семантическими категориями. Например, Минск -- это город, а город -- это место. Ищем в базе данных и находим справочник "Административно-территориальных единиц", а "Административно-территориальная единица" находится в смысловом ряду с городом, местом. б) Мы анализируем предлог "из" в предложной фразе "из Минска" и узнаем, что он имеет смысл места. Далее, через смысловой (синонимический) ряд выходим на справочник "Административно-территориальных единиц".
- Весь анализ должен происходить на клиенте. Т.е. надо подумать о передаче схемы с сервера на клиент, представлении ее в памяти и объектах для работы с ней.
- На сервере схема должна читаться однократно и храниться в оперативной памяти. Чтобы последующие обращения (покдлючения) клиентов не приводили к повторному длительному чтению всей структуры БД.
23 мая 2017 г.
Как не запутаться в зависимостях объектов ПИ
Уже миллион раз успел пожалеть, что сделал в Гедымине режим добавления объектов в ПИ "с зависимыми". Это мощная функция, которая требует от применяющего досконального знания теории реляционных баз данных, структуры конкретной БД, внутреннего устройства своего прикладного решения.
Добросовестная разработка с использованием данной галки требует следования определенной последовательности операций:
-
Добавить объект в ПИ с зависимыми.
Открыть список ПИ. Найти нужное и по-порядку, по списку входящих в него объектов, проверить что именно добавилось. Убрать лишнее.
Открыть список зависимостей для выбранного ПИ и проверить какие зависимости добавились. При необходимости изменить.
Сохранить ПИ в репозиторий.
Взять чистый эталон и загрузить на него пакет (каждый функционально завершенный и обособленный модуль должен быть оформлен в виде пакета).
Протестировать работоспособность загруженного пакета.
-
В такой базе может присутствовать целый букет устаревших, временных, давно забытых ПИ. При добавлении "с зависимостями" они подхватятся и затянутся в список зависимых ПИ.
В таблицах на таких БД могут присутствовать устаревшие, уже не используемые поля. Мало того, что они затянутся в ПИ, так еще затянутся и объекты на которые они ссылаются.
Устаревшие скрипт-функции могут привести к тому, что код, работающий на разработочной базе, не будет работать на чистой базе, собранной из актуальных ПИ.
-
Поскольку решение не велико, то объекты разобъем по следующим ПИ:
-
GS.Зарплата.Наряды.Метаданные -- домены, таблицы, триггеры и т.п.
GS.Зарплата.Наряды.Хранилище -- экранные формы.
GS.Зарплата.Наряды.Макросы -- перекрытые методы, локальные макросы форм.
GS.Зарплата.Наряды.Отчеты -- печатные формы.
-
GS.Зарплата.Наряды.Метаданные зависит от пакета GS.Зарплата.
GS.Зарплата.Наряды.Хранилище зависит от GS.Зарплата.Наряды.Метаданные.
GS.Зарплата.Наряды.Макросы зависит от GS.Зарплата.Наряды.Хранилище.
GS.Зарплата.Наряды.Отчеты зависит от GS.Зарплата.Наряды.Макросы.
Labels:
база данных,
документация,
исходный код,
полезное,
проблема
8 дек. 2016 г.
External exception C0000006
Ошибка возникает, когда операционная система не может загрузить нужную ей часть выполняемого файла или динамической библиотеки.
Причиной может быть обрыв сетевого соединения, если выполняемый файл находится на сетевом диске.
Мы добавили в заголовок gedemin.exe, gedemin_upd.exe и gdcc.exe флаг IMAGE_FILE_NET_RUN_FROM_SWAP. Теперь при запуске программы с сетевого диска она будет целиком копироваться в swap файл локального компьютера. Это замедлит процесс начальной загрузки, но позволит избавиться от ошибки в случае обрыва соединения.
Для обновления следует взять архив с нашего сайта и переписать с заменой все файлы из него. Утилита автоматического обновления может не сработать, так как номера версий файлов не изменились.
Следует протестировать новый выполняемый модуль в сетях, где возникала указанная проблема. Если установка флага не поможет, то, возможно, следует аналогичный флаг установить и для всех динамических библиотек, используемых Гедымином.
Также в сети есть информация, что если ошибка появляется при работе в терминальных сессиях Windows Terminal Server 2008, то рекомендуется установить в реестре флаг:
Subkey: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MRxSmb\Parameters
Type: REG_DWORD
Entry: MultiUserEnabled
Value: 1
Type: REG_DWORD
Entry: MultiUserEnabled
Value: 1
29 нояб. 2016 г.
GDMNN: Задача #1
В продолжение вчерашнего разговора. Первая задача: представить альтернативную структуру таблиц для документов, бухгалтерских проводок и складского движения.
Цель:
Цель:
- Унифицировать механизмы фиксации и учета движения, обобщив и распространив их не только на движение ТМЦ, но и на движение (изменение, трансформацию) любого объекта учета.
- Избавиться от дублирования данных и непрозрачных функций преобразования (поля документа => аналитические признаки в проводках).
- Любое поле документа может быть использовано в качестве аналитического признака при построении отчетов.
- Отойти от ограниченной структуры шапка-позиции. Для сложных документов предусмотреть наличие нескольких датасетов с произвольным уровнем вложенности.
- Для сумовых данных, используемых с целью ускорения выборок (INV_BALANCE), предусмотреть неблокирующую схему обновления, чтобы отказаться от автокомита в складских документах (комита частично введенного документа).
- Статус документа: черновик, отложенный, готовый.
- Уменьшить размер базы и, как следствие, увеличить скорость операций по изменению и выборке.
Labels:
база данных,
идеи,
оптимизация,
проблема,
Firebird,
SQL
27 янв. 2016 г.
Поиск кольцевых ссылок в реляционной базе данных рекурсивным SQL запросом
Наш предыдущий подход к поиску кольцевых ссылок, изложенный тут, заключался в том, чтобы дать рекурсивному запросу возможность зациклиться и контролировать количество итераций. При достаточно большом их числе мы могли сказать, что скорее всего в данных присутствует цилическая связь. Само число следовало подбирать эмпирически, анализируя данные конкретной задачи.
Между тем, существует способ однозначного выявления циклов без "наматывания" кругов по графу. Рассмотрим граф, показанный на рисунке ниже:
В базе данных он хранится в виде матрицы смежностей в таблице t:
CREATE TABLE t (
a INTEGER,
b INTEGER
)
Соответственно, исходные данные:
INSERT INTO t VALUES (1, 2); INSERT INTO t VALUES (2, 3); INSERT INTO t VALUES (3, 4); INSERT INTO t VALUES (3, 5); INSERT INTO t VALUES (3, 6); INSERT INTO t VALUES (2, 7); INSERT INTO t VALUES (6, 1);Для поиска кольцевых ссылок напишем запрос:
with recursive tree as
(
select distinct
a as initial, a, b, -1 as prev
from
t
union all
select
iif(tr.initial <> tt.a, tr.initial, -1) as initial,
tt.a,
tt.b,
tr.a as prev
from
t tt JOIN tree tr ON
tr.b = tt.a and tr.initial > 0
)
select
*
from
tree
where
initial = -1
Запрос покажет все первые сегменты A - B (и предыдущие, т.е. сегменты непосредственно приведшие к циклу, PREV - A), всех возможных циклов в заданных данных. В нашем случае:
INITIAL A B PREV -1 1 2 6 -1 2 3 1 -1 2 7 1 -1 3 4 2 -1 3 5 2 -1 3 6 2 -1 6 1 3При необходимости проверить не входит ли в кольцо конкретный узел, следует прописать его ИД в первом запросе рекурсивного CTE:
with recursive tree as
(
select distinct
a as initial, a, b, -1 as prev
from
t
where
a = _some_id_
union all
select
iif(tr.initial <> tt.a, tr.initial, -1) as initial,
tt.a,
tt.b,
tr.a as prev
from
t tt JOIN tree tr ON
tr.b = tt.a and tr.initial > 0
)
select
*
from
tree
where
initial = -1
14 нояб. 2015 г.
Перезапуск платформы по расписанию
В идеальном мире, где идеальные люди запускают идеальные программы, ошибкам просто нет места. Но в реальной жизни shit, к сожалению, happens. Когда сроки истекли еще вчера, а технические требования заказчик меняет каждый раз, когда видит очередную версию, приходится действовать по методике "run and fire". И, если ошибки на экране в худшем случае заставят пользователя произнести несколько нецензурных слов, то неявные утечки памяти и редкие баги в серверном коде, способны свести с ума даже закаленного аса разработки и отладки.
Здесь на помощь приходит "quick and dirty" решение в виде автоматического перезапуска платформы по расписанию:
На приведенном выше скриншоте показаны настройки автозадачи для перезапуска платформы каждые 10 минут. Стоит сделать замечание, что перезапуск не произойдет, если на экране открыто диалоговое окно в модальном режиме.
Здесь на помощь приходит "quick and dirty" решение в виде автоматического перезапуска платформы по расписанию:
На приведенном выше скриншоте показаны настройки автозадачи для перезапуска платформы каждые 10 минут. Стоит сделать замечание, что перезапуск не произойдет, если на экране открыто диалоговое окно в модальном режиме.
Labels:
документация,
полезное,
проблема
15 авг. 2015 г.
Application hangs in Windows XP SP2 compatibility mode under Windows 10
Seems that WinXP SP2 compatibility mode in the new Windows 10 has some... incompatibilities. Half of the past week was spent for a struggle with a mysterious application hang. Through some experiments it was found that the hanging is a result of interaction between gdNotifierThread and assigned notifier window. Although an update of a visual control is carried through a call of Synchronize method the whole application becomes unresponsive. The problem was solved by replacement of Synchronize call with sending a custom message using PostMessage function.
One could ask what is the reason in running Gedemin in a compatibility mode in the first place. Well, this is quick and dirty solution to make Gedemin's windows look good. Newest Windows 10 display theme looks weird with a windows and dialog boxes designed in the times when Ctl3D was a first class fashion.
14 мая 2014 г.
CURRENT OF vs поиск по ключу
Какое же утро без хорошего эксперимента. Берем встроенный сервер Firebird 2.5.3 и чистую БД:
Page size 8192 ODS version 11.2 Page buffers 75 Database dialect 3 Attributes force writeСоздаем две идентичных по структуре таблицы:
create table t1 (i integer not null) create table t2 (i integer not null)Одинаково заполняем каждую из них миллионом случайных значений:
execute block
as
declare variable i integer = 0;
declare variable v integer = 0;
begin
while (i < 1000000) do
begin
v = round(rand() * 2000000000);
insert into t1 values (:v);
insert into t2 values (:v);
i = :i + 1;
end
end
Для таблицы t1 создаем индекс:
create index t1x on t1 (i)Делаем коммит транзакции и переподключение к базе данных. Организуем скан таблицы t1 и случайным образом удаляем четверть записей через поиск по индексированному полю:
execute block
as
declare variable i integer;
begin
for select i from t1 into :i
do
begin
if (rand() < 0.25) then
delete from t1 where i = :i;
end
end
Статистика выполнения:
Execute : 10 717.00 ms Read : 239 855 Writes : 6 250 Fetches : 4 264 681 Marks : 250 286 IR : 250 286 NIR : 999 934 Deletes : 250 286Не совсем понятно почему NIR не миллион. Все остальное вполне логично. Коммит транзакции и еще раз удаляем четверть, теперь от оставшихся записей:
Execute : 582 243.00 ms Read : 414 519 Writes : 246 390 Fetches : 5 712 020 Marks : 944 206 IR : 187 214 NIR : 749 693 Deletes : 187 214 Expunges : 250 286Почти 10 минут! Обратите внимание на сумасшедшее количество Writes и Marks. Сборка мусора и перестроение индекса? Далее, берем таблицу t2 и удаляем из нее четверть записей, но с помощью курсора и конструкции CURRENT OF:
execute block
as
declare variable c cursor for
(select i from t2);
declare variable i integer;
begin
open c;
while (1=1) do
begin
fetch c into :i;
if (row_count = 0) then
leave;
if (rand() < 0.25) then
delete from t2 where current of c;
end
close c;
end
Статистика:
Execute : 8 362.00 ms Read : 6 138 Writes : 6 064 Fetches : 2 762 582 Marks : 250 100 NIR : 1 000 000 Deletes : 250 100Обратите внимание, насколько меньше чтений по сравнению с удалением по индексированному полю. Выполнение на 20% быстрее за счет того, что не надо дергать индекс. Коммит транзакции и еще раз удаляем четверть записей:
Execute : 8 252.00 ms Read : 6 137 Writes : 6 067 Fetches : 3 587 315 Marks : 693 789 NIR : 749 900 Deletes : 187 455 Expunges : 250 100Выполнился даже быстрее чем первый запрос, несмотря на сборку мусора, и в 70 (!!!) раз быстрее чем запрос с удалением по индексированному полю. Непонятно почему так велико значение Marks, ведь удаляется всего 187 455 записей. Подсчитаем количество записей в первой таблице:
select count(*) from t1Статистика:
Execute : 493 697.00 ms Read : 181 339 Writes : 181 084 Fetches : 3 009 620 Marks : 561 642 NIR : 562 500 Expunges : 187 214Все ясно, сборка мусора напоролась на индекс. 8 минут на кофе. Теперь считаем во второй:
select count(*) from t2Статистика:
Execute : 6 443.00 ms Read : 6 137 Writes : 6 064 Fetches : 2 261 901 Marks : 374 910 NIR : 562 445 Expunges : 187 455Упс! Мы ожидали увидеть сравнимые цифры по времени выполнения для первой и второй таблицы, но разница составила 76 (!!!) раз. Все из-за наличия в первой таблице индекса, который при подсчете количества никак не используется, но катастрофически тормозит сборку мусора. Попытаемся выяснить как влияет наличие индекса на выполнение операции CURRENT OF. Возвращаемся к исходной базе данных. Будем удалять записи из таблицы t1 с помощью конструкции CURRENT OF:
Execute : 8 050.00 ms Read : 6 138 Writes : 6 064 Fetches : 2 765 717 Marks : 251 145 NIR : 1 000 000 Deletes : 251 145В одно время с удалением из таблицы, по которой нет индекса. Второй цикл удаления:
Execute : 688 480.00 ms Read : 240 628 Writes : 240 341 Fetches : 4 592 695 Marks : 945 817 NIR : 748 855 Deletes : 186 248 Expunges : 251 145Самое большое время, которое нам пришлось наблюдать сегодня. Сборка мусора и индекс явно не дружат друг с другом. Для оценки влияния сборки мусора повторим все операции с самого начала на исходной базе данных с флагом подключения no_garbage_collect. Первое удаление из таблицы t1 (поиск по индексированному полю):
Execute : 12 418.00 ms Read : 239 735 Writes : 6 231 Fetches : 4 260 434 Marks : 249 809 NIR : 999 939 IR : 249 809 Deletes : 249 809Странно, но NIR снова не миллион. Куда деваются около шестидесяти чтений непонятно. Второе удаление:
Execute : 9 563.00 ms Read : 181 842 Writes : 6 189 Fetches : 3 702 914 Marks : 187 849 NIR : 750 162 IR : 187 849 Deletes : 187 849Записей стало меньше и время сканирования и удаления уменьшилось соответственно. Для второй таблицы удаление через курсор. Первый проход:
Execute : 8 112.00 ms Read : 6 138 Writes : 6 064 Fetches : 2 764 184 Marks : 250 634 NIR : 1 000 000 Deletes : 250 634Второй:
Execute : 8 034.00 ms Read : 6 137 Writes : 6 064 Fetches : 2 574 131 Marks : 187 283 NIR : 749 366 Deletes : 187 283Обратите внимание, что цифры идентичны тем, которые мы имели при включенной сборке мусора. Т.е. нет индекса и сборка мусора не проблема. Получаем количество записей для первой таблицы:
Execute : 546.00 ms Read : 6 137 Writes : 2 Fetches : 2 012 281 Marks : 0 NIR : 562 342Для второй:
Execute : 531.00 ms Read : 6 137 Writes : 2 Fetches : 2 012 281 Marks : 0 NIR : 562 083Выводы:
-
Нижесказанное применимо к частным случаям массовой обработки данных.
Если по таблице нет индексов, то включение/выключение сборки мусора практически не влияет на производительность.
Если по таблице созданы индексы, то сборка мусора способна радикально, в десятки и сотни раз, замедлить ход процесса.
В нашем примере CURRENT OF был на 20-25% быстрее чем поиск по индексированному полю.
CURRENT OF может применяться на таблицах, где индексов нет вообще. Совершенно очевидно, что удаление с поиском по неиндесированному полю выполнялось бы в нашем случае часами, если не сутками.
Labels:
архитектура,
база данных,
оптимизация,
полезное,
проблема,
производительность,
Firebird,
SQL
3 апр. 2014 г.
Как избежать расхождений при делении и последующем округлении массы единицы сырья или товара
Когда учет массы сырья ведется с определенной точностью, а на весах могут одновременно взвешиваться несколько единиц, нельзя высчитывать вес каждой простым делением.
Пример: учет ведется с точностью до 0.1 кг, на весы загнали три единицы и получили общую массу 511.1 кг. Простое деление даст нам 170.3666666666667 кг на единицу или 170.4 кг после округления. Когда в итоговом отчете три единицы посуммируются получится 511.2 кг, т.е. возникнет расхождение с исходными показаниями весов.
Правильно в данном случае поступить следующим образом:
-
Общую массу перевести в целочисленное количество минимальных единиц. В нашем случае такой единицей будет 0.1 кг, соответственно,
511.1 кг = 5111 по 0.1 кг Целочисленно разделить полученное число на количество единиц в партии:
5111 div 3 = 1703 = D Вычислить остаток от целочисленного деления:
5111 mod 3 = 2 = M Для произвольных M единиц в партии принять учетную массу (D + 1) для остальных -- D.
-
(1703 + 1) * 0.1 = 170.4 кг
(1703 + 1) * 0.1 = 170.4 кг
1703 * 0.1 = 170.3 кг
5 февр. 2014 г.
Перезапись объекта по потенциальному ключу
При загрузке объектов приоритет имеет поиск по потенциальному ключу (там, где перекрыт метод CheckTheSameStatement) перед поиском по РУИДу. Например, пусть у нас имеется исходная база данных с двумя отчетами:
Разработчик копирует ее себе на локальную машину:
...и производит доработку: удаляет отчет "Реестр" и переименовывает отчет "Новый реестр" в просто "Реестр":
Сохраняет доработанный отчет в ПИ и загружает его на исходную БД. Для отчетов у нас определен поиск объектов по имени в пределах одной папки. Т.е. будет найден отчет с именем "Реестр" и перезаписан. Попутно, его РУИД_1 заменится на РУИД_2 из файла.
Перезапись РУИДа означает, что исходный отчет "Новый реестр" вообще останется без РУИДа, потому что РУИД_2 теперь указывает на отчет "Реестр":
Так что будьте аккуратнее с переименованием объектов и следите за тем, что система пишет в лог при загрузке пространства имен.
27 янв. 2014 г.
Проблемы с загрузкой одиночного ПИ
Настройки тянули в себя все объекты по ссылкам и это одна из причин, по которой мы решили от них отказаться в пользу ПИ. В противоположность настройке, при сериализации пространства имен, на диск попадает только то, что непосредственно было включено программистом в ПИ. Если мы игнорируем зависимости ПИ и загружаем одиночный файл, то можем столкнуться с такой ошибкой:
Ее причина проста: в ПИ содержится ссылка на объект, который отсутствует в самом ПИ и в базе данных, или имеет в базе данных другой РУИД. В данном случае проблему вызывает ссылка на папку хранилища GLOBAL\DFM\Tgdc_dlgUserComplexDocument, которая в базе данных имеет РУИД отличный от того, который записан в файле ПИ.
Избежать проблемы можно, если настроить зависимости между файлами ПИ и не использовать загрузку одиночного файла.
Системные объекты, вроде папок хранилища, должны иметь единые стандартные РУИДы, которые у нас записаны в пакете "Общие данные". Данный пакет обязательно должен быть загружен на любую базу, на которой в последствии мы хотим разрабатывать и формировать пространства имен.
7 нояб. 2012 г.
Ручное изменение поля типа ссылка
Поменять поле типа ссылка, чтобы оно вместо одной таблицы ссылалось на другую, средствами Гедымина процесс длительный и непростой. Надо создать временное поле. Скопировать в него данные. Удалить исходное поле. Удалить домен. Создать новый домен. Создать новое поле. Перегнать в него исходные данные. Если к тому же поле участвует в триггере или процедуре, то придется предварительно перекомпилировать их с закоментированным текстом и восстановить в исходное состояние, после создания нового поля.
К счастью, для тех, кто знаком с устройством реляционной базы данных и не боится воспользоваться скальпелем и гвоздодёром, существует короткий обходной путь. Предположим, в некоторой таблице TABLE_A находится поле USR$FIELD_A типа USR$DFIELD_A, которое ссылается на таблицу TABLE_B. Изменим его так, чтобы оно ссылалось на таблицу TABLE_C. Поле USR$NAME из TABLE_C используется для отображения наименований объектов в выпадающих списках.
-
Идем Исследователь-Сервис-Атрибуты-Таблицы. Находим в списке нашу таблицу и открываем в диалоговом окне. Переходим на вкладку Скрипт.
Ищем строку вида: ALTER TABLE TABLE_A ADD CONSTRAINT USR$FKTABLE_ANN FOREIGN KEY (USR$FIELD_A) REFERENCES TABLE_B (ID) ON UPDATE CASCADE;
Из найденой строки узнаем имя ограничения. В нашем случае это USR$FKTABLE_ANN, где вместо NN будут какие-нибудь цифры.
Идем в окно SQL редактора и выполняем команду: ALTER TABLE TABLE_A DROP CONSTRAINT USR$FKTABLE_ANN
Там же набираем и выполняем команду: ALTER TABLE TABLE_A ADD CONSTRAINT USR$FKTABLE_ANN FOREIGN KEY (USR$FIELD_A) REFERENCES TABLE_C (ID) ON UPDATE CASCADE ON DELETE CASCADE Обратите внимание, что правила ON UPDATE и ON DELETE вы должны указать в соответствии с логикой вашей задачи.
Как вы поняли, первая команда удалила старую ссылку, а вторая создала новую. Остается сообщить об изменившейся ссылке Гедымину. Для начала следует в разделе Исследователь-Сервис-Атрибуты-Таблицы узнать идентификаторы таблицы TABLE_C и ее поля USR$NAME.
Узнать ИД проще простого: выделяем запись в гриде, правая кнопка мыши, команда Свойства...
Не выходя из формы просмотра с таблицами ищем TABLE_A и в ней поле USR$FIELD_A. Затем, правая кнопка мыши, комадна Свойства..., вкладка Данные:
-
DELETERULE -- прописываем правило для удаления записи, которое мы указали в команде создания FOREIGN KEY. Например, CASCADE, RESTRICT и т.д.
-
REFTABLE -- Прописываем имя TABLE_C.
REFLISTFIELD -- Имя поля из таблицы TABLE_C, которое будет использоваться для отображения в выпадающих списках.
REFTABLEKEY -- ИД таблицы TABLE_C. Его мы определили выше.
REFLISTFIELDKEY -- ИД поля из таблицы TABLE_C, которое будет использоваться для отображения в выпадающих списка.
REFLNAME -- локализованное наименование таблицы справочника.
REFLISTLNAME -- локализованное наименование поля для отображения в выпадающих списках.
24 авг. 2012 г.
Ветка HTTPSRV
Создал ветку HTTPSRV на основе текущих исходников с компонентами Indy 9. Цель -- встроить в Гедымин HTTP сервер. Первый шаг:
-
Сделать нить с низким приоритетом в которую поместить компонент TidHTTPServer.
При обращении клиента запрашивать логин и пароль, а затем генерировать страничку с информацией о системе (такой, как в окне О программе...)
-
Подумать как подключать скрипт-функции для обработки событий HTTP сервера.
Реализовать вызов скрипт-функции из нити HTTP сервера.
Реализовать формирование странички и взаимодействие с браузером в скрипт-функции.
Labels:
идеи,
інтэрнэт,
проблема,
программирование
24 июл. 2012 г.
Сохранение состояния бизнес-объекта
В случаях редактирования или удаления записи из набора данных бизнес-объекта сначала мы получаем ее тип с помощью метода GetCurrRecordClass и, если он отличается от типа самого бизнес-объекта, создаем отдельный экземпляр, в котором выполняем операцию. После чего порожденный бизнес-объект уничтожается.
Проблема в том, что у нас не предусмотрена передача состояния между исходным и созданным бизнес-объектами. Пример из жизни: идет удаление нескольких выделенных записей. В процессе, для каждой создается отдельный экземпляр БО. На одной из записей возникает ошибка. Мы хотим спросить пользователя: прервать весь процесс или продолжить, пропуская проблемные записи. Вопрос в том, как и где сохранить ответ пользователя, чтобы использовать его при отработке следующих по списку записей.
Найденное на скорую руку решение — добавить флаги в поле BaseState и копировать их после создания и перед удалением созданного бизнес-объекта — выглядит поверхностным. По-хорошему, следует ввести понятие порожденного бизнес-объекта и внутри него обрабатывать перенос состояния до и после выполнения операции.
Labels:
архитектура,
документация,
идеи,
проблема,
технология
1 апр. 2012 г.
Специфика ORM модели платформы Гедымин
ORM Гедымина имеет ряд нестыковок. Известны они давно и очередной раз привлекли к себе внимание во время разработки автоматизированного теста (см. предыдущий пост). Ниже перечислены некоторые из них:
-
Не учитывается разница между истинным абстрактным базовым классом (например, TgdcBase), и базовым классом c конкретной ListTable, который служит фундаментом для иерархии наследованных классов (например, TgdcBaseContact).
Некоторые объекты могут использоваться только внутри кода других объектов с выполнением магических действий по программной настройке:
-
Все наследники TgdcInvBaseRemains
TgdcInvCard
TgdcLink
TgdcAcctDocument
TgdcAcctEntryRegister
BN_BANKCATALOGUE, BN_BANKCATALOGUELINE, BN_BANKSTATEMENT, BN_BANKSTATEMENTLINE, BN_CURRCOMMISSION, BN_CHECKLIST, BN_CURRBUYCONTRACT, BN_CURRCOMMISSSELL, BN_CURRCONVCONTRACT, BN_CURRLISTALLOCATION, BN_CURRSELLCONTRACT, BN_DEMANDPAYMENT, CTL_INVOICE, CTL_RECEIPT, GD_TAXDESIGNDATE, GD_TAXRESULT, GD_CONTRACT, GD_DOCREALIZATION, INV_PRICE, INV_PRICELINE, USR$INV_ADDINFO, USR$BN_ACCMAKE, USR$BN_ACCMAKELINE, USR$BN_BILL, USR$BN_BILLLINE, USR$BN_BILLNDS, USR$BN_BILLNDSLINE, USR$BN_BLANKTRIP, USR$BN_BTRIPMONEY, USR$BN_BTRIPMONEYLINE, USR$BN_BUYCURR, USR$BN_CASHBOOK, USR$BN_CASHBOOKLINE, USR$BN_CASHDEPOSIT, USR$BN_CASHDEPOSITLINE, USR$BN_CASHREPORT, USR$BN_CASHREPORTLINE, USR$BN_CHANGECURR, USR$BN_COLLECT, USR$BN_COMMISS, USR$BN_COMMISSLINE, USR$BN_DATABTRIP, USR$BN_DATABTRIPLINE, USR$BN_DEMAND, USR$BN_INCOMECURR, USR$BN_INCOMECURRLINE, USR$BN_LISTDEM, USR$BN_LISTDEMLINE, USR$BN_NOTICE, USR$BN_NOTICELINE, USR$BN_ORDERBTRIP, USR$BN_ORDERBTRIPLINE, USR$BN_PAYMENT, USR$BN_SALECURR, USR$BN_SALECURRLINE, USR$BN_SHARECURR, USR$BN_SHARECURRLINE, USR$BN_TRANSFER, USR$BN_TTN, USR$BN_TTNLINE, USR$BNF_ACTS, USR$BNF_ACTSLINE, USR$BNF_CONTRACT, USR$BNF_CONTRACTLINE, USR$CHECKREGISTER, USR$CHECKREGISTERLINE, USR$CURR_PAYMENT, USR$FA_ACTNEW, USR$FA_ACTNEWLINE, USR$FA_AMORT, USR$FA_AMORTLINE, USR$FA_CHANGEPROP, USR$FA_CHANGEPROPLINE, USR$FA_COMPLECT, USR$FA_COMPLECTLINE, USR$FA_CONSERVATIONLINE, USR$FA_CONSERVATION, USR$FA_MOVEMENT, USR$FA_MOVEMENTLINE, USR$FA_REAPP_B, USR$FA_REAPP_BLINE, USR$FA_REAPP, USR$FA_REAPPLINE, USR$FA_REMONT, USR$FA_REMONTLINE, USR$FA_REPAIR, USR$FA_REPAIRLINE, USR$GS_ACCBALANCE, USR$GS_ACTWORK, USR$GS_ACTWORKLINE, USR$INCOME_ORD, USR$INCOME_ORDLINE, USR$INV_ACTWORK, USR$INV_ACTWORKLINE, USR$INV_ADDWBILL, USR$INV_ADDWBILLLINE, USR$INV_ATTORNEY, USR$INV_ATTORNEYLINE, USR$INV_BILL, USR$INV_BILLLINE, USR$INV_CARDPRMET, USR$INV_CARDPRMETLINE, USR$INV_CERT, USR$INV_CHANGEPERC, USR$INV_CHANGEPERCLINE, USR$INV_COMP, USR$INV_COMPLINE, USR$INV_CONTRACT, USR$INV_CONTRACTLINE, USR$INV_DECOMP, USR$INV_DECOMPLINE, USR$INV_GOODORDER, USR$INV_GOODORDERLINE, USR$INV_INVENT, USR$INV_INVENTLINE, USR$INV_INVMOVE, USR$INV_INVMOVELINE, USR$INV_NEWCOST, USR$INV_NEWCOSTLINE, USR$INV_OUTLAYS, USR$INV_PROCESSING, USR$INV_PROCESSINGLINE, USR$INV_PRODUCT, USR$INV_PRODUCTLINE, USR$INV_PRREC, USR$INV_PRRECLINE, USR$INV_REALCOMMIS, USR$INV_REALCOMMISLINE, USR$INV_RETAIL, USR$INV_RETAILLINE, USR$INV_RETCUST, USR$INV_RETCUSTLINE, USR$INV_RETPROV, USR$INV_RETPROVLINE, USR$INV_SELLBILL, USR$INV_SELLBILLLINE, USR$INV_SPEND, USR$INV_SPENDLINE, USR$INV_SPENDPREC, USR$INV_SPENDPRECLINE, USR$INV_TOTRADE, USR$INV_TOTRADELINE, USR$MOG_DEBTACC, USR$MOG_DEBTCONTR, USR$MOG_INBILLVAT, USR$MOG_INBILLVATLINE, USR$MOG_SERVICE, USR$REN_DIVSTATEMNLINE, USR$SKIDKI, USR$SKIDKILINE, USR$WG_ADDPAYBYPERIODLINE, USR$WG_ADDPAYBYPERIOD, USR$WG_AGREEMENTLONGLINE, USR$WG_AGREEMENT, USR$WG_AGREEMENTLINE, USR$WG_AGREEMENTLONG, USR$WG_ALIMONY, USR$WG_ALIMONYDEBT, USR$WG_ATTEST, USR$WG_AVGADDPAY, USR$WG_AVGADDPAYLINE, USR$WG_BANKCALC, USR$WG_BRIGADEORDERLINE, USR$WG_BRIGADEORDER, USR$WG_CHARGEREG, USR$WG_CHILDAID, USR$WG_CONTRACT, USR$WG_CONTRACTLINE, USR$WG_CORRECT, USR$WG_CORRECTLINE, USR$WG_FAMCHAG, USR$WG_HAZARDS, USR$WG_HAZARDSLINE, USR$WG_HOLIDAYWORK, USR$WG_HOLIDAYWORKLINE, USR$WG_INCTAXDEDUCTION, USR$WG_INCTAXOTHERPAYLINE, USR$WG_INCTAXOTHER, USR$WG_INCTAXOTHERPAY, USR$WG_INCTAXSECURITIES, USR$WG_INITINCOME, USR$WG_INITINCOMELINE, USR$WG_KINDDAY, USR$WG_KINDDAYLINE, USR$WG_LEAVEDOC, USR$WG_LEAVEDOCLINE, USR$WG_LEAVEPASS, USR$WG_LEAVESCHED, USR$WG_LEAVESCHEDLINE, USR$WG_LEAVESTOPDOC, USR$WG_MANUALINPUT, USR$WG_MANUALINPUTLINE, USR$WG_MOVEMENT, USR$WG_MOVEMENTLINE, USR$WG_MULTIORDER, USR$WG_MULTIORDERLINE, USR$WG_PARTDAY, USR$WG_PARTDAYLINE, USR$WG_PENALTYDOC, USR$WG_PERSONALORDERLINE, USR$WG_PERSONALORDER, USR$WG_PIECEWORK, USR$WG_PIECEWORKLINE, USR$WG_PROFDEVELOP, USR$WG_PU6, USR$WG_PU6LINE, USR$WG_RETRAINING, USR$WG_REWARDDOC, USR$WG_SALARYCALC, USR$WG_SALARYCALCLINE, USR$WG_SALARYPAY, USR$WG_SALARYPAYLINE, USR$WG_SCIENCEDEVELOP, USR$WG_SENCALC, USR$WG_SENCALCLINE, USR$WG_SETPAYMENT, USR$WG_SETPAYMENTLINE, USR$WG_SETSENBONUS, USR$WG_SICKLIST, USR$WG_SICKLISTJOURNAL, USR$WG_SICKLISTLINE, USR$WG_SINKDEBT, USR$WG_SINKDEBTLINE, USR$WG_STAFFLIST, USR$WG_STAFFLISTLINE, USR$WG_TAXATION, USR$WG_TBLCAL, USR$WG_TBLCALLINE, USR$WG_TIMEWORK, USR$WG_TIMEWORKLINE, USR$WG_TOTAL, USR$WG_TOTALLINE, USR$WG_VACATION, USR$WG_VACATIONLINE, USR$WG_WRITEOFF, USR$WG_WRITEOFFLINE, USR$WS_DISTANCE_LINE, USR$WS_FUEL_LINE, USR$WS_WAYSHEETКак уже отмечалось выше, связь мастер-дитэйл обрабатывается платформой только для документов и только на уровне пользовательского интерфейса. Связь мастер-дитэйл-сабдитэйл не обрабатывается вообще никак. Наследование подтипов невозможно. Не лишней была бы возможность создать атрибут типа бизнес-класс. То, что сейчас решается созданием автономного объекта и ручным связыванием его с головной записью. Пример: дополнительная информация по товарно-транспортной накладной.
Labels:
архитектура,
база данных,
идеи,
исходный код,
проблема,
тестирование
28 нояб. 2011 г.
Брюки превращаются, превращаются брюки...
Помните как там было, в Бриллиантовой руке: Легким движением руки брюки превращаются, превращаются брюки...
В Гедымине для некоторых классов возможно превращение экземпляров в объекты одного из родительских классов. Например, Физическое лицо может стать Сотрудником предприятия и наоборот. Рабочая организация — просто организацией. Естественно, что РУИД при таких метаморфозах сохраняется, а это влечет за собой проблемы при загрузке из потока. День убит на разборку такой ситуации: была рабочая организация, которая когда-то настройкой перенесена на другую базу. Впоследствии, на той базе она была удалена из списка рабочих организаций и стала просто компанией. Теперь, с очередной настройкой, эта же запись, все того же типа Рабочая организация и с тем же РУИДом, снова пытается загрузиться на базу. В процессе загрузки создается экземпляр TgdcOurCompany, который не видит уже существующую компанию, что дает основание механизму загрузки создать новый экземпляр и попытаться сохранить его в базе. При сохранении возникает исключение, так как в GD_CONTACT запись с таким ИД уже есть.
По хорошему, радикальное решение данной проблемы — это полный запрет на превращения типов. Т.е. гражданин Фунт не может стать зицпредседателем Фунтом. Он должен (ради чистоты парадигмы!) погибнуть и воскреснуть уже как ипостась другого класса и с новым РУИДом. Но, такое изменение может вылезти боком в местах, которые сейчас даже не представляется возможным предугадать.
Пока остановимся на том, что научим Рабочую организацию при загрузке из потока распознавать указанную выше ситуацию и автоматически корректировать записи в базе. Конкретную проблему мы решим, а над исправлением общей модели еще стоит поразмыслить.
Labels:
архитектура,
идеи,
исходный код,
проблема
8 нояб. 2011 г.
USING INDEX
Век живи — век учись. Оказывается, уже начиная с версии 1.5 в Firebird можно именовать индексы ограничений и указывать их сортировку. Синтаксис соответствующей команды:
[CONSTRAINT constraint-name] <constraint-type> <constraint-definition> [USING [ASC[ENDING] | DESC[ENDING]] INDEX index_name]В Гедымине мы используем суррогатные целочисленные первичные ключи. Идентификатор конкретной записи уникален в пределах всех таблиц базы и является положительным числом. Никакого дополнительного смысла в идентификатор не вкладывается, за исключением диапазона от 0 до 146999999, зарезервированного для системных объектов. Дополнительный смысл — иными словами говоря функциональная зависимость первичного ключа от некоторого поля (полей) записи помогла бы ускорить выполнение некоторых запросов и уменьшить размер базы данных. Вот несколько возможных случаев:
-
Для таблицы gd_document присваивать идентификатор в соответствии с хронологией документов. Тогда сортировка по дате и выборка за период (две наиболее частых операции со списком документов) будут выполняться с использованием индекса первичного ключа, который и так всегда под рукой у оптимизатора для соединений с дополнительными таблицами. Для быстрой выборки за период можно держать под рукой соответствие начальных ИД на каждую дату.
Идентификатор для интервальных деревьев присваивать в соответствии с порядковым номером узла при обходе дерева вглубь. Отпадает необходимость в полях LB, RB. Интервал для дочерних элементов будет определяться как разность между ИД соседних узлов. Для совместимости с существующим кодом можно ввести вычисляемые поля LB (просто будет возвращать значение идентификатора записи) и RB (идентификатор следующей записи на этом же уровне минус единица).
Для складских карточек (таблица INV_CARD) присваивать идентификатор в соответствии с очередностью цепочки, заменив поле parent на признак первой карточки в последовательности.
и т.д.
-
Откажемся от единого на всю базу генератора gd_g_unique в пользу выделенного генератора для каждой таблицы. Будем формировать идентификаторы с шагом 10 (для менее населенных таблиц можно использовать шаг 100 и даже 1000).
Вместо сдвига всего правого поддиапазона относительно вставляемой записи будем выискивать свободные места и перемещать отдельные "кусочки".
Labels:
архитектура,
деревья,
идеи,
оптимизация,
проблема,
Firebird
10 мар. 2011 г.
Проблема с интервальными деревьями в старых файлах настроек
Если при восстановлении базы (ранее сконвертированной под Firebird 2.5) из архива выдает сообщение об ошибке с текстом "Input parameter mismatch for procedure USR$_P_EXLIM_...", значит на базу с новой структурой интервальных деревьев была загружена настройка со старыми деревьями.
В таком случае надо на исходной базе выполнить в окне SQL редактора команду:
После этого базу можно архивировать.
Почему проблемная процедура создается без ошибок при загрузке настройки -- вопрос к создателям Firebird.
Особое внимание разработчикам! Все настройки с интервальными деревьями должны быть пересохранены после обновления структуры базы данных.
В таком случае надо на исходной базе выполнить в окне SQL редактора команду:
DELETE FROM fin_versioninfo WHERE id > 112Затем подключиться к ней новейшей версией gedemin.exe (всегда можно скачать с нашего сайта) и дождаться окончания обновления структуры базы данных. В процессе обновления будут пересозданы все процедуры и триггеры интервальных деревьев.
После этого базу можно архивировать.
Почему проблемная процедура создается без ошибок при загрузке настройки -- вопрос к создателям Firebird.
Особое внимание разработчикам! Все настройки с интервальными деревьями должны быть пересохранены после обновления структуры базы данных.
Labels:
проблема
Подписаться на:
Сообщения (Atom)








