ЦИТК-979 - Редактор запросов - Поддержка подстановки отладочных значений аргументов в SQL-выражение

This commit is contained in:
Mikhail Chechnev 2025-10-14 00:09:52 +03:00
parent 64145d7ac0
commit 1530bfa3bf
6 changed files with 177 additions and 38 deletions

View File

@ -18,14 +18,25 @@ import { InspectorQueryArguments } from "../inspector_query_args/inspector_query
import { InspectorQueryConditions } from "../inspector_query_cond/inspector_query_cond"; //Управление условиями отбора запроса import { InspectorQueryConditions } from "../inspector_query_cond/inspector_query_cond"; //Управление условиями отбора запроса
import { InspectorQueryEntities } from "../inspector_query_ents/inspector_query_ents"; //Управление сущностями запроса import { InspectorQueryEntities } from "../inspector_query_ents/inspector_query_ents"; //Управление сущностями запроса
import { InspectorQueryRelations } from "../inspector_query_rls/inspector_query_rls"; //Управление связями запроса 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(); const handleOptionsChanged = () => onOptionsChanged && onOptionsChanged();
@ -44,7 +55,7 @@ const Inspector = ({ query, entity, relation, entities = [], args = [], cond = n
<InspectorQueryRelations query={query} relation={relation} onOptionsChanged={handleOptionsChanged} /> <InspectorQueryRelations query={query} relation={relation} onOptionsChanged={handleOptionsChanged} />
</> </>
)} )}
<QueryArea qry={qry} qryMsg={qryMsg} /> <InspectorQueryArea query={query} substArgsVals={substArgsVals} qry={qry} qryMsg={qryMsg} onOptionsChanged={handleOptionsChanged} />
</P8PEditorBox> </P8PEditorBox>
); );
}; };
@ -57,6 +68,7 @@ Inspector.propTypes = {
entities: PropTypes.arrayOf(ENTITY_SHAPE), entities: PropTypes.arrayOf(ENTITY_SHAPE),
args: PropTypes.arrayOf(ARGUMENT_SHAPE), args: PropTypes.arrayOf(ARGUMENT_SHAPE),
cond: PropTypes.string, cond: PropTypes.string,
substArgsVals: PropTypes.number,
qry: PropTypes.string, qry: PropTypes.string,
qryMsg: PropTypes.string, qryMsg: PropTypes.string,
onOptionsChanged: PropTypes.func onOptionsChanged: PropTypes.func

View File

@ -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 };

View File

