ЦИТК-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 { 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
<InspectorQueryRelations query={query} relation={relation} onOptionsChanged={handleOptionsChanged} />
</>
)}
<QueryArea qry={qry} qryMsg={qryMsg} />
<InspectorQueryArea query={query} substArgsVals={substArgsVals} qry={qry} qryMsg={qryMsg} onOptionsChanged={handleOptionsChanged} />
</P8PEditorBox>
);
};
@ -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

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 - Панели мониторинга - Редактор запросов
Область запроса
Область 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 = "" }) => {
<Box p={2}>
<Stack direction={"row"} justifyContent={"right"} spacing={2}>
{qry && (
<>
<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 ? "Свернуть" : "Развернуть"}>
<Icon>{expanded ? "expand_more" : "expand_less"}</Icon>
@ -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 };

View File

@ -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 {

View File

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

View File

@ -17,7 +17,8 @@ create or replace package PKG_P8PANELS_QE_BASE as
type TOPT is record
(
RARGS TARGS, -- Аргументы
SCOND PKG_STD.TSTRING -- Условия отбора
SCOND PKG_STD.TSTRING, -- Условия отбора
NSUBST_ARGS_VALS PKG_STD.TNUMBER -- Флаг подстановки значений аргументов в запрос (1 - да, 0 - нет)
);
/* Типы данных - Атрибут сущности */
@ -365,6 +366,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
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'; -- Сообщение при формировании запроса
@ -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;
@ -798,6 +802,7 @@ 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.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)));
/* Освободим документ */
@ -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);
/* Читаем сущности запроса */