Загрузка данных
create or replace package body MDG_MAILING_GROUPS_PKG is
BULK_LIMIT constant pls_integer := 1000;
ERROR constant varchar2(6) := 'Error';
-- -------------------------
g_debug boolean := false;
-- -------------------------
column_not_exist exception;
pragma exception_init(column_not_exist, -904);
-- -------------------------
function fn(p in varchar2) return varchar2 is
begin
return sys_context('USERENV','CURRENT_SCHEMA')||'.'||$$plsql_unit||'.'||p;
end;
function quoted(p in varchar2) return varchar2 is
begin
return '"'||p||'"';
end;
-- -------------------------
procedure debug(p_msg in varchar2) is
begin
if g_debug then
dbms_output.put_line(p_msg);
end if;
end;
-- -------------------------
procedure enableDebug is begin g_debug := true; end;
procedure disableDebug is begin g_debug := false; end;
-- -------------------------
procedure importStage(p_stage varchar2) is
begin
debug('Stage: '||p_stage);
xxmdg.mdgControl.importStage(p_stage);
end;
-- -------------------------
procedure importError(p_message varchar2, p_key varchar2 := null) is
begin
debug(ERROR||' message: '||p_message);
debug(ERROR||' key: '||p_key);
xxmdg.mdgControl.importError(p_message, p_key);
end;
-- -------------------------
function checkGroupName(p_group_name in varchar2) return varchar2 is
begin
if p_group_name is null or trim(p_group_name) is null then
return ERROR||': groupName не задан или пустой';
end if;
if p_group_name <> trim(p_group_name) then
return ERROR||': groupName содержит пробелы по краям';
end if;
return 'ok';
end;
-- -------------------------
function checkEmail(p_email in varchar2) return varchar2 is
begin
if p_email is null or trim(p_email) is null then
return ERROR||': emailAddress не задан';
end if;
if not regexp_like(trim(p_email),
'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') then
return ERROR||': emailAddress имеет неверный формат';
end if;
return 'ok';
end;
-- -------------------------
function checkName(p_name in varchar2) return varchar2 is
begin
if p_name is null or trim(p_name) is null then
return ERROR||': имя/фамилия не заданы';
end if;
return 'ok';
end;
-- -------------------------
function checkIsInactive(p_is_inactive in varchar2) return varchar2 is
begin
if p_is_inactive is null then
return 'ok';
end if;
if upper(trim(p_is_inactive)) not in ('Y','N') then
return ERROR||': isInactive допускает только Y/N или NULL';
end if;
return 'ok';
end;
-- -------------------------
function checkDates(p_valid_from in varchar2,
p_valid_to in varchar2) return varchar2 is
l_from date;
l_to date;
begin
if p_valid_from is null or trim(p_valid_from) is null then
return ERROR||': validFrom не задан';
end if;
if p_valid_to is null or trim(p_valid_to) is null then
return ERROR||': validTo не задан';
end if;
begin
l_from := to_date(p_valid_from, 'YYYY-MM-DD');
l_to := to_date(p_valid_to, 'YYYY-MM-DD');
exception
when others then
return ERROR||': неверный формат даты (ожидается YYYY-MM-DD)';
end;
if l_from > l_to then
return ERROR||': validFrom больше validTo';
end if;
return 'ok';
end;
-- -------------------------
function checkDuplicatesInFile return boolean is
l_query clob;
l_dup_g varchar2(4000);
l_dup_e varchar2(4000);
l_cnt pls_integer;
l_found boolean := false;
c sys_refcursor;
begin
l_query :=
'select lower("groupName"), lower("emailAddress"), count(*) '||
' from ('||xxmdg.mdgControl.parsedContentQuery||') '||
' group by lower("groupName"), lower("emailAddress") '||
'having count(*) > 1';
open c for l_query;
loop
fetch c into l_dup_g, l_dup_e, l_cnt;
exit when c%notfound;
l_found := true;
importError(
ERROR||': дубликат в файле (groupName, emailAddress): '||
l_dup_g||' / '||l_dup_e||' (встречается '||l_cnt||' раз)');
end loop;
close c;
return not l_found;
exception
when column_not_exist then
importError('One of columns ("groupName","emailAddress") not exists in dictionary.');
return false;
when others then
importError('Unexpected error during checkDuplicatesInFile: '||sqlerrm);
return false;
end;
-- -------------------------
function loadExcelData return boolean is
subtype column_value_t is varchar2(4000);
subtype column_name_t is varchar2(4000);
type columnTable is table of column_value_t;
type rowIdTable is table of pls_integer;
type columnsTable is table of columnTable index by column_name_t;
type loadTable is table of mdg_mailing_groups_excel%rowtype;
l_cr pls_integer;
l_cv varchar2(4000);
l_columns_data columnsTable;
l_row_ids rowIdTable;
l_load_table loadTable := loadTable();
l_is_processed boolean := false;
parseCursor sys_refcursor;
function toStr(p_s varchar2) return varchar2 is
begin
l_cv := p_s;
return p_s;
end;
-- -------------------------
function appendToLoad(p_i pls_integer) return boolean is
l_row mdg_mailing_groups_excel%rowtype;
l_check varchar2(4000);
function strValue(p_col varchar2) return varchar2 is
begin
return nvl(toStr(l_columns_data(p_col)(p_i)), null);
end;
begin
l_cr := l_row_ids(p_i);
debug('row: '||l_cr);
-- Чтение полей
l_row."groupName" := trim(strValue('groupName'));
l_row."emailAddress" := trim(strValue('emailAddress'));
l_row."firstName" := trim(strValue('firstName'));
l_row."lastName" := trim(strValue('lastName'));
l_row."isInactive" := upper(trim(strValue('isInactive')));
l_row."validFrom" := strValue('validFrom');
l_row."validTo" := strValue('validTo');
-- Проверки полей
l_check := checkGroupName(l_row."groupName");
if l_check <> 'ok' then
raise_application_error(-20001, l_check);
end if;
l_check := checkEmail(l_row."emailAddress");
if l_check <> 'ok' then
raise_application_error(-20001, l_check);
end if;
l_check := checkName(l_row."firstName");
if l_check <> 'ok' then
raise_application_error(-20001, l_check||' (firstName)');
end if;
l_check := checkName(l_row."lastName");
if l_check <> 'ok' then
raise_application_error(-20001, l_check||' (lastName)');
end if;
l_check := checkIsInactive(l_row."isInactive");
if l_check <> 'ok' then
raise_application_error(-20001, l_check);
end if;
l_check := checkDates(l_row."validFrom", l_row."validTo");
if l_check <> 'ok' then
raise_application_error(-20001, l_check);
end if;
l_load_table.extend;
l_load_table(l_load_table.last) := l_row;
return true;
exception
when others then
importError('On row '||l_cr||' value "'||l_cv||'" error: '||sqlerrm);
return false;
end;
-- -------------------------
function openCursor return boolean is
l_base_query clob;
l_query clob;
l_add_columns varchar2(32767);
begin
l_base_query := xxmdg.mdgControl.parsedContentQuery;
l_add_columns :=
quoted('groupName') ||', '||
quoted('emailAddress') ||', '||
quoted('firstName') ||', '||
quoted('lastName') ||', '||
quoted('isInactive') ||', '||
quoted('validFrom') ||', '||
quoted('validTo');
l_query := 'select row_id,'||l_add_columns||' from ('||l_base_query||')';
open parseCursor for l_query;
return true;
exception
when column_not_exist then
importError('One of columns ('||l_add_columns||') not exists in dictionary.');
return false;
when others then
importError('Unexpected error during openCursor: '||sqlerrm);
return false;
end;
-- -------------------------
procedure fetchCursor is
begin
fetch parseCursor bulk collect into
l_row_ids,
l_columns_data('groupName'),
l_columns_data('emailAddress'),
l_columns_data('firstName'),
l_columns_data('lastName'),
l_columns_data('isInactive'),
l_columns_data('validFrom'),
l_columns_data('validTo')
limit BULK_LIMIT;
end;
-- -------------------------
function processCursor return boolean is
l_row_appended boolean;
l_error_exists boolean := false;
begin
for i in 1..l_row_ids.count loop
l_row_appended := appendToLoad(i);
if not l_row_appended then
l_error_exists := true;
end if;
end loop;
if not l_error_exists then
forall j in 1..l_load_table.count
insert into mdg_mailing_groups_excel values l_load_table(j);
end if;
l_load_table.delete;
return not l_error_exists;
end;
begin
importStage('Preprocessing of parsed data.');
savepoint before_mdg_mailing_groups_excel;
-- 1) Дубли по всему файлу — одним запросом
if not checkDuplicatesInFile then
return false;
end if;
-- 2) Построчная обработка с проверками полей
if not openCursor then
return false;
end if;
loop
fetchCursor;
exit when l_row_ids.count = 0;
l_is_processed := processCursor;
end loop;
close parseCursor;
return l_is_processed;
exception
when others then
rollback to before_mdg_mailing_groups_excel;
importError('Unexpected error during loadExcelData: '||sqlerrm);
return false;
end;
-- -------------------------
procedure updateDictionary is
l_batch_id etl.batchId;
begin
importStage('Loading data to document '||DOCUMENT_NAME);
l_batch_id := etl.startBatch('Update '||DOCUMENT_NAME||' (Excel data)',
etl.BATCH_BLOCK_UPDATE);
etl.lockDocumentBy(DOCUMENT_NAME, l_batch_id);
etl.updateWarehouse(
p_batch_id => l_batch_id,
p_document_name => DOCUMENT_NAME,
p_source_dsn => 'xxmdg.mdg_mailing_groups_excel',
p_update_mode => etl.UPDATE_REPLACE
);
etl.finalizeBatch(l_batch_id);
xxmdg.xxmdg_control_pkg.saveImportBatch(l_batch_id);
commit;
etl.waitBatch(l_batch_id);
importStage('File uploaded (merge)');
end;
-- -------------------------
procedure fullUpdateDictionary is
l_batch_id etl.batchId;
begin
importStage('Loading data to document '||DOCUMENT_NAME||' (replace)');
l_batch_id := etl.startBatch('Full update '||DOCUMENT_NAME||' (Excel data)',
etl.BATCH_BLOCK_UPDATE);
etl.lockDocumentBy(DOCUMENT_NAME, l_batch_id);
etl.updateWarehouse(
p_batch_id => l_batch_id,
p_document_name => DOCUMENT_NAME,
p_source_dsn => 'xxmdg.mdg_mailing_groups_excel',
p_destination_where => '1=1',
p_update_mode => etl.UPDATE_REPLACE
);
etl.finalizeBatch(l_batch_id);
xxmdg.xxmdg_control_pkg.saveImportBatch(l_batch_id);
commit;
etl.waitBatch(l_batch_id);
importStage('File uploaded (replace)');
end;
-- -------------------------
procedure importFromXLSX is
begin
if loadExcelData then
if xxmdg.xxmdg_control_pkg.fullUpdate then
fullUpdateDictionary;
else
updateDictionary;
end if;
end if;
end;
-- -------------------------
end;
/
create or replace package MDG_MAILING_GROUPS_PKG is
DOCUMENT_NAME constant varchar2(100) := 'MDG_MAILING_GROUPS';
procedure enableDebug;
procedure disableDebug;
procedure importFromXLSX;
-- Диагностика (публичные — для юнит-тестов)
function checkGroupName(p_group_name in varchar2) return varchar2;
function checkEmail(p_email in varchar2) return varchar2;
function checkName(p_name in varchar2) return varchar2;
function checkIsInactive(p_is_inactive in varchar2) return varchar2;
function checkDates(p_valid_from in varchar2,
p_valid_to in varchar2) return varchar2;
end;
/