@ -1,6 +1,6 @@
/* /*
Парус 8 - Панели мониторинга - Редактор запросов Парус 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 { Fab, Icon, Drawer, IconButton, TextField, Stack, Box, Snackbar, Alert } from "@mui/material"; //Компоненты MUI
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
import { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения import { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения
import { useQuerySQLExpr } from "./hooks"; //Пользовательские хуки для работы с SQL-выражением
//--------- //---------
//Константы //Константы
@ -38,8 +39,8 @@ const SNACK_BAR_MESSAGE_INIT = { text: null, type: null };
//Тело модуля //Тело модуля
//----------- //-----------
//Область запроса //Область SQL-выражения
const QueryArea = ({ qry = "", qryMsg = "" }) => { const InspectorQueryArea = ({ query, substArgsVals = 0, qry = "", qryMsg = "", onOptionsChanged }) => {
//Собственное состояние - текст всплывающего сообщения //Собственное состояние - текст всплывающего сообщения
const [snackBarMessage, setSnackBarMessage] = useState(SNACK_BAR_MESSAGE_INIT); const [snackBarMessage, setSnackBarMessage] = useState(SNACK_BAR_MESSAGE_INIT);
@ -49,6 +50,18 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => {
//Собственное состояние - развёрнутость //Собственное состояние - развёрнутость
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
//Работа с SQL-выражением
const { toggleSubstArgsVals } = useQuerySQLExpr(query);
//Уведомление родителя об изменении свойств
const notifyOptionsChanged = () => onOptionsChanged && onOptionsChanged();
//При нажатии на кнопку отображения/сокрытия значений аргументов в SQL-выражении запроса
const handleToggleSubstArgsValsClick = async () => {
await toggleSubstArgsVals();
notifyOptionsChanged();
};
//При нажатии на кнопку копирования текста запроса //При нажатии на кнопку копирования текста запроса
const handleCopyClick = async () => { const handleCopyClick = async () => {
try { try {
@ -87,9 +100,17 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => {
<Box p={2}> <Box p={2}>
<Stack direction={"row"} justifyContent={"right"} spacing={2}> <Stack direction={"row"} justifyContent={"right"} spacing={2}>
{qry && ( {qry && (
<IconButton onClick={handleCopyClick} title={"Скопировать текст запроса"}> <>
<Icon>content_copy</Icon> <IconButton
</IconButton> onClick={handleToggleSubstArgsValsClick}
title={substArgsVals === 1 ? "Скрыть значения аргументов" : "Отобразить значения аргументов"}
>
<Icon>{substArgsVals === 1 ? "code_off" : "code"}</Icon>
</IconButton>
<IconButton onClick={handleCopyClick} title={"Скопировать текст запроса"}>
<Icon>content_copy</Icon>
</IconButton>
</>
)} )}
<IconButton onClick={handleExpandClick} title={expanded ? "Свернуть" : "Развернуть"}> <IconButton onClick={handleExpandClick} title={expanded ? "Свернуть" : "Развернуть"}>
<Icon>{expanded ? "expand_more" : "expand_less"}</Icon> <Icon>{expanded ? "expand_more" : "expand_less"}</Icon>
@ -139,14 +160,17 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => {
); );
}; };
//Контроль свойств компонента - Область запроса //Контроль свойств компонента - Область SQL-выражения
QueryArea.propTypes = { InspectorQueryArea.propTypes = {
query: PropTypes.number.isRequired,
substArgsVals: PropTypes.number,
qry: PropTypes.string, qry: PropTypes.string,
qryMsg: PropTypes.string qryMsg: PropTypes.string,
onOptionsChanged: PropTypes.func
}; };
//---------------- //----------------
//Интерфейс модуля //Интерфейс модуля
//---------------- //----------------
export { QueryArea }; export { InspectorQueryArea };

View File

@ -139,7 +139,11 @@ const useQuery = query => {
loader: true loader: true
}); });
setQueryDiagram(serverQueryData2QueryDiagram(data?.XENTS?.XENT || [], data?.XRLS?.XRL || [])); 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 }); setQuerySQL({ qry: data?.XQRY, qryMsg: data?.XQRY_MSG });
setInit(true); setInit(true);
} finally { } finally {

View File

@ -155,6 +155,12 @@ create or replace package PKG_P8PANELS_QE as
SCOND in varchar2 -- Условия отбора SCOND in varchar2 -- Условия отбора
); );
/* Переключение флага подстановки значений аргументов запроса в SQL-выражение */
procedure QUERY_OPT_SUBST_ARGS_VALS_TGL
(
NRN in number -- Рег. номер запроса
);
end PKG_P8PANELS_QE; end PKG_P8PANELS_QE;
/ /
create or replace package body PKG_P8PANELS_QE as 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); PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_OPT_COND_SET; 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; end PKG_P8PANELS_QE;
/ /

View File

