diff --git a/app/panels/query_editor/components/inspector_query_rls/hooks.js b/app/panels/query_editor/components/inspector_query_rls/hooks.js
index 53af628..a212705 100644
--- a/app/panels/query_editor/components/inspector_query_rls/hooks.js
+++ b/app/panels/query_editor/components/inspector_query_rls/hooks.js
@@ -35,8 +35,20 @@ const useQueryRelations = query => {
[query, executeStored]
);
+ //Установка признака обязательности отношения сущности в запросе
+ const setRlMandatory = useCallback(
+ async (rl, mandatory) => {
+ await executeStored({
+ stored: "PKG_P8PANELS_QE.QUERY_RL_MANDATORY_SET",
+ args: { NRN: query, SID: rl, NMANDATORY: mandatory },
+ loader: false
+ });
+ },
+ [query, executeStored]
+ );
+
//Возвращаем интерфейс хука
- return { addRl, removeRl };
+ return { addRl, removeRl, setRlMandatory };
};
//----------------
diff --git a/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js b/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js
index 31cb9c3..8422385 100644
--- a/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js
+++ b/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js
@@ -9,12 +9,26 @@
import React, { useContext } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
-import { Icon, Button } from "@mui/material"; //Интерфейсные элементы
+import { Icon, Button, FormControlLabel, Checkbox } from "@mui/material"; //Интерфейсные элементы
import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений приложения
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы
import { RELATION_SHAPE } from "../relation/relation"; //Описание связи
import { useQueryRelations } from "./hooks"; //Хуки для работы со связями
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ MANDATORY_FORMCONTROLLABEL: {
+ width: "100%",
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center"
+ }
+};
+
//-----------
//Тело модуля
//-----------
@@ -25,7 +39,7 @@ const InspectorQueryRelations = ({ query, relation, onOptionsChanged }) => {
const { showMsgWarn } = useContext(MessagingСtx);
//Работа со связями на сервере
- const { removeRl } = useQueryRelations(query);
+ const { removeRl, setRlMandatory } = useQueryRelations(query);
//Уведомление родителя об изменении свойств
const notifyOptionsChanged = () => onOptionsChanged && onOptionsChanged();
@@ -39,11 +53,24 @@ const InspectorQueryRelations = ({ query, relation, onOptionsChanged }) => {
}
});
+ //При изменении состояния обязательности связи в запросе
+ const handleRelationMandatoryChange = async e => {
+ await setRlMandatory(relation.id, e.target.checked === true ? 1 : 0);
+ notifyOptionsChanged();
+ };
+
//Формирование представления
return (
-
+ <>
+ }
+ label={"Обязательна"}
+ />
+
+ >
);
};
diff --git a/app/panels/query_editor/components/query_diagram/query_diagram.js b/app/panels/query_editor/components/query_diagram/query_diagram.js
index a269c9d..88c51ff 100644
--- a/app/panels/query_editor/components/query_diagram/query_diagram.js
+++ b/app/panels/query_editor/components/query_diagram/query_diagram.js
@@ -194,10 +194,7 @@ const QueryDiagram = ({
onEdgeClick={handleEdgeClick}
onEdgesChange={handleEdgesChange}
onPaneClick={handlePaneClick}
- defaultEdgeOptions={{
- animated: true,
- style: STYLES.EDGE
- }}
+ defaultEdgeOptions={{ style: STYLES.EDGE }}
connectionLineStyle={STYLES.CONNECTION_LINE}
onConnect={handleConnect}
isValidConnection={validateConnection}
diff --git a/app/panels/query_editor/components/relation/relation.js b/app/panels/query_editor/components/relation/relation.js
index a7b62a5..782663c 100644
--- a/app/panels/query_editor/components/relation/relation.js
+++ b/app/panels/query_editor/components/relation/relation.js
@@ -17,7 +17,8 @@ import PropTypes from "prop-types"; //Контроль свойств компо
const RELATION_SHAPE = PropTypes.shape({
id: PropTypes.string.isRequired,
source: PropTypes.string.isRequired,
- target: PropTypes.string.isRequired
+ target: PropTypes.string.isRequired,
+ mandatory: PropTypes.number.isRequired
});
//----------------
diff --git a/app/panels/query_editor/hooks.js b/app/panels/query_editor/hooks.js
index b62396c..4d89f21 100644
--- a/app/panels/query_editor/hooks.js
+++ b/app/panels/query_editor/hooks.js
@@ -89,7 +89,7 @@ const serverQueryData2QueryDiagram = (entities, relations) => {
result.nodes = [...result.nodes, ...nodes];
});
//Грани для диаграммы
- result.edges = relations.map(r => ({ ...r }));
+ result.edges = relations.map(r => ({ ...r, animated: r.mandatory === 1 ? false : true }));
//Вернем итоговый результат
return result;
};
diff --git a/db/PKG_P8PANELS_QE.pck b/db/PKG_P8PANELS_QE.pck
index 322f741..c0929b8 100644
--- a/db/PKG_P8PANELS_QE.pck
+++ b/db/PKG_P8PANELS_QE.pck
@@ -111,6 +111,14 @@ create or replace package PKG_P8PANELS_QE as
SID in varchar2 -- Идентификатор связи
);
+ /* Установка признака обязательности связи */
+ procedure QUERY_RL_MANDATORY_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ SID in varchar2, -- Идентификатор связи
+ NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
+ );
+
/* Добавление аргумента запроса */
procedure QUERY_OPT_ARG_ADD
(
@@ -469,7 +477,7 @@ create or replace package body PKG_P8PANELS_QE as
/* Читаем существующие связи */
RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
/* Формируем описание новой связи и добавляем её в коллекцию */
- PKG_P8PANELS_QE_BASE.TRLS_ADD(RRLS => RRLS, SSOURCE => SSOURCE, STARGET => STARGET);
+ PKG_P8PANELS_QE_BASE.TRLS_ADD(RRLS => RRLS, SSOURCE => SSOURCE, STARGET => STARGET, NMANDATORY => 0);
/* Сохраняем обновленный набор связей */
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
/* Переформируем SQL-выражение */
@@ -504,6 +512,32 @@ create or replace package body PKG_P8PANELS_QE as
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
end QUERY_RL_REMOVE;
+ /* Установка признака обязательности связи */
+ procedure QUERY_RL_MANDATORY_SET
+ (
+ NRN in number, -- Рег. номер запроса
+ SID in varchar2, -- Идентификатор связи
+ NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
+ )
+ is
+ RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
+ RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
+ SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
+ SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
+ begin
+ /* Читаем запись запроса */
+ RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
+ /* Читаем существующие связи */
+ RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
+ /* Устанавливаем признакобязательности */
+ PKG_P8PANELS_QE_BASE.TRLS_MANDATORY_SET(RRLS => RRLS, SID => SID, NMANDATORY => NMANDATORY);
+ /* Сохраняем обновленный набор связей */
+ PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
+ /* Переформируем SQL-выражение */
+ PKG_P8PANELS_QE_BASE.QUERY_SQL_BUILD(NRN => RQ.RN, SQRY => SQRY, SQRY_MSG => SQRY_MSG);
+ PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
+ end QUERY_RL_MANDATORY_SET;
+
/* Добавление аргумента запроса */
procedure QUERY_OPT_ARG_ADD
(
diff --git a/db/PKG_P8PANELS_QE_BASE.pck b/db/PKG_P8PANELS_QE_BASE.pck
index 56ba0f4..577e9b8 100644
--- a/db/PKG_P8PANELS_QE_BASE.pck
+++ b/db/PKG_P8PANELS_QE_BASE.pck
@@ -57,7 +57,8 @@ create or replace package PKG_P8PANELS_QE_BASE as
(
SID PKG_STD.TSTRING, -- Уникальный идентификатор в запросе
SSOURCE PKG_STD.TSTRING, -- Идентификатор атрибута-источника
- STARGET PKG_STD.TSTRING -- Идентификатор атрибута-приёмника
+ STARGET PKG_STD.TSTRING, -- Идентификатор атрибута-приёмника
+ NMANDATORY PKG_STD.TNUMBER -- Флаг обязательности (1 - да, 0 - нет)
);
/* Типы данных - Коллекция отношений */
@@ -165,7 +166,8 @@ create or replace package PKG_P8PANELS_QE_BASE as
(
RRLS in out nocopy TRLS, -- Изменяемая коллекция
SSOURCE in varchar2, -- Источник
- STARGET in varchar2 -- Приёмник
+ STARGET in varchar2, -- Приёмник
+ NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
);
/* Удаление связи из коллекции */
@@ -175,6 +177,14 @@ create or replace package PKG_P8PANELS_QE_BASE as
SID in varchar2 -- Идентификатор удялемой связи
);
+ /* Установка признака обязательности связи в коллекции */
+ procedure TRLS_MANDATORY_SET
+ (
+ RRLS in out nocopy TRLS, -- Изменяемая коллекция
+ SID in varchar2, -- Идентификатор связи
+ NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
+ );
+
/* Подчистка коллекции связей по атрибуту */
procedure TRLS_CLEANUP_BY_ATTR
(
@@ -1649,15 +1659,17 @@ create or replace package body PKG_P8PANELS_QE_BASE as
(
SID in varchar2 := null, -- Идентификатор (null - автоформирование)
SSOURCE in varchar2, -- Источник
- STARGET in varchar2 -- Приёмник
+ STARGET in varchar2, -- Приёмник
+ NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
) return TRL -- Описание связи
is
RRL TRL; -- Буфер для результата
begin
/* Собираем описание связи */
- RRL.SID := COALESCE(SID, TRL_ID_MAKE(SSOURCE => SSOURCE, STARGET => STARGET));
- RRL.SSOURCE := SSOURCE;
- RRL.STARGET := STARGET;
+ RRL.SID := COALESCE(SID, TRL_ID_MAKE(SSOURCE => SSOURCE, STARGET => STARGET));
+ RRL.SSOURCE := SSOURCE;
+ RRL.STARGET := STARGET;
+ RRL.NMANDATORY := NMANDATORY;
/* Вернем полученное */
return RRL;
end TRL_MAKE;
@@ -1675,6 +1687,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
PKG_XFAST.ATTR(SNAME => SATTR_ID, SVALUE => RRL.SID);
PKG_XFAST.ATTR(SNAME => SATTR_SOURCE, SVALUE => RRL.SSOURCE);
PKG_XFAST.ATTR(SNAME => SATTR_TARGET, SVALUE => RRL.STARGET);
+ PKG_XFAST.ATTR(SNAME => SATTR_MANDATORY, NVALUE => RRL.NMANDATORY);
/* Закрываем описание связи */
PKG_XFAST.UP();
end TRL_TO_XML;
@@ -1700,9 +1713,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as
/* Считаваем узел связи */
XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_RL);
/* Получаем значения */
- RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
- RRES.SSOURCE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_SOURCE);
- RRES.STARGET := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TARGET);
+ RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
+ RRES.SSOURCE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_SOURCE);
+ RRES.STARGET := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TARGET);
+ RRES.NMANDATORY := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_MANDATORY);
/* Освободим документ */
PKG_XPATH.FREE(RDOCUMENT => XDOC);
exception
@@ -1766,7 +1780,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as
if (((NLIST_TYPE = 0) and (RRLS(I).SSOURCE = SSOURCE_TARGET)) or
((NLIST_TYPE = 1) and (RRLS(I).STARGET = SSOURCE_TARGET))) then
RRES.EXTEND();
- RRES(RRES.LAST) := TRL_MAKE(SID => RRLS(I).SID, SSOURCE => RRLS(I).SSOURCE, STARGET => RRLS(I).STARGET);
+ RRES(RRES.LAST) := TRL_MAKE(SID => RRLS(I).SID,
+ SSOURCE => RRLS(I).SSOURCE,
+ STARGET => RRLS(I).STARGET,
+ NMANDATORY => RRLS(I).NMANDATORY);
end if;
exception
when NO_DATA_FOUND then
@@ -1783,7 +1800,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as
(
RRLS in out nocopy TRLS, -- Изменяемая коллекция
SSOURCE in varchar2, -- Источник
- STARGET in varchar2 -- Приёмник
+ STARGET in varchar2, -- Приёмник
+ NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
)
is
RRL TRL; -- Добавляемая связь
@@ -1793,7 +1811,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
RRLS := TRLS();
end if;
/* Формируем пописание связи */
- RRL := TRL_MAKE(SSOURCE => SSOURCE, STARGET => STARGET);
+ RRL := TRL_MAKE(SSOURCE => SSOURCE, STARGET => STARGET, NMANDATORY => NMANDATORY);
/* Добавляем её в коллекцию */
RRLS.EXTEND();
RRLS(RRLS.LAST) := RRL;
@@ -1816,6 +1834,22 @@ create or replace package body PKG_P8PANELS_QE_BASE as
end if;
end TRLS_REMOVE;
+ /* Установка признака обязательности связи в коллекции */
+ procedure TRLS_MANDATORY_SET
+ (
+ RRLS in out nocopy TRLS, -- Изменяемая коллекция
+ SID in varchar2, -- Идентификатор связи
+ NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
+ )
+ is
+ NIND PKG_STD.TNUMBER; -- Индекс связи в коллекции
+ begin
+ NIND := TRLS_INDEX_BY_ID(RRLS => RRLS, SID => SID);
+ if (NIND is not null) then
+ RRLS(NIND).NMANDATORY := NMANDATORY;
+ end if;
+ end TRLS_MANDATORY_SET;
+
/* Подчистка коллекции связей по атрибуту */
procedure TRLS_CLEANUP_BY_ATTR
(
@@ -2806,6 +2840,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
)
is
NENT_SRC PKG_STD.TNUMBER; -- Индекс сущности-источника связи
+ SJOIN PKG_STD.TSTRING; -- Буфер для операции объединения
STABLE PKG_STD.TSTRING; -- Буфер для таблицы запроса
begin
/* Обходим связи */
@@ -2823,8 +2858,14 @@ create or replace package body PKG_P8PANELS_QE_BASE as
RRLS (I)
.STARGET);
end if;
+ /* Определим тип объединения */
+ if (RRLS(I).NMANDATORY = 1) then
+ SJOIN := 'join';
+ else
+ SJOIN := 'left join';
+ end if;
/* Соберем выражение и добавим его в запрос */
- STABLE := 'left join ' || RENTS(NENT_SRC).SNAME || ' ' || RENTS(NENT_SRC).SID || ' on ' || RRLS(I).STARGET ||
+ STABLE := SJOIN || ' ' || RENTS(NENT_SRC).SNAME || ' ' || RENTS(NENT_SRC).SID || ' on ' || RRLS(I).STARGET ||
' = ' || RRLS(I).SSOURCE;
PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => STABLE);
/* Добавим связи сущности-источника */
@@ -2874,12 +2915,6 @@ create or replace package body PKG_P8PANELS_QE_BASE as
end if;
end BUILD_WHERE;
begin
- /*
- TODO: owner="root" created="10.09.2025"
- text="Предусмотреть связям признак ""обязательность""
- с ним - join
- без него - left join"
- */
/*
TODO: owner="root" created="10.09.2025"
text="Предусмотреть отладочные значения для аргументов запроса"