From 1530bfa3bffd7c6be5f75f0fb3bbee1ad23d478c Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Tue, 14 Oct 2025 00:09:52 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A6=D0=98=D0=A2=D0=9A-979=20-=20=D0=A0=D0=B5?= =?UTF-8?q?=D0=B4=D0=B0=D0=BA=D1=82=D0=BE=D1=80=20=D0=B7=D0=B0=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=81=D0=BE=D0=B2=20-=20=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5?= =?UTF-8?q?=D1=80=D0=B6=D0=BA=D0=B0=20=D0=BF=D0=BE=D0=B4=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=BE=D1=82=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=BE=D1=87=D0=BD=D1=8B=D1=85=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9=20=D0=B0=D1=80=D0=B3=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=20=D0=B2=20SQL-=D0=B2=D1=8B=D1=80=D0=B0?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/inspector/inspector.js | 18 ++++- .../components/inspector_query_area/hooks.js | 35 ++++++++ .../inspector_query_area.js} | 44 ++++++++--- app/panels/query_editor/hooks.js | 6 +- db/PKG_P8PANELS_QE.pck | 33 ++++++++ db/PKG_P8PANELS_QE_BASE.pck | 79 +++++++++++++------ 6 files changed, 177 insertions(+), 38 deletions(-) create mode 100644 app/panels/query_editor/components/inspector_query_area/hooks.js rename app/panels/query_editor/components/{inspector/query_area.js => inspector_query_area/inspector_query_area.js} (75%) diff --git a/app/panels/query_editor/components/inspector/inspector.js b/app/panels/query_editor/components/inspector/inspector.js index 34d5124..04932c2 100644 --- a/app/panels/query_editor/components/inspector/inspector.js +++ b/app/panels/query_editor/components/inspector/inspector.js @@ -18,14 +18,25 @@ import { InspectorQueryArguments } from "../inspector_query_args/inspector_query import { InspectorQueryConditions } from "../inspector_query_cond/inspector_query_cond"; //Управление условиями отбора запроса import { InspectorQueryEntities } from "../inspector_query_ents/inspector_query_ents"; //Управление сущностями запроса import { InspectorQueryRelations } from "../inspector_query_rls/inspector_query_rls"; //Управление связями запроса -import { QueryArea } from "./query_area"; //Область запроса +import { InspectorQueryArea } from "../inspector_query_area/inspector_query_area"; //Область SQL-выражения //----------- //Тело модуля //----------- //Инспектор свойств -const Inspector = ({ query, entity, relation, entities = [], args = [], cond = null, qry = "", qryMsg = "", onOptionsChanged = null }) => { +const Inspector = ({ + query, + entity, + relation, + entities = [], + args = [], + cond = null, + substArgsVals = 0, + qry = "", + qryMsg = "", + onOptionsChanged = null +}) => { //При изменении настроек запроса const handleOptionsChanged = () => onOptionsChanged && onOptionsChanged(); @@ -44,7 +55,7 @@ const Inspector = ({ query, entity, relation, entities = [], args = [], cond = n )} - + ); }; @@ -57,6 +68,7 @@ Inspector.propTypes = { entities: PropTypes.arrayOf(ENTITY_SHAPE), args: PropTypes.arrayOf(ARGUMENT_SHAPE), cond: PropTypes.string, + substArgsVals: PropTypes.number, qry: PropTypes.string, qryMsg: PropTypes.string, onOptionsChanged: PropTypes.func diff --git a/app/panels/query_editor/components/inspector_query_area/hooks.js b/app/panels/query_editor/components/inspector_query_area/hooks.js new file mode 100644 index 0000000..79dc68b --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_area/hooks.js @@ -0,0 +1,35 @@ +/* + Парус 8 - Панели мониторинга - Редактор запросов + Пользовательские хуки для работы с областью SQL-выражения +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useContext, useCallback } from "react"; //Классы React +import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером + +//----------- +//Тело модуля +//----------- + +//Работа с областью SQL-выражения +const useQuerySQLExpr = query => { + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Установка флага сокрытия/отображения значений аргументов в SQL-выражении запроса + const toggleSubstArgsVals = useCallback(async () => { + await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_OPT_SUBST_ARGS_VALS_TGL", args: { NRN: query }, loader: false }); + }, [query, executeStored]); + + //Возвращаем интерфейс хука + return { toggleSubstArgsVals }; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { useQuerySQLExpr }; diff --git a/app/panels/query_editor/components/inspector/query_area.js b/app/panels/query_editor/components/inspector_query_area/inspector_query_area.js similarity index 75% rename from app/panels/query_editor/components/inspector/query_area.js rename to app/panels/query_editor/components/inspector_query_area/inspector_query_area.js index 6db3b9d..eeaa004 100644 --- a/app/panels/query_editor/components/inspector/query_area.js +++ b/app/panels/query_editor/components/inspector_query_area/inspector_query_area.js @@ -1,6 +1,6 @@ /* Парус 8 - Панели мониторинга - Редактор запросов - Область запроса + Область SQL-выражения */ //--------------------- @@ -12,6 +12,7 @@ import PropTypes from "prop-types"; //Контроль свойств компо import { Fab, Icon, Drawer, IconButton, TextField, Stack, Box, Snackbar, Alert } from "@mui/material"; //Компоненты MUI import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения import { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения +import { useQuerySQLExpr } from "./hooks"; //Пользовательские хуки для работы с SQL-выражением //--------- //Константы @@ -38,8 +39,8 @@ const SNACK_BAR_MESSAGE_INIT = { text: null, type: null }; //Тело модуля //----------- -//Область запроса -const QueryArea = ({ qry = "", qryMsg = "" }) => { +//Область SQL-выражения +const InspectorQueryArea = ({ query, substArgsVals = 0, qry = "", qryMsg = "", onOptionsChanged }) => { //Собственное состояние - текст всплывающего сообщения const [snackBarMessage, setSnackBarMessage] = useState(SNACK_BAR_MESSAGE_INIT); @@ -49,6 +50,18 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => { //Собственное состояние - развёрнутость const [expanded, setExpanded] = useState(false); + //Работа с SQL-выражением + const { toggleSubstArgsVals } = useQuerySQLExpr(query); + + //Уведомление родителя об изменении свойств + const notifyOptionsChanged = () => onOptionsChanged && onOptionsChanged(); + + //При нажатии на кнопку отображения/сокрытия значений аргументов в SQL-выражении запроса + const handleToggleSubstArgsValsClick = async () => { + await toggleSubstArgsVals(); + notifyOptionsChanged(); + }; + //При нажатии на кнопку копирования текста запроса const handleCopyClick = async () => { try { @@ -87,9 +100,17 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => { {qry && ( - - content_copy - + <> + + {substArgsVals === 1 ? "code_off" : "code"} + + + content_copy + + )} {expanded ? "expand_more" : "expand_less"} @@ -139,14 +160,17 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => { ); }; -//Контроль свойств компонента - Область запроса -QueryArea.propTypes = { +//Контроль свойств компонента - Область SQL-выражения +InspectorQueryArea.propTypes = { + query: PropTypes.number.isRequired, + substArgsVals: PropTypes.number, qry: PropTypes.string, - qryMsg: PropTypes.string + qryMsg: PropTypes.string, + onOptionsChanged: PropTypes.func }; //---------------- //Интерфейс модуля //---------------- -export { QueryArea }; +export { InspectorQueryArea }; diff --git a/app/panels/query_editor/hooks.js b/app/panels/query_editor/hooks.js index 4d89f21..38d83c4 100644 --- a/app/panels/query_editor/hooks.js +++ b/app/panels/query_editor/hooks.js @@ -139,7 +139,11 @@ const useQuery = query => { loader: true }); setQueryDiagram(serverQueryData2QueryDiagram(data?.XENTS?.XENT || [], data?.XRLS?.XRL || [])); - setQueryOption({ args: data?.XOPT?.XARGS?.XARG || [], cond: data?.XOPT?.XCOND || null }); + setQueryOption({ + args: data?.XOPT?.XARGS?.XARG || [], + cond: data?.XOPT?.XCOND || null, + substArgsVals: data?.XOPT?.XSUBST_ARGS_VALS || 0 + }); setQuerySQL({ qry: data?.XQRY, qryMsg: data?.XQRY_MSG }); setInit(true); } finally { diff --git a/db/PKG_P8PANELS_QE.pck b/db/PKG_P8PANELS_QE.pck index 88d0a23..2ed0b15 100644 --- a/db/PKG_P8PANELS_QE.pck +++ b/db/PKG_P8PANELS_QE.pck @@ -155,6 +155,12 @@ create or replace package PKG_P8PANELS_QE as SCOND in varchar2 -- Условия отбора ); + /* Переключение флага подстановки значений аргументов запроса в SQL-выражение */ + procedure QUERY_OPT_SUBST_ARGS_VALS_TGL + ( + NRN in number -- Рег. номер запроса + ); + end PKG_P8PANELS_QE; / create or replace package body PKG_P8PANELS_QE as @@ -651,5 +657,32 @@ create or replace package body PKG_P8PANELS_QE as PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN); end QUERY_OPT_COND_SET; + /* Переключение флага подстановки значений аргументов запроса в SQL-выражение */ + procedure QUERY_OPT_SUBST_ARGS_VALS_TGL + ( + NRN in number -- Рег. номер запроса + ) + is + RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса + ROPT PKG_P8PANELS_QE_BASE.TOPT; -- Настройка запроса + begin + /* Провим права доступа */ + PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER()); + /* Читаем запись запроса */ + RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN); + /* Читаем текущую настройку */ + ROPT := PKG_P8PANELS_QE_BASE.QUERY_OPT_GET(COPT => RQ.OPT); + /* Установим новый флаг подстановки значений аргументов в запрос */ + if (ROPT.NSUBST_ARGS_VALS = 1) then + ROPT.NSUBST_ARGS_VALS := 0; + else + ROPT.NSUBST_ARGS_VALS := 1; + end if; + /* Сохраняем обновленную настройку */ + PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT); + /* Переформируем SQL-выражение */ + PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN); + end QUERY_OPT_SUBST_ARGS_VALS_TGL; + end PKG_P8PANELS_QE; / diff --git a/db/PKG_P8PANELS_QE_BASE.pck b/db/PKG_P8PANELS_QE_BASE.pck index d1aaf98..c1b5e10 100644 --- a/db/PKG_P8PANELS_QE_BASE.pck +++ b/db/PKG_P8PANELS_QE_BASE.pck @@ -16,8 +16,9 @@ create or replace package PKG_P8PANELS_QE_BASE as /* Типы данных - Настройка запроса */ type TOPT is record ( - RARGS TARGS, -- Аргументы - SCOND PKG_STD.TSTRING -- Условия отбора + RARGS TARGS, -- Аргументы + SCOND PKG_STD.TSTRING, -- Условия отбора + NSUBST_ARGS_VALS PKG_STD.TNUMBER -- Флаг подстановки значений аргументов в запрос (1 - да, 0 - нет) ); /* Типы данных - Атрибут сущности */ @@ -352,21 +353,22 @@ create or replace package body PKG_P8PANELS_QE_BASE as SENT_TYPE_VIEW constant PKG_STD.TSTRING := 'VIEW'; -- Представление /* Константы - Теги для сериализации */ - STAG_DATA constant PKG_STD.TSTRING := 'XDATA'; -- Данные - STAG_QUERIES constant PKG_STD.TSTRING := 'XQUERIES'; -- Запросы - STAG_QUERY constant PKG_STD.TSTRING := 'XQUERY'; -- Запрос - STAG_ARGS constant PKG_STD.TSTRING := 'XARGS'; -- Аргументы запроса - STAG_ARG constant PKG_STD.TSTRING := 'XARG'; -- Аргумент запроса - STAG_ATTRS constant PKG_STD.TSTRING := 'XATTRS'; -- Атрибуты сущности - STAG_ATTR constant PKG_STD.TSTRING := 'XATTR'; -- Атрибут сущности - STAG_ENTS constant PKG_STD.TSTRING := 'XENTS'; -- Сущности - STAG_ENT constant PKG_STD.TSTRING := 'XENT'; -- Сущность - STAG_RLS constant PKG_STD.TSTRING := 'XRLS'; -- Связи - STAG_RL constant PKG_STD.TSTRING := 'XRL'; -- Связь - STAG_OPT constant PKG_STD.TSTRING := 'XOPT'; -- Настройка запроса - STAG_COND constant PKG_STD.TSTRING := 'XCOND'; -- Условия запроса - STAG_QRY constant PKG_STD.TSTRING := 'XQRY'; -- SQL-выражение запроса - STAG_QRY_MSG constant PKG_STD.TSTRING := 'XQRY_MSG'; -- Сообщение при формировании запроса + STAG_DATA constant PKG_STD.TSTRING := 'XDATA'; -- Данные + STAG_QUERIES constant PKG_STD.TSTRING := 'XQUERIES'; -- Запросы + STAG_QUERY constant PKG_STD.TSTRING := 'XQUERY'; -- Запрос + STAG_ARGS constant PKG_STD.TSTRING := 'XARGS'; -- Аргументы запроса + STAG_ARG constant PKG_STD.TSTRING := 'XARG'; -- Аргумент запроса + STAG_ATTRS constant PKG_STD.TSTRING := 'XATTRS'; -- Атрибуты сущности + STAG_ATTR constant PKG_STD.TSTRING := 'XATTR'; -- Атрибут сущности + STAG_ENTS constant PKG_STD.TSTRING := 'XENTS'; -- Сущности + STAG_ENT constant PKG_STD.TSTRING := 'XENT'; -- Сущность + STAG_RLS constant PKG_STD.TSTRING := 'XRLS'; -- Связи + STAG_RL constant PKG_STD.TSTRING := 'XRL'; -- Связь + STAG_OPT constant PKG_STD.TSTRING := 'XOPT'; -- Настройка запроса + STAG_COND constant PKG_STD.TSTRING := 'XCOND'; -- Условия запроса + STAG_SUBST_ARGS_VALS constant PKG_STD.TSTRING := 'XSUBST_ARGS_VALS'; -- Флаг подстановки значений аргументов в запрос + STAG_QRY constant PKG_STD.TSTRING := 'XQRY'; -- SQL-выражение запроса + STAG_QRY_MSG constant PKG_STD.TSTRING := 'XQRY_MSG'; -- Сообщение при формировании запроса /* Константы - Атрибуты для сериализации */ SATTR_ID constant PKG_STD.TSTRING := 'id'; -- Идентификатор @@ -770,6 +772,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as TARGS_TO_XML(RARGS => ROPT.RARGS); /* Условия отбора */ PKG_XFAST.HERB(SNAME => STAG_COND, SVALUE => ROPT.SCOND); + /* Флаг подстановки значений аргументов в запрос */ + PKG_XFAST.HERB(SNAME => STAG_SUBST_ARGS_VALS, NVALUE => ROPT.NSUBST_ARGS_VALS); /* Закрываем описание настройки */ PKG_XFAST.UP(); end TOPT_TO_XML; @@ -797,9 +801,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as /* Считаваем узел настройки */ XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_OPT); /* Получаем значения */ - RRES.SCOND := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => STAG_COND); - RRES.RARGS := TARGS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XNODE, - SPATTERN => STAG_ARGS))); + RRES.SCOND := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => STAG_COND); + RRES.NSUBST_ARGS_VALS := PKG_XPATH.VALUE_NUM(RNODE => XNODE, SPATTERN => STAG_SUBST_ARGS_VALS); + RRES.RARGS := TARGS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XNODE, + SPATTERN => STAG_ARGS))); /* Освободим документ */ PKG_XPATH.FREE(RDOCUMENT => XDOC); exception @@ -2960,12 +2965,38 @@ create or replace package body PKG_P8PANELS_QE_BASE as if (ROPT.SCOND is not null) then PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => 'where ' || ROPT.SCOND); end if; + if ((ROPT.NSUBST_ARGS_VALS = 1) and (ROPT.RARGS is not null) and (ROPT.RARGS.COUNT > 0)) then + for I in ROPT.RARGS.FIRST .. ROPT.RARGS.LAST + loop + begin + case + when ROPT.RARGS(I).NDATA_TYPE = PKG_STD.DATA_TYPE_STR then + SQRY := PKG_SQL_BUILD.VAR_REPLACE_TO_STR(SSQL => SQRY, + SNAME => ROPT.RARGS(I).SNAME, + SVALUE => ROPT.RARGS(I).SVALUE); + when ROPT.RARGS(I).NDATA_TYPE = PKG_STD.DATA_TYPE_NUM then + SQRY := PKG_SQL_BUILD.VAR_REPLACE_TO_NUM(SSQL => SQRY, + SNAME => ROPT.RARGS(I).SNAME, + NVALUE => TO_NUMBER(ROPT.RARGS(I).SVALUE)); + when ROPT.RARGS(I).NDATA_TYPE = PKG_STD.DATA_TYPE_DATE then + SQRY := PKG_SQL_BUILD.VAR_REPLACE_TO_DATE(SSQL => SQRY, + SNAME => ROPT.RARGS(I).SNAME, + DVALUE => TO_DATE(ROPT.RARGS(I).SVALUE, 'yyyy-mm-dd')); + else + SQRY := PKG_SQL_BUILD.VAR_REPLACE_TO_ANY(SSQL => SQRY, + SNAME => ROPT.RARGS(I).SNAME, + SANY => ROPT.RARGS(I).SVALUE); + end case; + exception + when others then + ADD_QRY_MSG(SMESSAGE => 'Ошибка конвертации при подстановке значения "' || ROPT.RARGS(I).SVALUE || + '" в аргумент запроса "' || ROPT.RARGS(I).SNAME || '".', + BCLEAR_QRY => false); + end; + end loop; + end if; end BUILD_WHERE; begin - /* - TODO: owner="root" created="10.09.2025" - text="Предусмотреть отладочные значения для аргументов запроса" - */ /* Читаем описание запроса */ RQ := QUERY_GET(NRN => NRN); /* Читаем сущности запроса */