@ -16,8 +16,9 @@ create or replace package PKG_P8PANELS_QE_BASE as
/* Типы данных - Настройка запроса */ /* Типы данных - Настройка запроса */
type TOPT is record type TOPT is record
( (
RARGS TARGS, -- Аргументы RARGS TARGS, -- Аргументы
SCOND PKG_STD.TSTRING -- Условия отбора 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'; -- Представление SENT_TYPE_VIEW constant PKG_STD.TSTRING := 'VIEW'; -- Представление
/* Константы - Теги для сериализации */ /* Константы - Теги для сериализации */
STAG_DATA constant PKG_STD.TSTRING := 'XDATA'; -- Данные STAG_DATA constant PKG_STD.TSTRING := 'XDATA'; -- Данные
STAG_QUERIES constant PKG_STD.TSTRING := 'XQUERIES'; -- Запросы STAG_QUERIES constant PKG_STD.TSTRING := 'XQUERIES'; -- Запросы
STAG_QUERY constant PKG_STD.TSTRING := 'XQUERY'; -- Запрос STAG_QUERY constant PKG_STD.TSTRING := 'XQUERY'; -- Запрос
STAG_ARGS constant PKG_STD.TSTRING := 'XARGS'; -- Аргументы запроса STAG_ARGS constant PKG_STD.TSTRING := 'XARGS'; -- Аргументы запроса
STAG_ARG constant PKG_STD.TSTRING := 'XARG'; -- Аргумент запроса STAG_ARG constant PKG_STD.TSTRING := 'XARG'; -- Аргумент запроса
STAG_ATTRS constant PKG_STD.TSTRING := 'XATTRS'; -- Атрибуты сущности STAG_ATTRS constant PKG_STD.TSTRING := 'XATTRS'; -- Атрибуты сущности
STAG_ATTR constant PKG_STD.TSTRING := 'XATTR'; -- Атрибут сущности STAG_ATTR constant PKG_STD.TSTRING := 'XATTR'; -- Атрибут сущности
STAG_ENTS constant PKG_STD.TSTRING := 'XENTS'; -- Сущности STAG_ENTS constant PKG_STD.TSTRING := 'XENTS'; -- Сущности
STAG_ENT constant PKG_STD.TSTRING := 'XENT'; -- Сущность STAG_ENT constant PKG_STD.TSTRING := 'XENT'; -- Сущность
STAG_RLS constant PKG_STD.TSTRING := 'XRLS'; -- Связи STAG_RLS constant PKG_STD.TSTRING := 'XRLS'; -- Связи
STAG_RL constant PKG_STD.TSTRING := 'XRL'; -- Связь STAG_RL constant PKG_STD.TSTRING := 'XRL'; -- Связь
STAG_OPT constant PKG_STD.TSTRING := 'XOPT'; -- Настройка запроса STAG_OPT constant PKG_STD.TSTRING := 'XOPT'; -- Настройка запроса
STAG_COND constant PKG_STD.TSTRING := 'XCOND'; -- Условия запроса STAG_COND constant PKG_STD.TSTRING := 'XCOND'; -- Условия запроса
STAG_QRY constant PKG_STD.TSTRING := 'XQRY'; -- SQL-выражение запроса STAG_SUBST_ARGS_VALS constant PKG_STD.TSTRING := 'XSUBST_ARGS_VALS'; -- Флаг подстановки значений аргументов в запрос
STAG_QRY_MSG constant PKG_STD.TSTRING := 'XQRY_MSG'; -- Сообщение при формировании запроса STAG_QRY constant PKG_STD.TSTRING := 'XQRY'; -- SQL-выражение запроса
STAG_QRY_MSG constant PKG_STD.TSTRING := 'XQRY_MSG'; -- Сообщение при формировании запроса
/* Константы - Атрибуты для сериализации */ /* Константы - Атрибуты для сериализации */
SATTR_ID constant PKG_STD.TSTRING := 'id'; -- Идентификатор 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); TARGS_TO_XML(RARGS => ROPT.RARGS);
/* Условия отбора */ /* Условия отбора */
PKG_XFAST.HERB(SNAME => STAG_COND, SVALUE => ROPT.SCOND); 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(); PKG_XFAST.UP();
end TOPT_TO_XML; 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); XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_OPT);
/* Получаем значения */ /* Получаем значения */
RRES.SCOND := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => STAG_COND); 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, RRES.NSUBST_ARGS_VALS := PKG_XPATH.VALUE_NUM(RNODE => XNODE, SPATTERN => STAG_SUBST_ARGS_VALS);
SPATTERN => STAG_ARGS))); 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); PKG_XPATH.FREE(RDOCUMENT => XDOC);
exception exception
@ -2960,12 +2965,38 @@ create or replace package body PKG_P8PANELS_QE_BASE as
if (ROPT.SCOND is not null) then if (ROPT.SCOND is not null) then
PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => 'where ' || ROPT.SCOND); PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => 'where ' || ROPT.SCOND);
end if; 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; end BUILD_WHERE;
begin begin
/*
TODO: owner="root" created="10.09.2025"
text="Предусмотреть отладочные значения для аргументов запроса"
*/
/* Читаем описание запроса */ /* Читаем описание запроса */
RQ := QUERY_GET(NRN => NRN); RQ := QUERY_GET(NRN => NRN);
/* Читаем сущности запроса */ /* Читаем сущности запроса */