utPLSQL has been supported by Redgate in the form of sponsored stickers and t-shirts. Thank you for helping us spreading the word!
-
-
-
+The list of **authors** and **significant contributors** is available on the [About](https://www.utplsql.org/about/) page. For a full list of contributors, see [contributors](https://github.com/utPLSQL/utPLSQL/graphs/contributors?all=1) on GitHub.
diff --git a/sonar-project.properties b/sonar-project.properties
index ddd794a91..0a835a2f3 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -1,8 +1,9 @@
# must be unique in a given SonarQube instance
-sonar.projectKey=utPLSQL
+sonar.organization=utplsql
+sonar.projectKey=utPLSQL_utPLSQL
# this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1.
sonar.projectName=utPLSQL
-sonar.projectVersion=v3.1.11-develop
+sonar.projectVersion=v3.2.4-develop
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
# Since SonarQube 4.2, this property is optional if sonar.modules is set.
@@ -14,7 +15,7 @@ sonar.coverage.exclusions=**/*.sql,**/*.pks
sonar.tests=./test
sonar.testExecutionReportPaths=./test_results.xml
sonar.links.issue=https://github.com/utPLSQL/utPLSQL/issues
-sonar.links.ci=https://travis-ci.org/utPLSQL/utPLSQL
+sonar.links.ci=https://github.com/utPLSQL/utPLSQL/actions
sonar.links.homepage=https://github.com/utPLSQL/utPLSQL
sonar.projectDescription=PL/SQL Unit Testing Framework
sonar.plsql.file.suffixes=sql,tab,pkb,tpb
@@ -28,9 +29,9 @@ sonar.pullrequest.github.endpoint=https://api.github.com/
sonar.pullrequest.github.repository=utPLSQL/utPLSQL
sonar.plsql.jdbc.driver.class=oracle.jdbc.OracleDriver
-sonar.plsql.jdbc.user=UT3
+sonar.plsql.jdbc.user=UT3_DEVELOP
sonar.plsql.jdbc.password=ut3
-sonar.plsql.defaultSchema=UT3
+sonar.plsql.defaultSchema=UT3_DEVELOP
# Encoding of the source code. Default is default system encoding
diff --git a/source/api/be_between.syn b/source/api/be_between.syn
index c0972595d..233dd65bf 100644
--- a/source/api/be_between.syn
+++ b/source/api/be_between.syn
@@ -1 +1 @@
-create synonym be_between for ut_be_between;
+create noneditionable synonym be_between for ut_be_between;
diff --git a/source/api/be_empty.syn b/source/api/be_empty.syn
index a8e28de6e..f63366fa3 100644
--- a/source/api/be_empty.syn
+++ b/source/api/be_empty.syn
@@ -1 +1 @@
-create synonym be_empty for ut_be_empty;
+create noneditionable synonym be_empty for ut_be_empty;
diff --git a/source/api/be_false.syn b/source/api/be_false.syn
index c2665bbcb..cdf497235 100644
--- a/source/api/be_false.syn
+++ b/source/api/be_false.syn
@@ -1 +1 @@
-create synonym be_false for ut_be_false;
+create noneditionable synonym be_false for ut_be_false;
diff --git a/source/api/be_greater_or_equal.syn b/source/api/be_greater_or_equal.syn
index 386fe7f32..67616dc1d 100644
--- a/source/api/be_greater_or_equal.syn
+++ b/source/api/be_greater_or_equal.syn
@@ -1 +1 @@
-create synonym be_greater_or_equal for ut_be_greater_or_equal;
+create noneditionable synonym be_greater_or_equal for ut_be_greater_or_equal;
diff --git a/source/api/be_greater_than.syn b/source/api/be_greater_than.syn
index 6f97b7d22..c180f3d43 100644
--- a/source/api/be_greater_than.syn
+++ b/source/api/be_greater_than.syn
@@ -1 +1 @@
-create synonym be_greater_than for ut_be_greater_than;
+create noneditionable synonym be_greater_than for ut_be_greater_than;
diff --git a/source/api/be_less_or_equal.syn b/source/api/be_less_or_equal.syn
index b2f836144..0069f52ba 100644
--- a/source/api/be_less_or_equal.syn
+++ b/source/api/be_less_or_equal.syn
@@ -1 +1 @@
-create synonym be_less_or_equal for ut_be_less_or_equal;
+create noneditionable synonym be_less_or_equal for ut_be_less_or_equal;
diff --git a/source/api/be_less_than.syn b/source/api/be_less_than.syn
index fbef3b4c3..5d2766d05 100644
--- a/source/api/be_less_than.syn
+++ b/source/api/be_less_than.syn
@@ -1 +1 @@
-create synonym be_less_than for ut_be_less_than;
+create noneditionable synonym be_less_than for ut_be_less_than;
diff --git a/source/api/be_like.syn b/source/api/be_like.syn
index a8be1de71..6f62f21a0 100644
--- a/source/api/be_like.syn
+++ b/source/api/be_like.syn
@@ -1 +1 @@
-create synonym be_like for ut_be_like;
+create noneditionable synonym be_like for ut_be_like;
diff --git a/source/api/be_not_null.syn b/source/api/be_not_null.syn
index d6458683a..a9e02b94d 100644
--- a/source/api/be_not_null.syn
+++ b/source/api/be_not_null.syn
@@ -1 +1 @@
-create synonym be_not_null for ut_be_not_null;
+create noneditionable synonym be_not_null for ut_be_not_null;
diff --git a/source/api/be_null.syn b/source/api/be_null.syn
index 42f6516c3..4fe3fb9dd 100644
--- a/source/api/be_null.syn
+++ b/source/api/be_null.syn
@@ -1 +1 @@
-create synonym be_null for ut_be_null;
+create noneditionable synonym be_null for ut_be_null;
diff --git a/source/api/be_true.syn b/source/api/be_true.syn
index bf7392a62..1aa348c7e 100644
--- a/source/api/be_true.syn
+++ b/source/api/be_true.syn
@@ -1 +1 @@
-create synonym be_true for ut_be_true;
+create noneditionable synonym be_true for ut_be_true;
diff --git a/source/api/be_within.syn b/source/api/be_within.syn
new file mode 100644
index 000000000..4f4f77606
--- /dev/null
+++ b/source/api/be_within.syn
@@ -0,0 +1 @@
+create noneditionable synonym be_within for ut_be_within;
diff --git a/source/api/be_within_pct.syn b/source/api/be_within_pct.syn
new file mode 100644
index 000000000..b9fc1cd53
--- /dev/null
+++ b/source/api/be_within_pct.syn
@@ -0,0 +1 @@
+create noneditionable synonym be_within_pct for ut_be_within_pct;
diff --git a/source/api/contain.syn b/source/api/contain.syn
index 5bce7d1d2..5b4158635 100644
--- a/source/api/contain.syn
+++ b/source/api/contain.syn
@@ -1 +1 @@
-create synonym contain for ut_contain;
+create noneditionable synonym contain for ut_contain;
diff --git a/source/api/equal.syn b/source/api/equal.syn
index 71ba58b53..0d81fcf6f 100644
--- a/source/api/equal.syn
+++ b/source/api/equal.syn
@@ -1 +1 @@
-create synonym equal for ut_equal;
+create noneditionable synonym equal for ut_equal;
diff --git a/source/api/have_count.syn b/source/api/have_count.syn
index a406d47ec..9cbf8ebf2 100644
--- a/source/api/have_count.syn
+++ b/source/api/have_count.syn
@@ -1 +1 @@
-create synonym have_count for ut_have_count;
+create noneditionable synonym have_count for ut_have_count;
diff --git a/source/api/match.syn b/source/api/match.syn
index ffbda35b1..13e8cc97d 100644
--- a/source/api/match.syn
+++ b/source/api/match.syn
@@ -1 +1 @@
-create synonym match for ut_match;
+create noneditionable synonym match for ut_match;
diff --git a/source/api/ut.pkb b/source/api/ut.pkb
index 3b94a2bf7..0a847cfaa 100644
--- a/source/api/ut.pkb
+++ b/source/api/ut.pkb
@@ -1,8 +1,8 @@
-create or replace package body ut is
+create or replace noneditionable package body ut is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -98,6 +98,11 @@ create or replace package body ut is
return ut_expectation_json(ut_data_value_json(a_actual), a_message);
end;
+ function expect(a_actual in json , a_message varchar2 := null) return ut_expectation_json is
+ begin
+ return ut_expectation_json(ut_data_value_json(a_actual), a_message);
+ end;
+
procedure fail(a_message in varchar2) is
begin
ut_expectation_processor.report_failure(a_message);
@@ -126,7 +131,11 @@ create or replace package body ut is
a_client_character_set varchar2,
a_random_test_order integer,
a_random_test_order_seed positive,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
pragma autonomous_transaction;
begin
@@ -145,7 +154,11 @@ create or replace package body ut is
false,
ut_utils.int_to_boolean(a_random_test_order),
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
rollback;
end;
@@ -162,10 +175,15 @@ create or replace package body ut is
a_client_character_set varchar2,
a_random_test_order integer,
a_random_test_order_seed positive,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
pragma autonomous_transaction;
begin
+
a_reporter := coalesce(a_reporter,ut_documentation_reporter());
ut_runner.run(
a_paths,
@@ -181,7 +199,11 @@ create or replace package body ut is
false,
ut_utils.int_to_boolean(a_random_test_order),
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
rollback;
end;
@@ -200,10 +222,15 @@ create or replace package body ut is
raise_if_packages_invalidated();
raise no_data_found;
end if;
- g_result_lines := ut_utils.clob_to_table(l_clob, ut_utils.gc_max_storage_varchar2_len);
- g_result_line_no := g_result_lines.first;
+ if l_clob is not null and l_clob != empty_clob() then
+ if length(l_clob) > ut_utils.gc_max_storage_varchar2_len then
+ g_result_lines := ut_utils.clob_to_table(l_clob, ut_utils.gc_max_storage_varchar2_len);
+ else
+ g_result_lines := ut_varchar2_list(l_clob);
+ end if;
+ g_result_line_no := g_result_lines.first;
+ end if;
end if;
-
if g_result_line_no is not null then
l_result := g_result_lines(g_result_line_no);
g_result_line_no := g_result_lines.next(g_result_line_no);
@@ -222,7 +249,11 @@ create or replace package body ut is
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined is
l_reporter ut_reporter_base := a_reporter;
l_results sys_refcursor;
@@ -239,10 +270,15 @@ create or replace package body ut is
a_client_character_set,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
if l_reporter is of (ut_output_reporter_base) then
l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor();
+ g_result_lines := ut_varchar2_list();
loop
pipe row( get_report_outputs( l_results ) );
end loop;
@@ -261,7 +297,11 @@ create or replace package body ut is
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined is
l_reporter ut_reporter_base := a_reporter;
l_results sys_refcursor;
@@ -278,10 +318,15 @@ create or replace package body ut is
a_client_character_set,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
if l_reporter is of (ut_output_reporter_base) then
l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor();
+ g_result_lines := ut_varchar2_list();
loop
pipe row( get_report_outputs( l_results ) );
end loop;
@@ -301,7 +346,11 @@ create or replace package body ut is
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined is
l_reporter ut_reporter_base := a_reporter;
l_results sys_refcursor;
@@ -318,10 +367,15 @@ create or replace package body ut is
a_client_character_set,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
if l_reporter is of (ut_output_reporter_base) then
l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor();
+ g_result_lines := ut_varchar2_list();
loop
pipe row( get_report_outputs( l_results ) );
end loop;
@@ -341,7 +395,11 @@ create or replace package body ut is
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined is
l_reporter ut_reporter_base := a_reporter;
l_results sys_refcursor;
@@ -358,10 +416,15 @@ create or replace package body ut is
a_client_character_set,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
if l_reporter is of (ut_output_reporter_base) then
l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor();
+ g_result_lines := ut_varchar2_list();
loop
pipe row( get_report_outputs( l_results ) );
end loop;
@@ -381,7 +444,11 @@ create or replace package body ut is
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined is
l_reporter ut_reporter_base := a_reporter;
l_results sys_refcursor;
@@ -398,10 +465,15 @@ create or replace package body ut is
a_client_character_set,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
if l_reporter is of (ut_output_reporter_base) then
l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor();
+ g_result_lines := ut_varchar2_list();
loop
pipe row( get_report_outputs( l_results ) );
end loop;
@@ -421,7 +493,11 @@ create or replace package body ut is
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined is
l_reporter ut_reporter_base := a_reporter;
l_results sys_refcursor;
@@ -438,10 +514,15 @@ create or replace package body ut is
a_client_character_set,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
if l_reporter is of (ut_output_reporter_base) then
l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor();
+ g_result_lines := ut_varchar2_list();
loop
pipe row( get_report_outputs( l_results ) );
end loop;
@@ -462,7 +543,11 @@ create or replace package body ut is
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
l_reporter ut_reporter_base := a_reporter;
begin
@@ -482,7 +567,11 @@ create or replace package body ut is
a_force_manual_rollback,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
else
run_autonomous(
@@ -497,7 +586,11 @@ create or replace package body ut is
a_client_character_set,
ut_utils.boolean_to_int(a_random_test_order),
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
end if;
if l_reporter is of (ut_output_reporter_base) then
@@ -519,7 +612,11 @@ create or replace package body ut is
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
l_reporter ut_reporter_base := a_reporter;
begin
@@ -536,7 +633,11 @@ create or replace package body ut is
a_force_manual_rollback,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
end;
@@ -552,7 +653,11 @@ create or replace package body ut is
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
begin
ut.run(
@@ -568,7 +673,11 @@ create or replace package body ut is
a_force_manual_rollback,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
end;
@@ -584,7 +693,11 @@ create or replace package body ut is
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
begin
ut.run(
@@ -600,7 +713,11 @@ create or replace package body ut is
a_force_manual_rollback,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
end;
@@ -617,7 +734,11 @@ create or replace package body ut is
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
begin
ut.run(
@@ -633,7 +754,11 @@ create or replace package body ut is
a_force_manual_rollback,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
end;
@@ -650,7 +775,11 @@ create or replace package body ut is
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
begin
ut.run(
@@ -666,7 +795,11 @@ create or replace package body ut is
a_force_manual_rollback,
a_random_test_order,
a_random_test_order_seed,
- a_tags
+ a_tags,
+ a_include_schema_expr,
+ a_include_object_expr,
+ a_exclude_schema_expr,
+ a_exclude_object_expr
);
end;
@@ -674,7 +807,7 @@ create or replace package body ut is
procedure set_nls is
begin
if g_nls_date_format is null then
- select nsp.value
+ select /*+ no_parallel */ nsp.value
into g_nls_date_format
from nls_session_parameters nsp
where parameter = 'NLS_DATE_FORMAT';
diff --git a/source/api/ut.pks b/source/api/ut.pks
index ff75179b0..fdba6b019 100644
--- a/source/api/ut.pks
+++ b/source/api/ut.pks
@@ -1,8 +1,8 @@
-create or replace package ut authid current_user as
+create or replace noneditionable package ut authid current_user as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -47,6 +47,8 @@ create or replace package ut authid current_user as
function expect(a_actual in json_element_t , a_message varchar2 := null) return ut_expectation_json;
+ function expect(a_actual in json , a_message varchar2 := null) return ut_expectation_json;
+
procedure fail(a_message in varchar2);
function run(
@@ -60,7 +62,11 @@ create or replace package ut authid current_user as
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined;
function run(
@@ -74,7 +80,11 @@ create or replace package ut authid current_user as
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined;
function run(
@@ -89,7 +99,11 @@ create or replace package ut authid current_user as
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined;
function run(
@@ -104,7 +118,11 @@ create or replace package ut authid current_user as
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined;
function run(
@@ -119,7 +137,11 @@ create or replace package ut authid current_user as
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined;
function run(
@@ -134,7 +156,11 @@ create or replace package ut authid current_user as
a_client_character_set varchar2 := null,
a_random_test_order integer := 0,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) return ut_varchar2_rows pipelined;
procedure run(
@@ -149,7 +175,11 @@ create or replace package ut authid current_user as
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
);
procedure run(
@@ -164,7 +194,11 @@ create or replace package ut authid current_user as
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
);
procedure run(
@@ -180,7 +214,11 @@ create or replace package ut authid current_user as
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
);
procedure run(
@@ -196,7 +234,11 @@ create or replace package ut authid current_user as
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
);
procedure run(
@@ -212,7 +254,11 @@ create or replace package ut authid current_user as
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
);
procedure run(
@@ -228,7 +274,11 @@ create or replace package ut authid current_user as
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
);
/**
diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb
index bb7d3e458..0db1d6e96 100644
--- a/source/api/ut_runner.pkb
+++ b/source/api/ut_runner.pkb
@@ -1,8 +1,8 @@
-create or replace package body ut_runner is
+create or replace noneditionable package body ut_runner is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -69,13 +69,16 @@ create or replace package body ut_runner is
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
) is
l_run ut_run;
l_coverage_schema_names ut_varchar2_rows;
l_paths ut_varchar2_list;
- l_random_test_order_seed positive;
- l_tags ut_varchar2_rows := ut_varchar2_rows();
+ l_random_test_order_seed positive;
begin
ut_event_manager.initialize();
if a_reporters is not empty then
@@ -114,35 +117,31 @@ create or replace package body ut_runner is
l_coverage_schema_names := ut_suite_manager.get_schema_names(l_paths);
end if;
-
- if a_tags is not null then
- l_tags := l_tags multiset union distinct ut_utils.convert_collection(
- ut_utils.trim_list_elements(ut_utils.filter_list(ut_utils.string_to_table(a_tags,','),ut_utils.gc_word_no_space))
- );
- end if;
-
l_run := ut_run(
a_run_paths => l_paths,
a_coverage_options => ut_coverage_options(
- coverage_run_id => sys_guid(),
+ coverage_run_id => ut_coverage.get_coverage_run_id(),
schema_names => l_coverage_schema_names,
exclude_objects => ut_utils.convert_collection(a_exclude_objects),
include_objects => ut_utils.convert_collection(a_include_objects),
- file_mappings => set(a_source_file_mappings)
+ file_mappings => set(a_source_file_mappings),
+ include_schema_expr => a_include_schema_expr,
+ include_object_expr => a_include_object_expr,
+ exclude_schema_expr => a_exclude_schema_expr,
+ exclude_object_expr => a_exclude_object_expr
),
a_test_file_mappings => set(a_test_file_mappings),
a_client_character_set => a_client_character_set,
a_random_test_order_seed => l_random_test_order_seed,
- a_run_tags => l_tags
+ a_run_tags => a_tags
);
- ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, l_tags);
+ ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, a_tags);
if a_force_manual_rollback then
l_run.set_rollback_type( a_rollback_type => ut_utils.gc_rollback_manual, a_force => true );
end if;
l_run.do_execute();
-
finish_run(l_run, a_force_manual_rollback);
exception
when others then
@@ -166,12 +165,14 @@ create or replace package body ut_runner is
ut_annotation_manager.purge_cache(a_object_owner, a_object_type);
end;
- function get_suites_info(a_owner varchar2 := null, a_package_name varchar2 := null) return ut_suite_items_info pipelined is
+ function get_suites_info(a_owner varchar2, a_package_name varchar2) return ut_suite_items_info pipelined is
l_cursor sys_refcursor;
l_results ut_suite_items_info;
c_bulk_limit constant integer := 100;
+ l_path varchar2(4000) := nvl(a_owner,sys_context('userenv', 'current_schema'))||'.'||nvl(a_package_name,'*');
begin
- l_cursor := ut_suite_manager.get_suites_info( nvl(a_owner,sys_context('userenv', 'current_schema')), a_package_name );
+
+ l_cursor := ut_suite_manager.get_suites_info(ut_varchar2_list(l_path));
loop
fetch l_cursor bulk collect into l_results limit c_bulk_limit;
for i in 1 .. l_results.count loop
@@ -183,6 +184,26 @@ create or replace package body ut_runner is
return;
end;
+ function get_suites_info(a_path varchar2 := null) return ut_suite_items_info pipelined is
+ l_cursor sys_refcursor;
+ l_results ut_suite_items_info;
+ c_bulk_limit constant integer := 100;
+ i pls_integer;
+ begin
+ l_cursor := ut_suite_manager.get_suites_info(ut_varchar2_list(nvl(a_path,sys_context('userenv', 'current_schema'))));
+ loop
+ fetch l_cursor bulk collect into l_results limit c_bulk_limit;
+ i := l_results.first;
+ while (i is not null) loop
+ pipe row (l_results(i));
+ i := l_results.next(i);
+ end loop;
+ exit when l_cursor%notfound;
+ end loop;
+ close l_cursor;
+ return;
+ end;
+
function is_test(a_owner varchar2, a_package_name varchar2, a_procedure_name varchar2) return boolean is
l_result boolean := false;
begin
@@ -237,37 +258,6 @@ create or replace package body ut_runner is
end loop;
end;
- function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2 is
- l_start_pos pls_integer := 1;
- l_end_pos pls_integer := 1;
- l_result varchar2(4000);
- l_item varchar2(4000);
- l_at_end boolean := false;
- begin
- if a_random_seed is null then
- l_result := a_path;
- end if;
- if a_path is not null then
- loop
- l_end_pos := instr(a_path,'.',l_start_pos);
- if l_end_pos = 0 then
- l_end_pos := length(a_path)+1;
- l_at_end := true;
- end if;
- l_item := substr(a_path,l_start_pos,l_end_pos-l_start_pos);
- if l_item is not null then
- l_result :=
- l_result ||
- ut_utils.get_hash( to_char( dbms_utility.get_hash_value( l_item, 1, a_random_seed ) ) );
- end if;
- exit when l_at_end;
- l_result := l_result || chr(0);
- l_start_pos := l_end_pos + 1;
- end loop;
- end if;
- return l_result;
- end;
-
procedure coverage_start(a_coverage_run_id raw) is
begin
ut_coverage.coverage_start(a_coverage_run_id);
diff --git a/source/api/ut_runner.pks b/source/api/ut_runner.pks
index 61578a4e0..17bd0fa7a 100644
--- a/source/api/ut_runner.pks
+++ b/source/api/ut_runner.pks
@@ -1,8 +1,8 @@
-create or replace package ut_runner authid current_user is
+create or replace noneditionable package ut_runner authid current_user is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -71,7 +71,11 @@ create or replace package ut_runner authid current_user is
a_force_manual_rollback boolean := false,
a_random_test_order boolean := false,
a_random_test_order_seed positive := null,
- a_tags varchar2 := null
+ a_tags varchar2 := null,
+ a_include_schema_expr varchar2 := null,
+ a_include_object_expr varchar2 := null,
+ a_exclude_schema_expr varchar2 := null,
+ a_exclude_object_expr varchar2 := null
);
/**
@@ -100,7 +104,14 @@ create or replace package ut_runner authid current_user is
* @param a_package_name name of unit test package to retrieve (optional), if NULL all unit test packages are returned
* @return ut_suite_items_info table of objects
*/
- function get_suites_info(a_owner varchar2 := null, a_package_name varchar2 := null) return ut_suite_items_info pipelined;
+ function get_suites_info(a_owner varchar2, a_package_name varchar2) return ut_suite_items_info pipelined;
+
+ /**
+ * Returns a pipelined collection containing information about unit test suites and the tests contained in them
+ *
+ * @param a_path a path from which we lookg for object or suite
+ */
+ function get_suites_info(a_path varchar2 := null) return ut_suite_items_info pipelined;
/**
@@ -140,11 +151,6 @@ create or replace package ut_runner authid current_user is
*/
function get_reporters_list return tt_reporters_info pipelined;
- /*
- * Returns a hash value of suitepath based on input path and random seed
- */
- function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2;
-
procedure coverage_start(a_coverage_run_id raw);
procedure coverage_stop;
diff --git a/source/api/ut_suite_item_info.tpb b/source/api/ut_suite_item_info.tpb
index 9b142acb0..d18f078ee 100644
--- a/source/api/ut_suite_item_info.tpb
+++ b/source/api/ut_suite_item_info.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_suite_item_info is
+create or replace noneditionable type body ut_suite_item_info is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ create or replace type body ut_suite_item_info is
*/
constructor function ut_suite_item_info(a_object_owner varchar2, a_object_name varchar2, a_item_name varchar2,
a_item_description varchar2, a_item_type varchar2, a_item_line_no integer, a_path varchar2, a_disabled_flag integer,
- a_tags ut_varchar2_rows) return self as result is
+ a_disabled_reason varchar2, a_tags ut_varchar2_rows) return self as result is
begin
self.object_owner := a_object_owner;
self.object_name := a_object_name;
@@ -27,6 +27,10 @@ create or replace type body ut_suite_item_info is
self.item_line_no := a_item_line_no;
self.path := a_path;
self.disabled_flag := a_disabled_flag;
+ self.disabled_reason := case when
+ a_disabled_flag = 1 then a_disabled_reason
+ else null
+ end;
self.tags := case
when a_tags is null then null
when a_tags.count = 0 then null
diff --git a/source/api/ut_suite_item_info.tps b/source/api/ut_suite_item_info.tps
index 807635d1b..4b4f9d52c 100644
--- a/source/api/ut_suite_item_info.tps
+++ b/source/api/ut_suite_item_info.tps
@@ -1,7 +1,7 @@
-create or replace type ut_suite_item_info as object (
+create or replace noneditionable type ut_suite_item_info as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -23,9 +23,10 @@ create or replace type ut_suite_item_info as object (
item_line_no integer, -- line_number where annotation identifying the item exists
path varchar2( 4000 ),-- suitepath of the item
disabled_flag integer, -- 0 (zero) if item is not disabled, 1 if item is disabled by --%disabled annotation
+ disabled_reason varchar2(4000), -- if disable flag is set then you can pass reason
tags varchar2(4000),
constructor function ut_suite_item_info(a_object_owner varchar2, a_object_name varchar2, a_item_name varchar2,
a_item_description varchar2, a_item_type varchar2, a_item_line_no integer, a_path varchar2, a_disabled_flag integer,
- a_tags ut_varchar2_rows) return self as result
+ a_disabled_reason varchar2, a_tags ut_varchar2_rows) return self as result
)
/
diff --git a/source/api/ut_suite_items_info.tps b/source/api/ut_suite_items_info.tps
index 7c9ac33c3..5a04c9a74 100644
--- a/source/api/ut_suite_items_info.tps
+++ b/source/api/ut_suite_items_info.tps
@@ -1,7 +1,7 @@
-create or replace type ut_suite_items_info as
+create or replace noneditionable type ut_suite_items_info as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/check_object_grants.sql b/source/check_object_grants.sql
index d499ced9c..0e7a9b4c9 100644
--- a/source/check_object_grants.sql
+++ b/source/check_object_grants.sql
@@ -1,3 +1,20 @@
+/*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
declare
$if dbms_db_version.version >= 18 $then
c_expected_grants constant dbmsoutput_linesarray := dbmsoutput_linesarray('DBMS_CRYPTO');
@@ -24,7 +41,7 @@ begin
l_target_table := get_view('dba_tab_privs');
l_owner_column := case when l_target_table like 'dba%' then 'owner' else 'table_schema' end;
execute immediate q'[
- select listagg(' - '||object_name,CHR(10)) within group(order by object_name)
+ select /*+ no_parallel */ listagg(' - '||object_name,CHR(10)) within group(order by object_name)
from (
select column_value as object_name
from table(:l_expected_grants)
diff --git a/source/check_sys_grants.sql b/source/check_sys_grants.sql
index 79b657328..f81157183 100644
--- a/source/check_sys_grants.sql
+++ b/source/check_sys_grants.sql
@@ -1,3 +1,20 @@
+/*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
define expected_grants = "&1"
declare
c_expected_grants constant dbmsoutput_linesarray := dbmsoutput_linesarray( &expected_grants );
diff --git a/source/core/annotations/ut_annotated_object.tps b/source/core/annotations/ut_annotated_object.tps
index 06a0f4a36..ca327d7fc 100644
--- a/source/core/annotations/ut_annotated_object.tps
+++ b/source/core/annotations/ut_annotated_object.tps
@@ -1,7 +1,7 @@
-create type ut_annotated_object as object(
+create or replace noneditionable type ut_annotated_object as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/annotations/ut_annotated_objects.tps b/source/core/annotations/ut_annotated_objects.tps
index c7b142a32..2b338e7d9 100644
--- a/source/core/annotations/ut_annotated_objects.tps
+++ b/source/core/annotations/ut_annotated_objects.tps
@@ -1,7 +1,7 @@
-create type ut_annotated_objects as
+create or replace noneditionable type ut_annotated_objects as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/annotations/ut_annotation.tps b/source/core/annotations/ut_annotation.tps
index 982c0348c..cc1f90e86 100644
--- a/source/core/annotations/ut_annotation.tps
+++ b/source/core/annotations/ut_annotation.tps
@@ -1,7 +1,7 @@
-create type ut_annotation as object(
+create or replace noneditionable type ut_annotation as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/annotations/ut_annotation_cache.sql b/source/core/annotations/ut_annotation_cache.sql
index 8baa6e75c..7d69b1db6 100644
--- a/source/core/annotations/ut_annotation_cache.sql
+++ b/source/core/annotations/ut_annotation_cache.sql
@@ -1,7 +1,7 @@
create table ut_annotation_cache (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/annotations/ut_annotation_cache_info.sql b/source/core/annotations/ut_annotation_cache_info.sql
index 7bbf99e00..6248f4fbc 100644
--- a/source/core/annotations/ut_annotation_cache_info.sql
+++ b/source/core/annotations/ut_annotation_cache_info.sql
@@ -1,7 +1,7 @@
create table ut_annotation_cache_info (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/annotations/ut_annotation_cache_manager.pkb b/source/core/annotations/ut_annotation_cache_manager.pkb
index 4882108fc..afc473404 100644
--- a/source/core/annotations/ut_annotation_cache_manager.pkb
+++ b/source/core/annotations/ut_annotation_cache_manager.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_annotation_cache_manager as
+create or replace noneditionable package body ut_annotation_cache_manager as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,20 +21,20 @@ create or replace package body ut_annotation_cache_manager as
l_timestamp timestamp := systimestamp;
pragma autonomous_transaction;
begin
- update ut_annotation_cache_schema s
+ update /*+ no_parallel */ ut_annotation_cache_schema s
set s.max_parse_time = l_timestamp
where s.object_type = a_object.object_type and s.object_owner = a_object.object_owner;
if sql%rowcount = 0 then
- insert into ut_annotation_cache_schema s
+ insert /*+ no_parallel */ into ut_annotation_cache_schema s
(object_owner, object_type, max_parse_time)
values (a_object.object_owner, a_object.object_type, l_timestamp);
end if;
-- if not in trigger, or object has annotations
- if ora_sysevent is null or a_object.annotations is not null and a_object.annotations.count > 0 then
+ if a_object.annotations is not null and a_object.annotations.count > 0 then
- update ut_annotation_cache_info i
+ update /*+ no_parallel */ ut_annotation_cache_info i
set i.parse_time = l_timestamp
where (i.object_owner, i.object_name, i.object_type)
in ((a_object.object_owner, a_object.object_name, a_object.object_type))
@@ -42,21 +42,24 @@ create or replace package body ut_annotation_cache_manager as
if sql%rowcount = 0 then
- insert into ut_annotation_cache_info
+ insert /*+ no_parallel */ into ut_annotation_cache_info
(cache_id, object_owner, object_name, object_type, parse_time)
values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, l_timestamp)
returning cache_id into l_cache_id;
end if;
+
+ delete /*+ no_parallel */ from ut_annotation_cache c where cache_id = l_cache_id;
- end if;
-
- delete from ut_annotation_cache c where cache_id = l_cache_id;
-
- if a_object.annotations is not null and a_object.annotations.count > 0 then
- insert into ut_annotation_cache
+ insert /*+ no_parallel */ into ut_annotation_cache
(cache_id, annotation_position, annotation_name, annotation_text, subobject_name)
- select l_cache_id, a.position, a.name, a.text, a.subobject_name
+ select /*+ no_parallel */ l_cache_id, a.position, a.name, a.text, a.subobject_name
from table(a_object.annotations) a;
+ elsif a_object.annotations is null or a_object.annotations.count = 0 then
+ ut_annotation_cache_manager.remove_from_cache(
+ ut_annotation_objs_cache_info(
+ ut_annotation_obj_cache_info(a_object.object_owner, a_object.object_name, a_object.object_type, 'Y', null)
+ )
+ );
end if;
commit;
end;
@@ -67,9 +70,9 @@ create or replace package body ut_annotation_cache_manager as
pragma autonomous_transaction;
begin
- delete from ut_annotation_cache c
+ delete /*+ no_parallel */ from ut_annotation_cache c
where c.cache_id
- in (select i.cache_id
+ in (select /*+ no_parallel */ i.cache_id
from ut_annotation_cache_info i
join table (a_objects) o
on o.object_name = i.object_name
@@ -78,25 +81,26 @@ create or replace package body ut_annotation_cache_manager as
and o.needs_refresh = 'Y'
);
- update ut_annotation_cache_schema s
+ update /*+ no_parallel */ ut_annotation_cache_schema s
set s.max_parse_time = l_timestamp
where (s.object_owner, s.object_type)
in (
- select o.object_owner, o.object_type
+ select /*+ no_parallel */ o.object_owner, o.object_type
from table(a_objects) o
where o.needs_refresh = 'Y'
);
if sql%rowcount = 0 then
- insert into ut_annotation_cache_schema s
+ insert /*+ no_parallel */ into ut_annotation_cache_schema s
(object_owner, object_type, max_parse_time)
- select distinct o.object_owner, o.object_type, l_timestamp
+ select /*+ no_parallel */ distinct o.object_owner, o.object_type, l_timestamp
from table(a_objects) o
where o.needs_refresh = 'Y';
end if;
- merge into ut_annotation_cache_info i
- using (select o.object_name, o.object_type, o.object_owner
+ merge /*+ no_parallel */
+ into ut_annotation_cache_info i
+ using (select /*+ no_parallel */ o.object_name, o.object_type, o.object_owner
from table(a_objects) o
where o.needs_refresh = 'Y'
) o
@@ -116,7 +120,7 @@ create or replace package body ut_annotation_cache_manager as
function get_cached_objects_list(a_object_owner varchar2, a_object_type varchar2, a_parsed_after timestamp := null) return ut_annotation_objs_cache_info is
l_result ut_annotation_objs_cache_info;
begin
- select ut_annotation_obj_cache_info(
+ select /*+ no_parallel */ ut_annotation_obj_cache_info(
object_owner => i.object_owner,
object_name => i.object_name,
object_type => i.object_type,
@@ -135,7 +139,7 @@ create or replace package body ut_annotation_cache_manager as
l_result t_cache_schema_info;
begin
begin
- select *
+ select /*+ no_parallel */ *
into l_result
from ut_annotation_cache_schema s
where s.object_type = a_object_type and s.object_owner = a_object_owner;
@@ -149,7 +153,7 @@ create or replace package body ut_annotation_cache_manager as
procedure set_fully_refreshed(a_object_owner varchar2, a_object_type varchar2) is
pragma autonomous_transaction;
begin
- update ut_annotation_cache_schema s
+ update /*+ no_parallel */ ut_annotation_cache_schema s
set s.full_refresh_time = s.max_parse_time
where s.object_owner = a_object_owner
and s.object_type = a_object_type;
@@ -160,9 +164,9 @@ create or replace package body ut_annotation_cache_manager as
pragma autonomous_transaction;
begin
- delete from ut_annotation_cache_info i
+ delete /*+ no_parallel */ from ut_annotation_cache_info i
where exists (
- select 1 from table (a_objects) o
+ select /*+ no_parallel */ 1 from table (a_objects) o
where o.object_name = i.object_name
and o.object_type = i.object_type
and o.object_owner = i.object_owner
@@ -175,7 +179,7 @@ create or replace package body ut_annotation_cache_manager as
l_results sys_refcursor;
begin
open l_results for
- select ut_annotated_object(
+ select /*+ no_parallel */ ut_annotated_object(
i.object_owner, i.object_name, i.object_type, i.parse_time,
cast(
collect(
@@ -204,15 +208,15 @@ create or replace package body ut_annotation_cache_manager as
else
l_filter := case when a_object_owner is null then ':a_object_owner is null' else 'object_owner = :a_object_owner' end;
l_filter := l_filter || ' and ' || case when a_object_type is null then ':a_object_type is null' else 'object_type = :a_object_type' end;
- l_cache_filter := ' c.cache_id in (select i.cache_id from ut_annotation_cache_info i where ' || l_filter || ' )';
+ l_cache_filter := ' c.cache_id in (select /*+ no_parallel */ i.cache_id from ut_annotation_cache_info i where ' || l_filter || ' )';
end if;
- execute immediate 'delete from ut_annotation_cache c where ' || l_cache_filter
+ execute immediate 'delete /*+ no_parallel */ from ut_annotation_cache c where ' || l_cache_filter
using a_object_owner, a_object_type;
- execute immediate ' delete from ut_annotation_cache_info i where ' || l_filter
+ execute immediate ' delete /*+ no_parallel */ from ut_annotation_cache_info i where ' || l_filter
using a_object_owner, a_object_type;
- execute immediate ' delete from ut_annotation_cache_schema s where ' || l_filter
+ execute immediate ' delete /*+ no_parallel */ from ut_annotation_cache_schema s where ' || l_filter
using a_object_owner, a_object_type;
commit;
diff --git a/source/core/annotations/ut_annotation_cache_manager.pks b/source/core/annotations/ut_annotation_cache_manager.pks
index e4ba6dd7a..1aa4cb501 100644
--- a/source/core/annotations/ut_annotation_cache_manager.pks
+++ b/source/core/annotations/ut_annotation_cache_manager.pks
@@ -1,7 +1,7 @@
-create or replace package ut_annotation_cache_manager authid definer as
+create or replace noneditionable package ut_annotation_cache_manager authid definer as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/annotations/ut_annotation_cache_schema.sql b/source/core/annotations/ut_annotation_cache_schema.sql
index b8f908e84..b8ebaa40b 100644
--- a/source/core/annotations/ut_annotation_cache_schema.sql
+++ b/source/core/annotations/ut_annotation_cache_schema.sql
@@ -1,7 +1,7 @@
create table ut_annotation_cache_schema (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/annotations/ut_annotation_cache_seq.sql b/source/core/annotations/ut_annotation_cache_seq.sql
index 0a898e98e..f0f1f17d7 100644
--- a/source/core/annotations/ut_annotation_cache_seq.sql
+++ b/source/core/annotations/ut_annotation_cache_seq.sql
@@ -1,7 +1,7 @@
create sequence ut_annotation_cache_seq
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb
index c9de1c7e4..b35f40075 100644
--- a/source/core/annotations/ut_annotation_manager.pkb
+++ b/source/core/annotations/ut_annotation_manager.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_annotation_manager as
+create or replace noneditionable package body ut_annotation_manager as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ create or replace package body ut_annotation_manager as
l_cached_objects := ut_annotation_cache_manager.get_cached_objects_list( a_object_owner, a_object_type );
if l_cached_objects is not empty then
- execute immediate 'select /*+ cardinality(i '||ut_utils.scale_cardinality(cardinality(l_cached_objects))||') */
+ execute immediate 'select /*+ no_parallel cardinality(i '||ut_utils.scale_cardinality(cardinality(l_cached_objects))||') */
value(i)
from table( :l_data ) i
where
@@ -61,17 +61,17 @@ create or replace package body ut_annotation_manager as
l_refresh_needed boolean;
l_objects_view varchar2(200) := ut_metadata.get_objects_view_name();
l_cached_objects ut_annotation_objs_cache_info;
- l_result ut_annotation_objs_cache_info;
+ l_result ut_annotation_objs_cache_info := ut_annotation_objs_cache_info();
begin
- ut_event_manager.trigger_event( 'get_objects_to_refresh - start' );
+ ut_event_manager.trigger_event( 'get_objects_to_refresh - start', ut_key_anyvalues().put('ut_trigger_check.is_alive()',ut_trigger_check.is_alive()) );
l_refresh_needed := ( ut_trigger_check.is_alive() = false ) or a_modified_after is null;
- l_cached_objects := ut_annotation_cache_manager.get_cached_objects_list( a_object_owner, a_object_type, a_modified_after );
if l_refresh_needed then
--limit the list to objects that exist and are visible to the invoking user
--enrich the list by info about cache validity
+ l_cached_objects := ut_annotation_cache_manager.get_cached_objects_list( a_object_owner, a_object_type, a_modified_after );
execute immediate
- 'select /*+ cardinality(i '||ut_utils.scale_cardinality(cardinality(l_cached_objects))||') */
+ 'select /*+ no_parallel cardinality(i '||ut_utils.scale_cardinality(cardinality(l_cached_objects))||') */
'||l_ut_owner||q'[.ut_annotation_obj_cache_info(
object_owner => o.owner,
object_name => o.object_name,
@@ -94,8 +94,6 @@ create or replace package body ut_annotation_manager as
else 'o.last_ddl_time >= cast(:a_modified_after as date)'
end
bulk collect into l_result using l_cached_objects, a_modified_after;
- else
- l_result := l_cached_objects;
end if;
ut_event_manager.trigger_event('get_objects_to_refresh - end (count='||l_result.count||')');
return l_result;
@@ -108,7 +106,7 @@ create or replace package body ut_annotation_manager as
begin
l_card := ut_utils.scale_cardinality(cardinality(a_objects_to_refresh));
open l_result for
- q'[select x.name, x.text
+ q'[select /*+ no_parallel */ x.name, x.text
from (select /*+ cardinality( r ]'||l_card||q'[ )*/
s.name, s.text, s.line,
max(case when s.text like '%--%\%%' escape '\'
@@ -123,6 +121,7 @@ create or replace package body ut_annotation_manager as
and s.type = r.object_type
where s.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'['
and s.type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'['
+ and s.origin_con_id = sys_context('userenv', 'con_id')
) x
where x.is_annotated = 'Y'
order by x.name, x.line]'
@@ -145,34 +144,37 @@ create or replace package body ut_annotation_manager as
l_parse_time date := sysdate;
pragma autonomous_transaction;
begin
- loop
- fetch a_sources_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit;
- for i in 1 .. l_names.count loop
- if l_names(i) != l_name then
- l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type);
- ut_annotation_cache_manager.update_cache(
- ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations)
- );
- l_object_lines.delete;
- end if;
-
- l_name := l_names(i);
- l_object_lines(l_object_lines.count+1) := l_lines(i);
- end loop;
- exit when a_sources_cursor%notfound;
+ begin
+ loop
+ fetch a_sources_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit;
+ for i in 1 .. l_names.count loop
+ if l_names(i) != l_name then
+ l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type);
+ ut_annotation_cache_manager.update_cache(
+ ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations)
+ );
+ l_object_lines.delete;
+ end if;
+
+ l_name := l_names(i);
+ l_object_lines(l_object_lines.count+1) := l_lines(i);
+ end loop;
+ exit when a_sources_cursor%notfound;
- end loop;
- if a_sources_cursor%rowcount > 0 then
- l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type);
- ut_annotation_cache_manager.update_cache(
- ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations)
- );
- l_object_lines.delete;
+ end loop;
+ if a_sources_cursor%rowcount > 0 then
+ l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type);
+ ut_annotation_cache_manager.update_cache(
+ ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations)
+ );
+ l_object_lines.delete;
+ end if;
+ end;
+ if a_sources_cursor%isopen then
+ close a_sources_cursor;
end if;
- close a_sources_cursor;
end;
-
procedure validate_annotation_cache(
a_object_owner varchar2,
a_object_type varchar2,
@@ -233,26 +235,27 @@ create or replace package body ut_annotation_manager as
function get_source_from_sql_text(a_object_name varchar2, a_sql_text ora_name_list_t, a_parts binary_integer) return sys_refcursor is
l_sql_clob clob;
- l_sql_lines ut_varchar2_rows := ut_varchar2_rows();
+ l_sql_lines dbms_preprocessor.source_lines_t := dbms_preprocessor.source_lines_t();
+ l_sql_lines_clob ut_varchar2_list := ut_varchar2_list();
l_result sys_refcursor;
begin
if a_parts > 0 then
for i in 1..a_parts loop
ut_utils.append_to_clob(l_sql_clob, a_sql_text(i));
end loop;
- l_sql_clob := ut_utils.replace_multiline_comments(l_sql_clob);
- -- replace comment lines that contain "-- create or replace"
- l_sql_clob := regexp_replace(l_sql_clob, '^.*[-]{2,}\s*create(\s+or\s+replace).*$', modifier => 'mi');
- -- remove the "create [or replace] [[non]editionable] " so that we have only "type|package" for parsing
- -- needed for dbms_preprocessor
- l_sql_clob := regexp_replace(l_sql_clob, '^(.*?\s*create(\s+or\s+replace)?(\s+(editionable|noneditionable))?\s+?)((package|type).*)', '\5', 1, 1, 'ni');
- -- remove "OWNER." from create or replace statement.
- -- Owner is not supported along with AUTHID - see issue https://github.com/utPLSQL/utPLSQL/issues/1088
- l_sql_clob := regexp_replace(l_sql_clob, '^(package|type)\s+("?[a-zA-Z][a-zA-Z0-9#_$]*"?\.)(.*)', '\1 \3', 1, 1, 'ni');
- l_sql_lines := ut_utils.convert_collection( ut_utils.clob_to_table(l_sql_clob) );
+
+ l_sql_lines_clob := ut_utils.clob_to_table(l_sql_clob);
+ for i in 1..l_sql_lines_clob.count loop
+ l_sql_lines(i) := l_sql_lines_clob(i);
+ end loop;
+
+ -- replace multiline comments that contain "-- create or replace" with single line comment to avoid parsing issues with dbms_preprocessor
+ l_sql_lines := ut_utils.replace_multiline_comments(l_sql_lines);
+ -- strip CREATE header (possibly split across lines) while preserving line numbers
+ l_sql_lines := ut_utils.strip_create_header_lines(l_sql_lines);
end if;
open l_result for
- select a_object_name as name, column_value||chr(10) as text from table(l_sql_lines);
+ select /*+ no_parallel */ a_object_name as name, column_value||chr(10) as text from table(l_sql_lines);
return l_result;
end;
@@ -261,11 +264,12 @@ create or replace package body ut_annotation_manager as
l_sources_view varchar2(200) := ut_metadata.get_source_view_name();
begin
open l_result for
- q'[select :a_object_name, s.text
+ q'[select /*+ no_parallel */ :a_object_name, s.text
from ]'||l_sources_view||q'[ s
where s.type = :a_object_type
and s.owner = :a_object_owner
and s.name = :a_object_name
+ and s.origin_con_id = sys_context('userenv', 'con_id')
order by s.line]'
using a_object_name, a_object_type, a_object_owner, a_object_name;
return l_result;
@@ -279,7 +283,7 @@ create or replace package body ut_annotation_manager as
'GSMCATUSER','GSMUSER','ORACLE_OCM','OUTLN','REMOTE_SCHEDULER_AGENT','SYS','SYS$UMF',
'SYSBACKUP','SYSDG','SYSKM','SYSRAC','SYSTEM','WMSYS','XDB','XS$NULL');
$else
- select username bulk collect into l_restricted_users
+ select /*+ no_parallel */ username bulk collect into l_restricted_users
from all_users where oracle_maintained = 'Y';
$end
if ora_dict_obj_owner member of l_restricted_users then
diff --git a/source/core/annotations/ut_annotation_manager.pks b/source/core/annotations/ut_annotation_manager.pks
index 70257afac..eedef002a 100644
--- a/source/core/annotations/ut_annotation_manager.pks
+++ b/source/core/annotations/ut_annotation_manager.pks
@@ -1,7 +1,7 @@
-create or replace package ut_annotation_manager authid current_user as
+create or replace noneditionable package ut_annotation_manager authid current_user as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/annotations/ut_annotation_obj_cache_info.tps b/source/core/annotations/ut_annotation_obj_cache_info.tps
index 4b8178a9d..5436a11ed 100644
--- a/source/core/annotations/ut_annotation_obj_cache_info.tps
+++ b/source/core/annotations/ut_annotation_obj_cache_info.tps
@@ -1,7 +1,7 @@
-create type ut_annotation_obj_cache_info as object(
+create or replace noneditionable type ut_annotation_obj_cache_info as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/annotations/ut_annotation_objs_cache_info.tps b/source/core/annotations/ut_annotation_objs_cache_info.tps
index bab33a9d3..c29b7d208 100644
--- a/source/core/annotations/ut_annotation_objs_cache_info.tps
+++ b/source/core/annotations/ut_annotation_objs_cache_info.tps
@@ -1,7 +1,7 @@
-create type ut_annotation_objs_cache_info as
+create or replace noneditionable type ut_annotation_objs_cache_info as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb
index 6248f36b9..5680d3cd2 100644
--- a/source/core/annotations/ut_annotation_parser.pkb
+++ b/source/core/annotations/ut_annotation_parser.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_annotation_parser as
+create or replace noneditionable package body ut_annotation_parser as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -22,12 +22,10 @@ create or replace package body ut_annotation_parser as
type tt_comment_list is table of varchar2(32767) index by binary_integer;
gc_annotation_qualifier constant varchar2(1) := '%';
- gc_annot_comment_pattern constant varchar2(30) := '^( |'||chr(09)||')*-- *('||gc_annotation_qualifier||'.*?)$'; -- chr(09) is a tab character
gc_comment_replacer_patter constant varchar2(50) := '{COMMENT#%N%}';
gc_comment_replacer_regex_ptrn constant varchar2(25) := '{COMMENT#(\d+)}';
- gc_regexp_identifier constant varchar2(50) := '[a-zA-Z][a-zA-Z0-9#_$]*';
- gc_annotation_block_pattern constant varchar2(200) := '(({COMMENT#.+}'||chr(10)||')+)( |'||chr(09)||')*(procedure|function)\s+(' ||
- gc_regexp_identifier || ')';
+ gc_regexp_identifier constant varchar2(50) := '[[:alpha:]][[:alnum:]$#_]*';
+
gc_annotation_pattern constant varchar2(50) := gc_annotation_qualifier || gc_regexp_identifier || '[ '||chr(9)||']*(\(.*?\)\s*?$)?';
@@ -72,133 +70,144 @@ create or replace package body ut_annotation_parser as
a_subobject_name varchar2 := null
) is
l_loop_index binary_integer := 1;
- l_annotation_index binary_integer;
+ l_annotation_index binary_integer := 1;
begin
- -- loop while there are unprocessed comment blocks
- while 0 != nvl(regexp_instr(srcstr => a_source
- ,pattern => gc_comment_replacer_regex_ptrn
- ,occurrence => l_loop_index
- ,subexpression => 1)
- ,0) loop
+ -- loop while there are unprocessed comment blocks
+ while l_annotation_index is not null loop
-- define index of the comment block and get it's content from cache
l_annotation_index := regexp_substr( a_source ,gc_comment_replacer_regex_ptrn ,1 ,l_loop_index ,subexpression => 1);
- add_annotation( a_annotations, l_annotation_index, a_comments( l_annotation_index ), a_subobject_name );
- l_loop_index := l_loop_index + 1;
+ if l_annotation_index is not null then
+ add_annotation( a_annotations, l_annotation_index, a_comments( l_annotation_index ), a_subobject_name );
+ l_loop_index := l_loop_index + 1;
+ end if;
end loop;
end add_annotations;
- procedure add_procedure_annotations(a_annotations in out nocopy ut_annotations, a_source clob, a_comments in out nocopy tt_comment_list) is
- l_proc_comments varchar2(32767);
- l_proc_name varchar2(250);
- l_annot_proc_ind number;
- l_annot_proc_block varchar2(32767);
+ procedure add_procedure_annotations(
+ a_annotations in out nocopy ut_annotations,
+ a_source in dbms_preprocessor.source_lines_t,
+ a_comments in out nocopy tt_comment_list
+ ) is
+ l_proc_comments varchar2(32767);
+ l_proc_name varchar2(250);
+ l_line varchar2(32767);
+ l_in_comment_block boolean := false;
+ l_i binary_integer;
begin
- -- loop through procedures and functions of the package and get all the comment blocks just before it's declaration
- l_annot_proc_ind := 1;
- loop
- --find annotated procedure index
- l_annot_proc_ind := regexp_instr(srcstr => a_source
- ,pattern => gc_annotation_block_pattern
- ,occurrence => 1
- ,modifier => 'i'
- ,position => l_annot_proc_ind);
- exit when l_annot_proc_ind = 0;
-
- --get the annotations with procedure name
- l_annot_proc_block := regexp_substr(srcstr => a_source
- ,pattern => gc_annotation_block_pattern
- ,position => l_annot_proc_ind
- ,occurrence => 1
- ,modifier => 'i');
-
- --extract the annotations
- l_proc_comments := trim(regexp_substr(srcstr => l_annot_proc_block
- ,pattern => gc_annotation_block_pattern
- ,modifier => 'i'
- ,subexpression => 1));
- --extract the procedure name
- l_proc_name := trim(regexp_substr(srcstr => l_annot_proc_block
- ,pattern => gc_annotation_block_pattern
- ,modifier => 'i'
- ,subexpression => 5));
-
- -- parse the comment block for the syntactically correct annotations and store them as an array
- add_annotations(a_annotations, l_proc_comments, a_comments, l_proc_name);
-
- l_annot_proc_ind := instr(a_source, ';', l_annot_proc_ind + length(l_annot_proc_block) );
+ l_i := 1;
+ while l_i <= a_source.count loop
+ l_line := a_source(l_i);
+ -- Comment placeholder line: start/continue accumulating a block
+ if instr(l_line, chr(123) ||'COMMENT#') > 0 then
+ if l_in_comment_block then
+ l_proc_comments := l_proc_comments || l_line || chr(10);
+ else
+ l_in_comment_block := true;
+ l_proc_comments := l_line || chr(10);
+ end if;
+ l_i := l_i + 1;
+ -- Whitespace-only line: allowed between comment block and proc decl
+ elsif l_in_comment_block and trim(replace(l_line, chr(9))) is null then
+ l_i := l_i + 1;
+ -- procedure/function declaration following a comment block
+ elsif l_in_comment_block
+ and regexp_like(l_line, '^\s*(procedure|function)\s+', 'i')
+ then
+ -- extract just the identifier name (subexpression 2)
+ l_proc_name := trim(regexp_substr(srcstr =>l_line
+ ,pattern => '^\s*(procedure|function)\s+('||gc_regexp_identifier||')'
+ ,modifier => 'i'
+ ,subexpression => 2));
+
+ -- pass accumulated comment placeholders + proc name to add_annotations
+ add_annotations(a_annotations, l_proc_comments, a_comments, l_proc_name);
+
+ -- reset comment block state
+ l_in_comment_block := false;
+ l_proc_comments := null;
+
+ -- advance past proc header to the terminating ';'
+ -- the header may span multiple lines e.g. with multi-line parameter lists
+ while l_i <= a_source.count loop
+ exit when instr(a_source(l_i), ';') > 0;
+ l_i := l_i + 1;
+ end loop;
+ l_i := l_i + 1; -- step past the ';' line itself
+ -- Any other line: reset comment block accumulator
+ else
+ l_in_comment_block := false;
+ l_proc_comments := null;
+ l_i := l_i + 1;
+ end if;
end loop;
end add_procedure_annotations;
- function extract_and_replace_comments(a_source in out nocopy clob) return tt_comment_list is
- l_comments tt_comment_list;
- l_comment_pos binary_integer;
- l_comment_line binary_integer;
+ function extract_and_replace_comments(
+ a_source in out nocopy dbms_preprocessor.source_lines_t
+ ) return tt_comment_list is
+ l_comments tt_comment_list;
+ l_line varchar2(32767);
+ l_comment_pos binary_integer;
l_comment_replacer varchar2(50);
- l_source clob := a_source;
begin
- l_comment_pos := 1;
- loop
-
- l_comment_pos := regexp_instr(srcstr => a_source
- ,pattern => gc_annot_comment_pattern
- ,occurrence => 1
- ,modifier => 'm'
- ,position => l_comment_pos);
-
- exit when l_comment_pos = 0;
-
- -- position index is shifted by 1 because gc_annot_comment_pattern contains ^ as first sign
- -- but after instr index already points to the char on that line
- l_comment_pos := l_comment_pos-1;
- l_comment_line := length(substr(a_source,1,l_comment_pos))-length(replace(substr(a_source,1,l_comment_pos),chr(10)))+1;
- l_comments(l_comment_line) := trim(regexp_substr(srcstr => a_source
- ,pattern => gc_annot_comment_pattern
- ,occurrence => 1
- ,position => l_comment_pos
- ,modifier => 'm'
- ,subexpression => 2));
-
- l_comment_replacer := replace(gc_comment_replacer_patter, '%N%', l_comment_line);
-
- l_source := regexp_replace(srcstr => a_source
- ,pattern => gc_annot_comment_pattern
- ,replacestr => l_comment_replacer
- ,position => l_comment_pos
- ,occurrence => 1
- ,modifier => 'm');
- dbms_lob.freetemporary(a_source);
- a_source := l_source;
- dbms_lob.freetemporary(l_source);
- l_comment_pos := l_comment_pos + length(l_comment_replacer);
+ for i in 1 .. a_source.count loop
+ l_line := a_source(i);
+
+ -- fast path: skip lines that can't possibly match
+ -- must contain '--' and '%' to be an annotation comment
+ if instr(l_line, '--') = 0 or instr(l_line, gc_annotation_qualifier) = 0 then
+ continue;
+ end if;
+
+ -- find '--' on the line
+ l_comment_pos := instr(l_line, '--');
+
+ -- verify everything before '--' is only spaces/tabs (matches ^ *( |\t)*--)
+ if trim(replace(substr(l_line, 1, l_comment_pos - 1), chr(9))) is not null then
+ continue;
+ end if;
+
+ -- skip '--' and any spaces after it, then check for annotation qualifier '%'
+ l_comment_pos := l_comment_pos + 2;
+ -- skip optional spaces between -- and %
+ while l_comment_pos <= length(l_line)
+ and substr(l_line, l_comment_pos, 1) = ' '
+ loop
+ l_comment_pos := l_comment_pos + 1;
+ end loop;
+
+ -- must start with annotation qualifier at this position
+ if substr(l_line, l_comment_pos, 1) != gc_annotation_qualifier then
+ continue;
+ end if;
+
+ -- extract annotation text (from % to end of line, trimmed)
+ l_comments(i) := trim(substr(l_line, l_comment_pos));
+
+ -- replace line with placeholder, preserving line number in token
+ l_comment_replacer := replace(gc_comment_replacer_patter, '%N%', i);
+ a_source(i) := l_comment_replacer;
end loop;
- ut_utils.debug_log(a_source);
return l_comments;
end extract_and_replace_comments;
------------------------------------------------------------
--public definitions
------------------------------------------------------------
-
- function parse_object_annotations(a_source clob) return ut_annotations is
- l_source clob := a_source;
+ function parse_object_annotations(a_source dbms_preprocessor.source_lines_t) return ut_annotations is
+ l_source dbms_preprocessor.source_lines_t := a_source;
l_comments tt_comment_list;
l_annotations ut_annotations := ut_annotations();
l_result ut_annotations;
l_comment_index positive;
begin
-
- l_source := ut_utils.replace_multiline_comments(l_source);
-
- -- replace all single line comments with {COMMENT#12} element and store it's content for easier processing
- -- this call modifies l_source
+ l_source := ut_utils.replace_multiline_comments(l_source);
l_comments := extract_and_replace_comments(l_source);
-
add_procedure_annotations(l_annotations, l_source, l_comments);
-
delete_processed_comments(l_comments, l_annotations);
--at this point, only the comments not related to procedures are left, so we process them all as top-level
@@ -208,16 +217,15 @@ create or replace package body ut_annotation_parser as
l_comment_index := l_comments.next(l_comment_index);
end loop;
- dbms_lob.freetemporary(l_source);
-
- select value(x) bulk collect into l_result from table(l_annotations) x order by x.position;
+ select /*+ no_parallel */ value(x) bulk collect into l_result from table(l_annotations) x order by x.position asc;
return l_result;
+
end parse_object_annotations;
function parse_object_annotations(a_source_lines dbms_preprocessor.source_lines_t, a_object_type varchar2) return ut_annotations is
l_processed_lines dbms_preprocessor.source_lines_t;
- l_source clob;
+ l_source dbms_preprocessor.source_lines_t;
l_annotations ut_annotations := ut_annotations();
ex_package_is_wrapped exception;
pragma exception_init(ex_package_is_wrapped, -24241);
@@ -233,13 +241,11 @@ create or replace package body ut_annotation_parser as
else
l_processed_lines := sys.dbms_preprocessor.get_post_processed_source(a_source_lines);
end if;
- --convert to clob
for i in 1..l_processed_lines.count loop
- ut_utils.append_to_clob(l_source, replace(l_processed_lines(i), chr(13)||chr(10), chr(10)));
+ l_source(i) := replace(l_processed_lines(i), chr(13)||chr(10), chr(10));
end loop;
--parse annotations
l_annotations := parse_object_annotations(l_source);
- dbms_lob.freetemporary(l_source);
exception
when ex_package_is_wrapped or source_text_is_empty then
null;
diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks
index 0368811db..3e0eaf84c 100644
--- a/source/core/annotations/ut_annotation_parser.pks
+++ b/source/core/annotations/ut_annotation_parser.pks
@@ -1,7 +1,7 @@
-create or replace package ut_annotation_parser authid current_user as
+create or replace noneditionable package ut_annotation_parser authid current_user as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -29,16 +29,5 @@ create or replace package ut_annotation_parser authid current_user as
*/
function parse_object_annotations(a_source_lines dbms_preprocessor.source_lines_t, a_object_type varchar2) return ut_annotations;
-
- /**
- *
- * @private
- * Parses source code and converts it to annotations
- *
- * @param a_source_lines ordered lines of source code to be parsed
- * @return array containing annotations
- */
- function parse_object_annotations(a_source clob) return ut_annotations;
-
end;
/
diff --git a/source/core/annotations/ut_annotations.tps b/source/core/annotations/ut_annotations.tps
index 85be05e72..9e4adc5ae 100644
--- a/source/core/annotations/ut_annotations.tps
+++ b/source/core/annotations/ut_annotations.tps
@@ -1,7 +1,7 @@
-create type ut_annotations
+create or replace noneditionable type ut_annotations
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/annotations/ut_trigger_annotation_parsing.trg b/source/core/annotations/ut_trigger_annotation_parsing.trg
index 437b7742d..75caea5e8 100644
--- a/source/core/annotations/ut_trigger_annotation_parsing.trg
+++ b/source/core/annotations/ut_trigger_annotation_parsing.trg
@@ -2,6 +2,22 @@ create or replace trigger ut_trigger_annotation_parsing
after create or alter or drop
on database
begin
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
if (ora_dict_obj_owner = UPPER('&&UT3_OWNER')
and ora_dict_obj_name = 'UT3_TRIGGER_ALIVE'
and ora_dict_obj_type = 'SYNONYM')
diff --git a/source/core/annotations/ut_trigger_check.pkb b/source/core/annotations/ut_trigger_check.pkb
index 76d3aa11c..463243c5e 100644
--- a/source/core/annotations/ut_trigger_check.pkb
+++ b/source/core/annotations/ut_trigger_check.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_trigger_check is
+create or replace noneditionable package body ut_trigger_check is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ create or replace package body ut_trigger_check is
function is_alive return boolean is
pragma autonomous_transaction;
begin
- execute immediate 'create or replace synonym '||ut_utils.ut_owner||'.'||gc_check_object_name||' for no_object';
+ execute immediate 'create or replace noneditionable synonym '||ut_utils.ut_owner||'.'||gc_check_object_name||' for no_object';
return g_is_trigger_live;
end;
diff --git a/source/core/annotations/ut_trigger_check.pks b/source/core/annotations/ut_trigger_check.pks
index c4c27c596..dae59f3aa 100644
--- a/source/core/annotations/ut_trigger_check.pks
+++ b/source/core/annotations/ut_trigger_check.pks
@@ -1,7 +1,7 @@
-create or replace package ut_trigger_check authid definer is
+create or replace noneditionable package ut_trigger_check authid definer is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage.pkb b/source/core/coverage/ut_coverage.pkb
index 81d17704a..be9b4ad5d 100644
--- a/source/core/coverage/ut_coverage.pkb
+++ b/source/core/coverage/ut_coverage.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_coverage is
+create or replace noneditionable package body ut_coverage is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ create or replace package body ut_coverage is
g_develop_mode boolean not null := false;
g_is_started boolean not null := false;
+ g_coverage_run_id raw(32);
procedure set_develop_mode(a_develop_mode in boolean) is
begin
@@ -35,27 +36,33 @@ create or replace package body ut_coverage is
l_join_mappings varchar2(32767);
l_filters varchar2(32767);
l_mappings_cardinality integer := 0;
+ l_regex_exc_filters varchar2(32767);
begin
l_result := q'[
with
- trigger_source_offsets as (
- select min(s.line) - 1 offset, s.owner, s.name, s.type
- from {sources_view} s
- where s.type = 'TRIGGER'
- {filters}
- and (lower(s.text) like '%begin%' or lower(s.text) like '%declare%' or lower(s.text) like '%compound%')
- group by s.owner, s.name, s.type
- ),
sources as (
select /*+ cardinality(f {mappings_cardinality}) */
{l_full_name} as full_name, s.owner, s.name, s.type,
- s.line - case when s.type = 'TRIGGER' then o.offset else 0 end as line,
+ s.line
+ - case
+ when s.type = 'TRIGGER'
+ then
+ /* calculate offset of line number for trigger source in coverage reporting */
+ min(case when lower(s.text) like '%begin%' or lower(s.text) like '%declare%' or lower(s.text) like '%compound%' then s.line-1 end)
+ over (partition by s.owner, s.type, s.name)
+ else 0
+ end as line,
s.text
from {sources_view} s {join_file_mappings}
- left join trigger_source_offsets o
- on (s.owner = o.owner and s.name = o.name and s.type = o.type)
where s.type in ('PACKAGE BODY', 'TYPE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER')
+ and s.origin_con_id = sys_context('userenv', 'con_id')
{filters}
+ {regex_exc_filters}
+ and not exists (
+ select /*+ cardinality(el {excluded_objects_cardinality})*/ 1
+ from table(:l_excluded_objects) el
+ where s.owner = el.owner and s.name = el.name
+ )
),
coverage_sources as (
select full_name, owner, name, type, line, text,
@@ -77,13 +84,17 @@ create or replace package body ut_coverage is
end as to_be_skipped
from sources s
)
- select full_name, owner, name, type, line, to_be_skipped, text
+ select /*+ no_parallel */ full_name, owner, name, type, line, to_be_skipped, text
from coverage_sources s
-- Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter
- where (s.owner, s.name) not in ( select /*+ cardinality(el {skipped_objects_cardinality})*/el.owner, el.name from table(:l_skipped_objects) el )
+ where not exists (
+ select /*+ cardinality(el {skipped_objects_cardinality})*/ 1
+ from table(:l_skipped_objects) el
+ where s.owner = el.owner and s.name = el.name
+ )
and line > 0
]';
-
+
if a_coverage_options.file_mappings is not empty then
l_mappings_cardinality := ut_utils.scale_cardinality(cardinality(a_coverage_options.file_mappings));
l_full_name := 'f.file_name';
@@ -92,6 +103,18 @@ create or replace package body ut_coverage is
on s.name = f.object_name
and s.type = f.object_type
and s.owner = f.object_owner';
+ elsif coalesce(a_coverage_options.include_schema_expr,a_coverage_options.include_object_expr) is not null then
+ l_full_name := q'[lower(s.type||' '||s.owner||'.'||s.name)]';
+ if a_coverage_options.include_schema_expr is not null then
+ l_filters := q'[and regexp_like(s.owner,:a_include_schema_expr,'i')]';
+ else
+ l_filters := 'and :a_include_schema_expr is null';
+ end if;
+ if a_coverage_options.include_object_expr is not null then
+ l_filters := l_filters|| q'[ and regexp_like(s.name,:a_include_object_expr,'i')]';
+ else
+ l_filters := l_filters|| 'and :a_include_object_expr is null';
+ end if;
else
l_full_name := q'[lower(s.type||' '||s.owner||'.'||s.name)]';
l_filters := case
@@ -109,37 +132,67 @@ create or replace package body ut_coverage is
end;
end if;
+
+ if a_coverage_options.exclude_schema_expr is not null then
+ l_regex_exc_filters := q'[ and not regexp_like(s.owner,:a_exclude_schema_expr,'i')]';
+ else
+ l_regex_exc_filters := ' and :a_exclude_schema_expr is null ';
+ end if;
+
+ if a_coverage_options.exclude_object_expr is not null then
+ l_regex_exc_filters := l_regex_exc_filters||q'[ and not regexp_like(s.name,:a_exclude_obj_expr,'i')]';
+ else
+ l_regex_exc_filters := l_regex_exc_filters||'and :a_exclude_obj_expr is null ';
+ end if;
+
+
+
l_result := replace(l_result, '{sources_view}', ut_metadata.get_source_view_name());
l_result := replace(l_result, '{l_full_name}', l_full_name);
l_result := replace(l_result, '{join_file_mappings}', l_join_mappings);
l_result := replace(l_result, '{filters}', l_filters);
l_result := replace(l_result, '{mappings_cardinality}', l_mappings_cardinality);
l_result := replace(l_result, '{skipped_objects_cardinality}', ut_utils.scale_cardinality(cardinality(a_skip_objects)));
+ l_result := replace(l_result, '{excluded_objects_cardinality}', ut_utils.scale_cardinality(cardinality(coalesce(a_coverage_options.exclude_objects, ut_object_names()))));
+ l_result := replace(l_result, '{regex_exc_filters}', l_regex_exc_filters);
return l_result;
end;
function get_cov_sources_cursor(a_coverage_options in ut_coverage_options) return sys_refcursor is
- l_cursor sys_refcursor;
- l_skip_objects ut_object_names;
- l_sql varchar2(32767);
+ l_cursor sys_refcursor;
+ l_skip_objects ut_object_names;
+ l_excluded_objects ut_object_names;
+ l_sql varchar2(32767);
begin
if not is_develop_mode() then
--skip all the utplsql framework objects and all the unit test packages that could potentially be reported by coverage.
- l_skip_objects := ut_utils.get_utplsql_objects_list() multiset union all coalesce(a_coverage_options.exclude_objects, ut_object_names());
+ l_skip_objects := coalesce( ut_utils.get_utplsql_objects_list() multiset union all ut_suite_manager.get_schema_ut_packages(a_coverage_options.schema_names, a_coverage_options.include_schema_expr) , ut_object_names() );
end if;
+ --Regex exclusion override the standard exclusion objects.
+ if a_coverage_options.exclude_schema_expr is null and a_coverage_options.exclude_object_expr is null then
+ l_excluded_objects := coalesce(a_coverage_options.exclude_objects, ut_object_names());
+ end if;
+
l_sql := get_cov_sources_sql(a_coverage_options, l_skip_objects);
ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('l_sql',l_sql) );
if a_coverage_options.file_mappings is not empty then
- open l_cursor for l_sql using a_coverage_options.file_mappings, l_skip_objects;
+ open l_cursor for l_sql using a_coverage_options.file_mappings, a_coverage_options.exclude_schema_expr,
+ a_coverage_options.exclude_object_expr, l_excluded_objects, l_skip_objects;
+ elsif coalesce(a_coverage_options.include_schema_expr,a_coverage_options.include_object_expr) is not null then
+ open l_cursor for l_sql using a_coverage_options.include_schema_expr, a_coverage_options.include_object_expr,
+ a_coverage_options.exclude_schema_expr, a_coverage_options.exclude_object_expr,
+ l_excluded_objects, l_skip_objects;
elsif a_coverage_options.include_objects is not empty then
- open l_cursor for l_sql using a_coverage_options.include_objects, a_coverage_options.include_objects, l_skip_objects;
+ open l_cursor for l_sql using a_coverage_options.include_objects, a_coverage_options.exclude_schema_expr,
+ a_coverage_options.exclude_object_expr, l_excluded_objects, l_skip_objects;
else
- open l_cursor for l_sql using a_coverage_options.schema_names, a_coverage_options.schema_names, l_skip_objects;
+ open l_cursor for l_sql using a_coverage_options.schema_names, a_coverage_options.exclude_schema_expr,
+ a_coverage_options.exclude_object_expr, l_excluded_objects, l_skip_objects;
end if;
return l_cursor;
end;
@@ -177,6 +230,7 @@ create or replace package body ut_coverage is
l_block_coverage_id integer;
begin
if not is_develop_mode() and not g_is_started then
+ g_coverage_run_id := a_coverage_run_id;
l_line_coverage_id := ut_coverage_helper_profiler.coverage_start( l_run_comment );
l_block_coverage_id := ut_coverage_helper_block.coverage_start( l_run_comment );
g_is_started := true;
@@ -200,9 +254,9 @@ create or replace package body ut_coverage is
begin
if not is_develop_mode() then
g_is_started := false;
+ g_coverage_run_id := null;
ut_coverage_helper_block.coverage_stop();
ut_coverage_helper_profiler.coverage_stop();
- g_is_started := false;
end if;
end;
@@ -260,7 +314,15 @@ create or replace package body ut_coverage is
$end
return l_result_profiler_enrich;
- end get_coverage_data;
+ end get_coverage_data;
+
+ function get_coverage_run_id return raw is
+ begin
+ if g_coverage_run_id is null then
+ g_coverage_run_id := sys_guid();
+ end if;
+ return g_coverage_run_id;
+ end;
end;
/
diff --git a/source/core/coverage/ut_coverage.pks b/source/core/coverage/ut_coverage.pks
index ba5263dd2..b8bc4e169 100644
--- a/source/core/coverage/ut_coverage.pks
+++ b/source/core/coverage/ut_coverage.pks
@@ -1,7 +1,7 @@
-create or replace package ut_coverage authid current_user is
+create or replace noneditionable package ut_coverage authid current_user is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -74,5 +74,7 @@ create or replace package ut_coverage authid current_user is
function get_coverage_data(a_coverage_options ut_coverage_options) return t_coverage;
+ function get_coverage_run_id return raw;
+
end;
/
diff --git a/source/core/coverage/ut_coverage_block.pkb b/source/core/coverage/ut_coverage_block.pkb
index 79fd42699..bfdf13274 100644
--- a/source/core/coverage/ut_coverage_block.pkb
+++ b/source/core/coverage/ut_coverage_block.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_coverage_block is
+create or replace noneditionable package body ut_coverage_block is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage_block.pks b/source/core/coverage/ut_coverage_block.pks
index 08de58e69..9fccb3272 100644
--- a/source/core/coverage/ut_coverage_block.pks
+++ b/source/core/coverage/ut_coverage_block.pks
@@ -1,7 +1,7 @@
-create or replace package ut_coverage_block authid current_user is
+create or replace noneditionable package ut_coverage_block authid current_user is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage_helper.pkb b/source/core/coverage/ut_coverage_helper.pkb
index 4c41aa2f6..ed1bea2ce 100644
--- a/source/core/coverage/ut_coverage_helper.pkb
+++ b/source/core/coverage/ut_coverage_helper.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_coverage_helper is
+create or replace noneditionable package body ut_coverage_helper is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ create or replace package body ut_coverage_helper is
procedure insert_into_tmp_table(a_data t_coverage_sources_tmp_rows) is
begin
forall i in 1 .. a_data.count
- insert into ut_coverage_sources_tmp
+ insert /*+ no_parallel */ into ut_coverage_sources_tmp
(full_name,owner,name,type,line,text,to_be_skipped)
values(a_data(i).full_name,a_data(i).owner,a_data(i).name,a_data(i).type,a_data(i).line,a_data(i).text,a_data(i).to_be_skipped);
end;
@@ -49,7 +49,7 @@ create or replace package body ut_coverage_helper is
function is_tmp_table_populated return boolean is
l_result integer;
begin
- select 1 into l_result from ut_coverage_sources_tmp where rownum = 1;
+ select /*+ no_parallel */ 1 into l_result from ut_coverage_sources_tmp where rownum = 1;
return (l_result = 1);
exception
when no_data_found then
@@ -60,7 +60,7 @@ create or replace package body ut_coverage_helper is
l_result t_tmp_table_objects_crsr;
begin
open l_result for
- select o.owner, o.name, o.type, o.full_name, max(o.line) as lines_count,
+ select /*+ no_parallel */ o.owner, o.name, o.type, o.full_name, max(o.line) as lines_count,
cast(
collect(decode(to_be_skipped, 'Y', to_char(line))) as ut_varchar2_list
) as to_be_skipped_list
@@ -73,7 +73,7 @@ create or replace package body ut_coverage_helper is
function get_tmp_table_object_lines(a_owner varchar2, a_object_name varchar2) return ut_varchar2_list is
l_result ut_varchar2_list;
begin
- select rtrim(s.text,chr(10)) as text
+ select /*+ no_parallel */ rtrim(s.text,chr(10)) as text
bulk collect into l_result
from ut_coverage_sources_tmp s
where s.owner = a_owner
@@ -86,7 +86,7 @@ create or replace package body ut_coverage_helper is
procedure set_coverage_run_ids( a_coverage_run_id raw, a_line_coverage_id integer, a_block_coverage_id integer ) is
pragma autonomous_transaction;
begin
- insert into ut_coverage_runs
+ insert /*+ no_parallel */ into ut_coverage_runs
( coverage_run_id, line_coverage_id, block_coverage_id )
values
( a_coverage_run_id, a_line_coverage_id, a_block_coverage_id );
diff --git a/source/core/coverage/ut_coverage_helper.pks b/source/core/coverage/ut_coverage_helper.pks
index 632b18a0d..11988d935 100644
--- a/source/core/coverage/ut_coverage_helper.pks
+++ b/source/core/coverage/ut_coverage_helper.pks
@@ -1,7 +1,7 @@
-create or replace package ut_coverage_helper authid definer is
+create or replace noneditionable package ut_coverage_helper authid definer is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage_helper_block.pkb b/source/core/coverage/ut_coverage_helper_block.pkb
index e030f2c37..3abbd54fc 100644
--- a/source/core/coverage/ut_coverage_helper_block.pkb
+++ b/source/core/coverage/ut_coverage_helper_block.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_coverage_helper_block is
+create or replace noneditionable package body ut_coverage_helper_block is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -39,6 +39,14 @@ create or replace package body ut_coverage_helper_block is
$else
null;
$end
+ exception
+ when others then
+ if sqlcode = -08402 then
+ dbms_output.put_line('Unable to finish gathering coverage with DBMS_PLSQL_CODE_COVERAGE. ');
+ dbms_output.put_line('Encountered exception `ORA-08402` when calling procedure `DBMS_PLSQL_CODE_COVERAGE.STOP_COVERAGE()`.');
+ dbms_output.put_line('Coverage report will only include line-level code-coverage (without branches).');
+ dbms_output.put_line('Please reference following issue for details on possible causes: https://github.com/utPLSQL/utPLSQL/issues/1097 ');
+ end if;
end;
function block_results(a_object ut_coverage_helper.t_tmp_table_object, a_coverage_run_id raw) return t_block_rows is
@@ -46,7 +54,8 @@ create or replace package body ut_coverage_helper_block is
l_ut_owner varchar2(250) := ut_utils.ut_owner;
begin
execute immediate q'[
- select line as line,
+ select /*+ no_parallel */
+ line as line,
count(block) as blocks,
sum(covered) as covered_blocks
from (select line,
diff --git a/source/core/coverage/ut_coverage_helper_block.pks b/source/core/coverage/ut_coverage_helper_block.pks
index 18adf4fbd..dd8c451d7 100644
--- a/source/core/coverage/ut_coverage_helper_block.pks
+++ b/source/core/coverage/ut_coverage_helper_block.pks
@@ -1,7 +1,7 @@
-create or replace package ut_coverage_helper_block authid current_user is
+create or replace noneditionable package ut_coverage_helper_block authid current_user is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage_helper_profiler.pkb b/source/core/coverage/ut_coverage_helper_profiler.pkb
index dacfcbaa3..f98631593 100644
--- a/source/core/coverage/ut_coverage_helper_profiler.pkb
+++ b/source/core/coverage/ut_coverage_helper_profiler.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_coverage_helper_profiler is
+create or replace noneditionable package body ut_coverage_helper_profiler is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -58,7 +58,7 @@ create or replace package body ut_coverage_helper_profiler is
function proftab_results(a_object ut_coverage_helper.t_tmp_table_object, a_coverage_run_id raw) return t_proftab_rows is
l_coverage_rows t_proftab_rows;
begin
- select
+ select /*+ no_parallel */
d.line#,
case when sum(d.total_occur) = 0 and sum(d.total_time) > 0 then 1 else sum(d.total_occur) end total_occur
bulk collect into l_coverage_rows
diff --git a/source/core/coverage/ut_coverage_helper_profiler.pks b/source/core/coverage/ut_coverage_helper_profiler.pks
index f105d3c9c..ca2a44a64 100644
--- a/source/core/coverage/ut_coverage_helper_profiler.pks
+++ b/source/core/coverage/ut_coverage_helper_profiler.pks
@@ -1,7 +1,7 @@
-create or replace package ut_coverage_helper_profiler authid definer is
+create or replace noneditionable package ut_coverage_helper_profiler authid definer is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage_profiler.pkb b/source/core/coverage/ut_coverage_profiler.pkb
index 6d9f894d5..4e874dabf 100644
--- a/source/core/coverage/ut_coverage_profiler.pkb
+++ b/source/core/coverage/ut_coverage_profiler.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_coverage_profiler is
+create or replace noneditionable package body ut_coverage_profiler is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage_profiler.pks b/source/core/coverage/ut_coverage_profiler.pks
index 59256ae65..788005e15 100644
--- a/source/core/coverage/ut_coverage_profiler.pks
+++ b/source/core/coverage/ut_coverage_profiler.pks
@@ -1,7 +1,7 @@
-create or replace package ut_coverage_profiler authid current_user is
+create or replace noneditionable package ut_coverage_profiler authid current_user is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage_reporter_base.tpb b/source/core/coverage/ut_coverage_reporter_base.tpb
index 96c4b5066..e86af3795 100644
--- a/source/core/coverage/ut_coverage_reporter_base.tpb
+++ b/source/core/coverage/ut_coverage_reporter_base.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_coverage_reporter_base is
+create or replace noneditionable type body ut_coverage_reporter_base is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -92,8 +92,7 @@ create or replace type body ut_coverage_reporter_base is
ut_coverage_helper.cleanup_tmp_table();
(l_reporter as ut_output_reporter_base).before_calling_run(null);
l_reporter.after_calling_run( ut_run( a_coverage_options => a_coverage_options, a_client_character_set => a_client_character_set ) );
- l_reporter.on_finalize(null);
- for i in (select x.text from table(l_reporter.get_lines(1, 1)) x ) loop
+ for i in (select /*+ no_parallel */ x.text from table(l_reporter.get_lines(1, 1)) x ) loop
pipe row (i.text);
end loop;
return;
@@ -106,8 +105,7 @@ create or replace type body ut_coverage_reporter_base is
ut_coverage_helper.cleanup_tmp_table();
(l_reporter as ut_output_reporter_base).before_calling_run(null);
l_reporter.after_calling_run( ut_run( a_coverage_options => a_coverage_options, a_client_character_set => a_client_character_set ) );
- l_reporter.on_finalize(null);
- open l_result for select x.text from table(l_reporter.get_lines(1, 1)) x;
+ open l_result for select /*+ no_parallel */ x.text from table(l_reporter.get_lines(1, 1)) x;
return l_result;
end;
diff --git a/source/core/coverage/ut_coverage_reporter_base.tps b/source/core/coverage/ut_coverage_reporter_base.tps
index cae165258..c4afe389b 100644
--- a/source/core/coverage/ut_coverage_reporter_base.tps
+++ b/source/core/coverage/ut_coverage_reporter_base.tps
@@ -1,7 +1,7 @@
-create or replace type ut_coverage_reporter_base under ut_output_reporter_base(
+create or replace noneditionable type ut_coverage_reporter_base under ut_output_reporter_base(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/coverage/ut_coverage_sources_tmp.sql b/source/core/coverage/ut_coverage_sources_tmp.sql
index abaae5353..35eb05510 100644
--- a/source/core/coverage/ut_coverage_sources_tmp.sql
+++ b/source/core/coverage/ut_coverage_sources_tmp.sql
@@ -1,7 +1,7 @@
create global temporary table ut_coverage_sources_tmp(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/events/ut_event_item.tps b/source/core/events/ut_event_item.tps
index efec940f5..d9e1b84fc 100644
--- a/source/core/events/ut_event_item.tps
+++ b/source/core/events/ut_event_item.tps
@@ -1,7 +1,7 @@
-create or replace type ut_event_item authid current_user as object (
+create or replace noneditionable type ut_event_item authid current_user as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/events/ut_event_listener.tps b/source/core/events/ut_event_listener.tps
index 71ee16a0f..7236fd512 100644
--- a/source/core/events/ut_event_listener.tps
+++ b/source/core/events/ut_event_listener.tps
@@ -1,7 +1,7 @@
-create or replace type ut_event_listener authid current_user as object (
+create or replace noneditionable type ut_event_listener authid current_user as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/events/ut_event_manager.pkb b/source/core/events/ut_event_manager.pkb
index 039eb43a0..50c500d13 100644
--- a/source/core/events/ut_event_manager.pkb
+++ b/source/core/events/ut_event_manager.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_event_manager as
+create or replace noneditionable package body ut_event_manager as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/events/ut_event_manager.pks b/source/core/events/ut_event_manager.pks
index f67bd0f4d..08db7f5da 100644
--- a/source/core/events/ut_event_manager.pks
+++ b/source/core/events/ut_event_manager.pks
@@ -1,7 +1,7 @@
-create or replace package ut_event_manager authid current_user as
+create or replace noneditionable package ut_event_manager authid current_user as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/output_buffers/ut_output_buffer_base.tpb b/source/core/output_buffers/ut_output_buffer_base.tpb
index 38f008e75..8b9c236ef 100644
--- a/source/core/output_buffers/ut_output_buffer_base.tpb
+++ b/source/core/output_buffers/ut_output_buffer_base.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_output_buffer_base is
+create or replace noneditionable type body ut_output_buffer_base is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -18,33 +18,110 @@ create or replace type body ut_output_buffer_base is
member procedure init(self in out nocopy ut_output_buffer_base, a_output_id raw := null, a_self_type varchar2 := null) is
pragma autonomous_transaction;
- l_exists int;
begin
cleanup_buffer();
self.self_type := coalesce(a_self_type,self.self_type);
self.output_id := coalesce(a_output_id, self.output_id, sys_guid());
self.start_date := coalesce(self.start_date, sysdate);
- self.last_message_id := 0;
- select count(*) into l_exists from ut_output_buffer_info_tmp where output_id = self.output_id;
- if ( l_exists > 0 ) then
- update ut_output_buffer_info_tmp set start_date = self.start_date where output_id = self.output_id;
- else
- insert into ut_output_buffer_info_tmp(output_id, start_date) values (self.output_id, self.start_date);
- end if;
+ merge /*+ no_parallel */ into ut_output_buffer_info_tmp dst
+ using (select 1 from dual) src
+ on (dst.output_id = self.output_id)
+ when matched then
+ update
+ set dst.start_date = self.start_date
+ when not matched then
+ insert (output_id, start_date)
+ values (self.output_id, self.start_date);
commit;
+ dbms_lock.allocate_unique( self.output_id, self.lock_handle);
self.is_closed := 0;
end;
- member function get_lines_cursor(a_initial_timeout natural := null, a_timeout_sec natural := null) return sys_refcursor is
+ member procedure lock_buffer(a_timeout_sec number := null) is
+ l_status integer;
+ begin
+ l_status := dbms_lock.request( self.lock_handle, dbms_lock.x_mode, 5, false );
+ if l_status != 0 then
+ raise_application_error(-20000, 'Cannot allocate lock for output buffer of reporter. lock request status = '||l_status||', lock handle = '||self.lock_handle||', self.output_id ='||self.output_id);
+ end if;
+ end;
+
+ member procedure close(self in out nocopy ut_output_buffer_base) is
+ l_status integer;
+ begin
+ l_status := dbms_lock.release( self.lock_handle );
+ if l_status != 0 then
+ raise_application_error(-20000, 'Cannot release lock for output buffer of reporter. Lock_handle = '||self.lock_handle||' status = '||l_status);
+ end if;
+ self.is_closed := 1;
+ end;
+
+
+ member procedure remove_buffer_info(self in ut_output_buffer_base) is
+ pragma autonomous_transaction;
+ begin
+ delete from ut_output_buffer_info_tmp a
+ where a.output_id = self.output_id;
+ commit;
+ end;
+
+ member function timeout_producer_not_started( a_producer_started boolean, a_already_waited_sec number, a_init_wait_sec number ) return boolean
+ is
+ l_result boolean := false;
+ begin
+ if not a_producer_started and a_already_waited_sec >= a_init_wait_sec then
+ if a_init_wait_sec > 0 then
+ self.remove_buffer_info();
+ raise_application_error(
+ ut_utils.gc_out_buffer_timeout,
+ 'Timeout occurred while waiting for report data producer to start. Waited for: '||ut_utils.to_string( a_already_waited_sec )||' seconds.'
+ );
+ else
+ l_result := true;
+ end if;
+ end if;
+ return l_result;
+ end;
+
+ member function timeout_producer_not_finished(a_producer_finished boolean, a_already_waited_sec number, a_timeout_sec number) return boolean
+ is
+ l_result boolean := false;
+ begin
+ if not a_producer_finished and a_timeout_sec is not null and a_already_waited_sec >= a_timeout_sec then
+ if a_timeout_sec > 0 then
+ self.remove_buffer_info();
+ raise_application_error(
+ ut_utils.gc_out_buffer_timeout,
+ 'Timeout occurred while waiting for more data from producer. Waited for: '||ut_utils.to_string( a_already_waited_sec )||' seconds.'
+ );
+ else
+ l_result := true;
+ end if;
+ end if;
+ return l_result;
+ end;
+
+ member function get_lock_status return integer is
+ l_result integer;
+ l_release_status integer;
+ begin
+ l_result := dbms_lock.request( self.lock_handle, dbms_lock.s_mode, 0, false );
+ if l_result = 0 then
+ l_release_status := dbms_lock.release( self.lock_handle );
+ end if;
+ return l_result;
+ end;
+
+ member function get_lines_cursor(a_initial_timeout number := null, a_timeout_sec number := null) return sys_refcursor is
l_lines sys_refcursor;
begin
open l_lines for
- select text, item_type
+ select /*+ no_parallel */ text, item_type
from table(self.get_lines(a_initial_timeout, a_timeout_sec));
return l_lines;
end;
- member procedure lines_to_dbms_output(self in ut_output_buffer_base, a_initial_timeout natural := null, a_timeout_sec natural := null) is
+ member procedure lines_to_dbms_output(self in ut_output_buffer_base, a_initial_timeout number := null, a_timeout_sec number := null) is
l_data sys_refcursor;
l_clob clob;
l_item_type varchar2(32767);
@@ -54,16 +131,20 @@ create or replace type body ut_output_buffer_base is
loop
fetch l_data into l_clob, l_item_type;
exit when l_data%notfound;
- l_lines := ut_utils.clob_to_table(l_clob);
- for i in 1 .. l_lines.count loop
- dbms_output.put_line(l_lines(i));
- end loop;
+ if dbms_lob.getlength(l_clob) > 32767 then
+ l_lines := ut_utils.clob_to_table(l_clob);
+ for i in 1 .. l_lines.count loop
+ dbms_output.put_line(l_lines(i));
+ end loop;
+ else
+ dbms_output.put_line(l_clob);
+ end if;
end loop;
close l_data;
end;
member procedure cleanup_buffer(self in ut_output_buffer_base, a_retention_time_sec natural := null) is
- gc_buffer_retention_sec constant naturaln := coalesce(a_retention_time_sec, 60 * 60 * 24); -- 24 hours
+ gc_buffer_retention_sec constant naturaln := coalesce(a_retention_time_sec, 60 * 60 * 24 * 5); -- 5 days
l_retention_days number := gc_buffer_retention_sec / (60 * 60 * 24);
l_max_retention_date date := sysdate - l_retention_days;
pragma autonomous_transaction;
diff --git a/source/core/output_buffers/ut_output_buffer_base.tps b/source/core/output_buffers/ut_output_buffer_base.tps
index 745692f38..da2bc9143 100644
--- a/source/core/output_buffers/ut_output_buffer_base.tps
+++ b/source/core/output_buffers/ut_output_buffer_base.tps
@@ -1,7 +1,7 @@
-create or replace type ut_output_buffer_base force authid definer as object(
+create or replace noneditionable type ut_output_buffer_base force authid definer as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -19,16 +19,21 @@ create or replace type ut_output_buffer_base force authid definer as object(
output_id raw(32),
is_closed number(1,0),
start_date date,
- last_message_id number(38,0),
+ lock_handle varchar2(30 byte),
self_type varchar2(250 byte),
member procedure init(self in out nocopy ut_output_buffer_base, a_output_id raw := null, a_self_type varchar2 := null),
- member function get_lines_cursor(a_initial_timeout natural := null, a_timeout_sec natural := null) return sys_refcursor,
- member procedure lines_to_dbms_output(self in ut_output_buffer_base, a_initial_timeout natural := null, a_timeout_sec natural := null),
+ member procedure lock_buffer(a_timeout_sec number := null),
+ member function timeout_producer_not_started( a_producer_started boolean, a_already_waited_sec number, a_init_wait_sec number ) return boolean,
+ member function timeout_producer_not_finished(a_producer_finished boolean, a_already_waited_sec number, a_timeout_sec number) return boolean,
+ member function get_lock_status return integer,
+ member function get_lines_cursor(a_initial_timeout number := null, a_timeout_sec number := null) return sys_refcursor,
+ member procedure lines_to_dbms_output(self in ut_output_buffer_base, a_initial_timeout number := null, a_timeout_sec number := null),
member procedure cleanup_buffer(self in ut_output_buffer_base, a_retention_time_sec natural := null),
- not instantiable member procedure close(self in out nocopy ut_output_buffer_base),
+ member procedure remove_buffer_info(self in ut_output_buffer_base),
+ member procedure close(self in out nocopy ut_output_buffer_base),
not instantiable member procedure send_line(self in out nocopy ut_output_buffer_base, a_text varchar2, a_item_type varchar2 := null),
not instantiable member procedure send_lines(self in out nocopy ut_output_buffer_base, a_text_list ut_varchar2_rows, a_item_type varchar2 := null),
not instantiable member procedure send_clob(self in out nocopy ut_output_buffer_base, a_text clob, a_item_type varchar2 := null),
- not instantiable member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined
+ not instantiable member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined
) not final not instantiable
/
diff --git a/source/core/output_buffers/ut_output_buffer_info_tmp.sql b/source/core/output_buffers/ut_output_buffer_info_tmp.sql
index ec09619d2..2aba5a55c 100644
--- a/source/core/output_buffers/ut_output_buffer_info_tmp.sql
+++ b/source/core/output_buffers/ut_output_buffer_info_tmp.sql
@@ -1,7 +1,7 @@
create table ut_output_buffer_info_tmp(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/output_buffers/ut_output_buffer_tmp.sql b/source/core/output_buffers/ut_output_buffer_tmp.sql
index 49fefa24d..68b738d3f 100644
--- a/source/core/output_buffers/ut_output_buffer_tmp.sql
+++ b/source/core/output_buffers/ut_output_buffer_tmp.sql
@@ -1,7 +1,7 @@
create table ut_output_buffer_tmp(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -30,3 +30,4 @@ create table ut_output_buffer_tmp(
) organization index nologging initrans 100
overflow nologging initrans 100;
+create sequence ut_output_buffer_tmp_seq cache 20;
diff --git a/source/core/output_buffers/ut_output_bulk_buffer.tpb b/source/core/output_buffers/ut_output_bulk_buffer.tpb
new file mode 100644
index 000000000..4f6698eaf
--- /dev/null
+++ b/source/core/output_buffers/ut_output_bulk_buffer.tpb
@@ -0,0 +1,160 @@
+create or replace noneditionable type body ut_output_bulk_buffer is
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ constructor function ut_output_bulk_buffer(self in out nocopy ut_output_bulk_buffer, a_output_id raw := null) return self as result is
+ begin
+ self.init(a_output_id, $$plsql_unit);
+ return;
+ end;
+
+ overriding member procedure send_line(self in out nocopy ut_output_bulk_buffer, a_text varchar2, a_item_type varchar2 := null) is
+ pragma autonomous_transaction;
+ begin
+ if a_text is not null or a_item_type is not null then
+ if length(a_text) > ut_utils.gc_max_storage_varchar2_len then
+ self.send_lines(
+ ut_utils.convert_collection(
+ ut_utils.clob_to_table(a_text, ut_utils.gc_max_storage_varchar2_len)
+ ),
+ a_item_type
+ );
+ else
+ insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type)
+ values (self.output_id, ut_output_buffer_tmp_seq.nextval, a_text, a_item_type);
+ end if;
+ commit;
+ end if;
+ end;
+
+ overriding member procedure send_lines(self in out nocopy ut_output_bulk_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null) is
+ pragma autonomous_transaction;
+ begin
+ insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type)
+ select /*+ no_parallel */ self.output_id, ut_output_buffer_tmp_seq.nextval, t.column_value, a_item_type
+ from table(a_text_list) t
+ where t.column_value is not null or a_item_type is not null;
+ commit;
+ end;
+
+ overriding member procedure send_clob(self in out nocopy ut_output_bulk_buffer, a_text clob, a_item_type varchar2 := null) is
+ pragma autonomous_transaction;
+ begin
+ if a_text is not null and a_text != empty_clob() or a_item_type is not null then
+ if length(a_text) > ut_utils.gc_max_storage_varchar2_len then
+ self.send_lines(
+ ut_utils.convert_collection(
+ ut_utils.clob_to_table(a_text, ut_utils.gc_max_storage_varchar2_len)
+ ),
+ a_item_type
+ );
+ else
+ insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type)
+ values (self.output_id, ut_output_buffer_tmp_seq.nextval, a_text, a_item_type);
+ end if;
+ commit;
+ end if;
+ end;
+
+ overriding member procedure lines_to_dbms_output(self in ut_output_bulk_buffer, a_initial_timeout number := null, a_timeout_sec number := null) is
+ l_data sys_refcursor;
+ l_text ut_varchar2_rows;
+ l_item_type ut_varchar2_rows;
+ begin
+ l_data := self.get_lines_cursor(a_initial_timeout, a_timeout_sec);
+ loop
+ fetch l_data bulk collect into l_text, l_item_type limit 10000;
+ for idx in 1 .. l_text.count loop
+ dbms_output.put_line(l_text(idx));
+ end loop;
+ exit when l_data%notfound;
+ end loop;
+ close l_data;
+ self.remove_buffer_info();
+ end;
+
+ overriding member function get_lines_cursor(a_initial_timeout number := null, a_timeout_sec number := null) return sys_refcursor is
+ lc_init_wait_sec constant number := coalesce(a_initial_timeout, 10 );
+ l_already_waited_sec number(10,2) := 0;
+ l_sleep_time number(2,1);
+ l_exists integer;
+ l_finished boolean := false;
+ l_data_produced boolean := false;
+ l_producer_active boolean := false;
+ l_producer_started boolean := false;
+ l_producer_finished boolean := false;
+ l_results sys_refcursor;
+ begin
+
+ while not l_finished loop
+
+ if not l_data_produced then
+ select /*+ no_parallel */ count(1) into l_exists
+ from ut_output_buffer_tmp o
+ where o.output_id = self.output_id and rownum = 1;
+ l_data_produced := (l_exists = 1);
+ end if;
+
+ l_sleep_time := case when l_already_waited_sec >= 1 then 0.5 else 0.1 end;
+ l_producer_active := (self.get_lock_status() <> 0);
+ l_producer_started := (l_producer_active or l_data_produced ) or l_producer_started;
+ l_producer_finished := (l_producer_started and not l_producer_active) or l_producer_finished;
+ l_finished :=
+ self.timeout_producer_not_finished(l_producer_finished, l_already_waited_sec, a_timeout_sec)
+ or self.timeout_producer_not_started(l_producer_started, l_already_waited_sec, lc_init_wait_sec)
+ or l_producer_finished;
+
+ dbms_lock.sleep(l_sleep_time);
+ l_already_waited_sec := l_already_waited_sec + l_sleep_time;
+ end loop;
+
+ open l_results for
+ select /*+ no_parallel */ o.text, o.item_type
+ from ut_output_buffer_tmp o
+ where o.output_id = self.output_id
+ and o.text is not null
+ order by o.output_id, o.message_id;
+
+ return l_results;
+
+ end;
+
+ /* Important note.
+ This function code is almost duplicated between two types for performance reasons.
+ The pipe row clause is much faster on VARCHAR2 then it is on clob.
+ That is the key reason for two implementations.
+ */
+ overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined is
+ l_data sys_refcursor;
+ l_text ut_varchar2_rows;
+ l_item_type ut_varchar2_rows;
+ begin
+ l_data := self.get_lines_cursor(a_initial_timeout, a_timeout_sec);
+ loop
+ fetch l_data bulk collect into l_text, l_item_type limit 10000;
+ for idx in 1 .. l_text.count loop
+ pipe row( ut_output_data_row(l_text(idx), l_item_type(idx)) );
+ end loop;
+ exit when l_data%notfound;
+ end loop;
+ close l_data;
+ self.remove_buffer_info();
+ return;
+ end;
+
+end;
+/
diff --git a/source/core/output_buffers/ut_output_bulk_buffer.tps b/source/core/output_buffers/ut_output_bulk_buffer.tps
new file mode 100644
index 000000000..538bc12a5
--- /dev/null
+++ b/source/core/output_buffers/ut_output_bulk_buffer.tps
@@ -0,0 +1,27 @@
+create or replace noneditionable type ut_output_bulk_buffer under ut_output_buffer_base (
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ constructor function ut_output_bulk_buffer(self in out nocopy ut_output_bulk_buffer, a_output_id raw := null) return self as result,
+ overriding member procedure send_line(self in out nocopy ut_output_bulk_buffer, a_text varchar2, a_item_type varchar2 := null),
+ overriding member procedure send_lines(self in out nocopy ut_output_bulk_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null),
+ overriding member procedure send_clob(self in out nocopy ut_output_bulk_buffer, a_text clob, a_item_type varchar2 := null),
+ overriding member procedure lines_to_dbms_output(self in ut_output_bulk_buffer, a_initial_timeout number := null, a_timeout_sec number := null),
+ overriding member function get_lines_cursor(a_initial_timeout number := null, a_timeout_sec number := null) return sys_refcursor,
+ overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined
+) not final
+/
diff --git a/source/core/output_buffers/ut_output_clob_buffer_tmp.sql b/source/core/output_buffers/ut_output_clob_buffer_tmp.sql
index 48cfe6fde..fa61191ce 100644
--- a/source/core/output_buffers/ut_output_clob_buffer_tmp.sql
+++ b/source/core/output_buffers/ut_output_clob_buffer_tmp.sql
@@ -6,7 +6,7 @@ begin
v_table_sql := 'create table ut_output_clob_buffer_tmp(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -45,3 +45,5 @@ begin
end;
end;
/
+
+create sequence ut_output_clob_buffer_tmp_seq cache 20;
diff --git a/source/core/output_buffers/ut_output_clob_table_buffer.tpb b/source/core/output_buffers/ut_output_clob_table_buffer.tpb
index 8c2825151..f6d7c9a66 100644
--- a/source/core/output_buffers/ut_output_clob_table_buffer.tpb
+++ b/source/core/output_buffers/ut_output_clob_table_buffer.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_output_clob_table_buffer is
+create or replace noneditionable type body ut_output_clob_table_buffer is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -22,23 +22,12 @@ create or replace type body ut_output_clob_table_buffer is
return;
end;
- overriding member procedure close(self in out nocopy ut_output_clob_table_buffer) is
- pragma autonomous_transaction;
- begin
- self.last_message_id := self.last_message_id + 1;
- insert into ut_output_clob_buffer_tmp(output_id, message_id, is_finished)
- values (self.output_id, self.last_message_id, 1);
- commit;
- self.is_closed := 1;
- end;
-
overriding member procedure send_line(self in out nocopy ut_output_clob_table_buffer, a_text varchar2, a_item_type varchar2 := null) is
pragma autonomous_transaction;
begin
if a_text is not null or a_item_type is not null then
- self.last_message_id := self.last_message_id + 1;
- insert into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type)
- values (self.output_id, self.last_message_id, a_text, a_item_type);
+ insert /*+ no_parallel */ into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type)
+ values (self.output_id, ut_output_clob_buffer_tmp_seq.nextval, a_text, a_item_type);
end if;
commit;
end;
@@ -46,11 +35,10 @@ create or replace type body ut_output_clob_table_buffer is
overriding member procedure send_lines(self in out nocopy ut_output_clob_table_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null) is
pragma autonomous_transaction;
begin
- insert into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type)
- select self.output_id, self.last_message_id + rownum, t.column_value, a_item_type
+ insert /*+ no_parallel */ into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type)
+ select /*+ no_parallel */ self.output_id, ut_output_clob_buffer_tmp_seq.nextval, t.column_value, a_item_type
from table(a_text_list) t
where t.column_value is not null or a_item_type is not null;
- self.last_message_id := self.last_message_id + SQL%rowcount;
commit;
end;
@@ -58,98 +46,87 @@ create or replace type body ut_output_clob_table_buffer is
pragma autonomous_transaction;
begin
if a_text is not null and a_text != empty_clob() or a_item_type is not null then
- self.last_message_id := self.last_message_id + 1;
- insert into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type)
- values (self.output_id, self.last_message_id, a_text, a_item_type);
+ insert /*+ no_parallel */ into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type)
+ values (self.output_id, ut_output_clob_buffer_tmp_seq.nextval, a_text, a_item_type);
end if;
commit;
end;
- overriding member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined is
- type t_rowid_tab is table of urowid;
- l_message_rowids t_rowid_tab;
- l_buffer_data ut_output_data_rows;
- l_finished_flags ut_integer_list;
- l_already_waited_for number(10,2) := 0;
- l_finished boolean := false;
- lc_init_wait_sec constant naturaln := coalesce(a_initial_timeout, 60 ); -- 1 minute
- lc_max_wait_sec constant naturaln := coalesce(a_timeout_sec, 60 * 60 * 4); -- 4 hours
- l_wait_for integer := lc_init_wait_sec;
- lc_short_sleep_time constant number(1,1) := 0.1; --sleep for 100 ms between checks
- lc_long_sleep_time constant number(1) := 1; --sleep for 1 s when waiting long
- lc_long_wait_time constant number(1) := 1; --waiting more than 1 sec
- l_sleep_time number(2,1) := lc_short_sleep_time;
- lc_bulk_limit constant integer := 5000;
- l_max_message_id integer := lc_bulk_limit;
-
- procedure remove_read_data(a_message_rowids t_rowid_tab) is
- pragma autonomous_transaction;
+ overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined is
+ lc_init_wait_sec constant number := coalesce(a_initial_timeout, 10 );
+ l_buffer_rowids ut_varchar2_rows;
+ l_buffer_data ut_output_data_rows;
+ l_finished_flags ut_integer_list;
+ l_already_waited_sec number(10,2) := 0;
+ l_finished boolean := false;
+ l_sleep_time number(2,1);
+ l_lock_status integer;
+ l_producer_started boolean := false;
+ l_producer_finished boolean := false;
+ procedure get_data_from_buffer_table(
+ a_buffer_data out nocopy ut_output_data_rows,
+ a_buffer_rowids out nocopy ut_varchar2_rows,
+ a_finished_flags out nocopy ut_integer_list
+ ) is
+ lc_bulk_limit constant integer := 5000;
begin
- forall i in 1 .. a_message_rowids.count
- delete from ut_output_clob_buffer_tmp a
- where rowid = a_message_rowids(i);
- commit;
+ with ordered_buffer as (
+ select /*+ no_parallel index(a) */ ut_output_data_row(a.text, a.item_type), rowidtochar(a.rowid), is_finished
+ from ut_output_clob_buffer_tmp a
+ where a.output_id = self.output_id
+ and a.message_id <= (select min(message_id) from ut_output_clob_buffer_tmp o where o.output_id = self.output_id) + lc_bulk_limit
+ order by a.message_id
+ )
+ select /*+ no_parallel */ b.*
+ bulk collect into a_buffer_data, a_buffer_rowids, a_finished_flags
+ from ordered_buffer b;
end;
- procedure remove_buffer_info is
- pragma autonomous_transaction;
+ procedure remove_read_data(a_buffer_rowids ut_varchar2_rows) is
+ pragma autonomous_transaction;
begin
- delete from ut_output_buffer_info_tmp a
- where a.output_id = self.output_id;
+ forall i in 1 .. a_buffer_rowids.count
+ delete from ut_output_clob_buffer_tmp a
+ where rowid = chartorowid(a_buffer_rowids(i));
commit;
end;
- begin
+ begin
while not l_finished loop
- with ordered_buffer as (
- select /*+ index(a) */ a.rowid, ut_output_data_row(a.text, a.item_type), is_finished
- from ut_output_clob_buffer_tmp a
- where a.output_id = self.output_id
- and a.message_id <= l_max_message_id
- order by a.message_id
- )
- select b.*
- bulk collect into l_message_rowids, l_buffer_data, l_finished_flags
- from ordered_buffer b;
- --nothing fetched from output, wait and try again
- if l_buffer_data.count = 0 then
- $if dbms_db_version.version >= 18 $then
- dbms_session.sleep(l_sleep_time);
- $else
- dbms_lock.sleep(l_sleep_time);
- $end
- l_already_waited_for := l_already_waited_for + l_sleep_time;
- if l_already_waited_for > lc_long_wait_time then
- l_sleep_time := lc_long_sleep_time;
- end if;
- else
- --reset wait time
- -- we wait lc_max_wait_sec for new message
- l_wait_for := lc_max_wait_sec;
- l_already_waited_for := 0;
- l_sleep_time := lc_short_sleep_time;
+ l_sleep_time := case when l_already_waited_sec >= 1 then 0.5 else 0.1 end;
+ l_lock_status := self.get_lock_status();
+ get_data_from_buffer_table( l_buffer_data, l_buffer_rowids, l_finished_flags );
+
+ if l_buffer_data.count > 0 then
+ l_already_waited_sec := 0;
for i in 1 .. l_buffer_data.count loop
if l_buffer_data(i).text is not null then
- pipe row(l_buffer_data(i));
+ pipe row( l_buffer_data(i) );
elsif l_finished_flags(i) = 1 then
l_finished := true;
exit;
end if;
end loop;
- remove_read_data(l_message_rowids);
- l_max_message_id := l_max_message_id + lc_bulk_limit;
- end if;
- if l_finished or l_already_waited_for >= l_wait_for then
- remove_buffer_info();
- if l_already_waited_for > 0 and l_already_waited_for >= l_wait_for then
- raise_application_error(
- ut_utils.gc_out_buffer_timeout,
- 'Timeout occurred while waiting for output data. Waited for: '||l_already_waited_for||' seconds.'
- );
- end if;
+ remove_read_data(l_buffer_rowids);
+ else
+ --nothing fetched from output, wait.
+ dbms_lock.sleep(l_sleep_time);
+ l_already_waited_sec := l_already_waited_sec + l_sleep_time;
end if;
+
+ l_producer_started := (l_lock_status <> 0 or l_buffer_data.count > 0) or l_producer_started;
+ l_producer_finished := (l_producer_started and l_lock_status = 0 and l_buffer_data.count = 0) or l_producer_finished;
+
+ l_finished :=
+ self.timeout_producer_not_finished(l_producer_finished, l_already_waited_sec, a_timeout_sec)
+ or self.timeout_producer_not_started(l_producer_started, l_already_waited_sec, lc_init_wait_sec)
+ or l_producer_finished
+ or l_finished;
+
end loop;
+
+ self.remove_buffer_info();
return;
end;
diff --git a/source/core/output_buffers/ut_output_clob_table_buffer.tps b/source/core/output_buffers/ut_output_clob_table_buffer.tps
index 2eb3640b0..b254c1f47 100644
--- a/source/core/output_buffers/ut_output_clob_table_buffer.tps
+++ b/source/core/output_buffers/ut_output_clob_table_buffer.tps
@@ -1,7 +1,7 @@
-create or replace type ut_output_clob_table_buffer under ut_output_buffer_base (
+create or replace noneditionable type ut_output_clob_table_buffer under ut_output_buffer_base (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ create or replace type ut_output_clob_table_buffer under ut_output_buffer_base (
overriding member procedure send_line(self in out nocopy ut_output_clob_table_buffer, a_text varchar2, a_item_type varchar2 := null),
overriding member procedure send_lines(self in out nocopy ut_output_clob_table_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null),
overriding member procedure send_clob(self in out nocopy ut_output_clob_table_buffer, a_text clob, a_item_type varchar2 := null),
- overriding member procedure close(self in out nocopy ut_output_clob_table_buffer),
- overriding member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined
+ overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined
) not final
/
diff --git a/source/core/output_buffers/ut_output_data_row.tps b/source/core/output_buffers/ut_output_data_row.tps
index b0f5efe91..2eed2ee1f 100644
--- a/source/core/output_buffers/ut_output_data_row.tps
+++ b/source/core/output_buffers/ut_output_data_row.tps
@@ -1,7 +1,7 @@
-create or replace type ut_output_data_row as object (
+create or replace noneditionable type ut_output_data_row as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/output_buffers/ut_output_data_rows.tps b/source/core/output_buffers/ut_output_data_rows.tps
index 1918f0c81..a10440617 100644
--- a/source/core/output_buffers/ut_output_data_rows.tps
+++ b/source/core/output_buffers/ut_output_data_rows.tps
@@ -1,7 +1,7 @@
-create or replace type ut_output_data_rows as
+create or replace noneditionable type ut_output_data_rows as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/output_buffers/ut_output_table_buffer.tpb b/source/core/output_buffers/ut_output_table_buffer.tpb
index e3f869b22..d87362572 100644
--- a/source/core/output_buffers/ut_output_table_buffer.tpb
+++ b/source/core/output_buffers/ut_output_table_buffer.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_output_table_buffer is
+create or replace noneditionable type body ut_output_table_buffer is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -22,21 +22,11 @@ create or replace type body ut_output_table_buffer is
return;
end;
- overriding member procedure close(self in out nocopy ut_output_table_buffer) is
- pragma autonomous_transaction;
- begin
- self.last_message_id := self.last_message_id + 1;
- insert into ut_output_buffer_tmp(output_id, message_id, is_finished)
- values (self.output_id, self.last_message_id, 1);
- commit;
- self.is_closed := 1;
- end;
-
overriding member procedure send_line(self in out nocopy ut_output_table_buffer, a_text varchar2, a_item_type varchar2 := null) is
pragma autonomous_transaction;
begin
if a_text is not null or a_item_type is not null then
- if length(a_text) > ut_utils.gc_max_storage_varchar2_len then
+ if lengthb(a_text) > ut_utils.gc_max_storage_varchar2_len then
self.send_lines(
ut_utils.convert_collection(
ut_utils.clob_to_table(a_text, ut_utils.gc_max_storage_varchar2_len)
@@ -44,9 +34,8 @@ create or replace type body ut_output_table_buffer is
a_item_type
);
else
- self.last_message_id := self.last_message_id + 1;
- insert into ut_output_buffer_tmp(output_id, message_id, text, item_type)
- values (self.output_id, self.last_message_id, a_text, a_item_type);
+ insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type)
+ values (self.output_id, ut_output_buffer_tmp_seq.nextval, a_text, a_item_type);
end if;
commit;
end if;
@@ -55,11 +44,10 @@ create or replace type body ut_output_table_buffer is
overriding member procedure send_lines(self in out nocopy ut_output_table_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null) is
pragma autonomous_transaction;
begin
- insert into ut_output_buffer_tmp(output_id, message_id, text, item_type)
- select self.output_id, self.last_message_id + rownum, t.column_value, a_item_type
+ insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type)
+ select /*+ no_parallel */ self.output_id, ut_output_buffer_tmp_seq.nextval, t.column_value, a_item_type
from table(a_text_list) t
where t.column_value is not null or a_item_type is not null;
- self.last_message_id := self.last_message_id + SQL%rowcount;
commit;
end;
@@ -67,7 +55,7 @@ create or replace type body ut_output_table_buffer is
pragma autonomous_transaction;
begin
if a_text is not null and a_text != empty_clob() or a_item_type is not null then
- if length(a_text) > ut_utils.gc_max_storage_varchar2_len then
+ if ut_utils.lengthb_clob(a_text) > ut_utils.gc_max_storage_varchar2_len then
self.send_lines(
ut_utils.convert_collection(
ut_utils.clob_to_table(a_text, ut_utils.gc_max_storage_varchar2_len)
@@ -75,99 +63,100 @@ create or replace type body ut_output_table_buffer is
a_item_type
);
else
- self.last_message_id := self.last_message_id + 1;
- insert into ut_output_buffer_tmp(output_id, message_id, text, item_type)
- values (self.output_id, self.last_message_id, a_text, a_item_type);
+ insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type)
+ values (self.output_id, ut_output_buffer_tmp_seq.nextval, a_text, a_item_type);
end if;
commit;
end if;
end;
- overriding member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined is
- l_buffer_data ut_varchar2_rows;
- l_item_types ut_varchar2_rows;
- l_finished_flags ut_integer_list;
- l_already_waited_for number(10,2) := 0;
- l_finished boolean := false;
- lc_init_wait_sec constant naturaln := coalesce(a_initial_timeout, 60 ); -- 1 minute
- lc_max_wait_sec constant naturaln := coalesce(a_timeout_sec, 60 * 60 * 4); -- 4 hours
- l_wait_for integer := lc_init_wait_sec;
- lc_short_sleep_time constant number(1,1) := 0.1; --sleep for 100 ms between checks
- lc_long_sleep_time constant number(1) := 1; --sleep for 1 s when waiting long
- lc_long_wait_time constant number(1) := 1; --waiting more than 1 sec
- l_sleep_time number(2,1) := lc_short_sleep_time;
- lc_bulk_limit constant integer := 5000;
- l_max_message_id integer := lc_bulk_limit;
-
- procedure get_data_from_buffer(
- a_max_message_id integer,
- a_buffer_data out nocopy ut_varchar2_rows,
- a_item_types out nocopy ut_varchar2_rows,
- a_finished_flags out nocopy ut_integer_list
- ) is
- pragma autonomous_transaction;
- begin
- delete from (
- select *
- from ut_output_buffer_tmp o
- where o.output_id = self.output_id
- and o.message_id <= a_max_message_id
- order by o.message_id
- ) d
- returning d.text, d.item_type, d.is_finished
- bulk collect into a_buffer_data, a_item_types, a_finished_flags;
- commit;
-
- end;
-
- procedure remove_buffer_info is
- pragma autonomous_transaction;
- begin
- delete from ut_output_buffer_info_tmp a
- where a.output_id = self.output_id;
- commit;
- end;
+ overriding member procedure lines_to_dbms_output(self in ut_output_table_buffer, a_initial_timeout number := null, a_timeout_sec number := null) is
+ l_data sys_refcursor;
+ l_text ut_varchar2_rows;
+ l_item_type ut_varchar2_rows;
+ begin
+ l_data := self.get_lines_cursor(a_initial_timeout, a_timeout_sec);
+ loop
+ fetch l_data bulk collect into l_text, l_item_type limit 10000;
+ for idx in 1 .. l_text.count loop
+ dbms_output.put_line(l_text(idx));
+ end loop;
+ exit when l_data%notfound;
+ end loop;
+ close l_data;
+ end;
- begin
+ /* Important note.
+ This function code is almost duplicated between two types for performance reasons.
+ The pipe row clause is much faster on VARCHAR2 then it is on clob.
+ That is the key reason for two implementations.
+ */
+ overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined is
+ lc_init_wait_sec constant number := coalesce(a_initial_timeout, 10 );
+ l_buffer_texts ut_varchar2_rows;
+ l_buffer_item_types ut_varchar2_rows;
+ l_finished_flags ut_integer_list;
+ l_already_waited_sec number(10,2) := 0;
+ l_finished boolean := false;
+ l_sleep_time number(2,1);
+ l_lock_status integer;
+ l_producer_started boolean := false;
+ l_producer_finished boolean := false;
+
+ procedure get_data_from_buffer_table(
+ a_buffer_texts out nocopy ut_varchar2_rows,
+ a_buffer_item_types out nocopy ut_varchar2_rows,
+ a_finished_flags out nocopy ut_integer_list
+ ) is
+ lc_bulk_limit constant integer := 20000;
+ pragma autonomous_transaction;
+ begin
+ delete /*+ no_parallel */ from (
+ select /*+ no_parallel */ *
+ from ut_output_buffer_tmp a
+ where a.output_id = self.output_id
+ and a.message_id <= (select min(message_id) from ut_output_buffer_tmp o where o.output_id = self.output_id) + lc_bulk_limit
+ order by a.message_id
+ ) d
+ returning d.text, d.item_type, d.is_finished
+ bulk collect into a_buffer_texts, a_buffer_item_types, a_finished_flags;
+ commit;
+ end;
+ begin
while not l_finished loop
- get_data_from_buffer( l_max_message_id, l_buffer_data, l_item_types, l_finished_flags);
- --nothing fetched from output, wait and try again
- if l_buffer_data.count = 0 then
- $if dbms_db_version.version >= 18 $then
- dbms_session.sleep(l_sleep_time);
- $else
- dbms_lock.sleep(l_sleep_time);
- $end
- l_already_waited_for := l_already_waited_for + l_sleep_time;
- if l_already_waited_for > lc_long_wait_time then
- l_sleep_time := lc_long_sleep_time;
- end if;
- else
- --reset wait time
- -- we wait lc_max_wait_sec for new message
- l_wait_for := lc_max_wait_sec;
- l_already_waited_for := 0;
- l_sleep_time := lc_short_sleep_time;
- for i in 1 .. l_buffer_data.count loop
- if l_buffer_data(i) is not null then
- pipe row(ut_output_data_row(l_buffer_data(i),l_item_types(i)));
+
+ l_sleep_time := case when l_already_waited_sec >= 1 then 0.5 else 0.1 end;
+ l_lock_status := self.get_lock_status();
+ get_data_from_buffer_table( l_buffer_texts, l_buffer_item_types, l_finished_flags );
+
+ if l_buffer_texts.count > 0 then
+ l_already_waited_sec := 0;
+ for i in 1 .. l_buffer_texts.count loop
+ if l_buffer_texts(i) is not null then
+ pipe row( ut_output_data_row(l_buffer_texts(i), l_buffer_item_types(i)) );
elsif l_finished_flags(i) = 1 then
l_finished := true;
exit;
end if;
end loop;
- l_max_message_id := l_max_message_id + lc_bulk_limit;
- end if;
- if l_finished or l_already_waited_for >= l_wait_for then
- remove_buffer_info();
- if l_already_waited_for > 0 and l_already_waited_for >= l_wait_for then
- raise_application_error(
- ut_utils.gc_out_buffer_timeout,
- 'Timeout occurred while waiting for output data. Waited for: '||l_already_waited_for||' seconds.'
- );
- end if;
+ else
+ --nothing fetched from output, wait.
+ dbms_lock.sleep(l_sleep_time);
+ l_already_waited_sec := l_already_waited_sec + l_sleep_time;
end if;
+
+ l_producer_started := (l_lock_status <> 0 or l_buffer_texts.count > 0) or l_producer_started;
+ l_producer_finished := (l_producer_started and l_lock_status = 0 and l_buffer_texts.count = 0) or l_producer_finished;
+
+ l_finished :=
+ self.timeout_producer_not_finished(l_producer_finished, l_already_waited_sec, a_timeout_sec)
+ or self.timeout_producer_not_started(l_producer_started, l_already_waited_sec, lc_init_wait_sec)
+ or l_producer_finished
+ or l_finished;
+
end loop;
+
+ self.remove_buffer_info();
return;
end;
diff --git a/source/core/output_buffers/ut_output_table_buffer.tps b/source/core/output_buffers/ut_output_table_buffer.tps
index 3e395304a..3f13e736d 100644
--- a/source/core/output_buffers/ut_output_table_buffer.tps
+++ b/source/core/output_buffers/ut_output_table_buffer.tps
@@ -1,7 +1,7 @@
-create or replace type ut_output_table_buffer under ut_output_buffer_base (
+create or replace noneditionable type ut_output_table_buffer under ut_output_buffer_base (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ create or replace type ut_output_table_buffer under ut_output_buffer_base (
overriding member procedure send_line(self in out nocopy ut_output_table_buffer, a_text varchar2, a_item_type varchar2 := null),
overriding member procedure send_lines(self in out nocopy ut_output_table_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null),
overriding member procedure send_clob(self in out nocopy ut_output_table_buffer, a_text clob, a_item_type varchar2 := null),
- overriding member procedure close(self in out nocopy ut_output_table_buffer),
- overriding member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined
+ overriding member procedure lines_to_dbms_output(self in ut_output_table_buffer, a_initial_timeout number := null, a_timeout_sec number := null),
+ overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined
) not final
/
diff --git a/source/core/session_context/ut_session_context.pkb b/source/core/session_context/ut_session_context.pkb
index 2364d8513..12d104eed 100644
--- a/source/core/session_context/ut_session_context.pkb
+++ b/source/core/session_context/ut_session_context.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_session_context as
+create or replace noneditionable package body ut_session_context as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/session_context/ut_session_context.pks b/source/core/session_context/ut_session_context.pks
index bbe256db4..a5e4038a0 100644
--- a/source/core/session_context/ut_session_context.pks
+++ b/source/core/session_context/ut_session_context.pks
@@ -1,7 +1,7 @@
-create or replace package ut_session_context as
+create or replace noneditionable package ut_session_context as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/session_context/ut_session_info.tpb b/source/core/session_context/ut_session_info.tpb
index 2d3fdbed9..14f52b003 100644
--- a/source/core/session_context/ut_session_info.tpb
+++ b/source/core/session_context/ut_session_info.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_session_info as
+create or replace noneditionable type body ut_session_info as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -28,12 +28,14 @@ create or replace type body ut_session_info as
member procedure before_calling_run(self in out nocopy ut_session_info, a_run in ut_run) is
begin
ut_session_context.set_context( 'run_paths', ut_utils.to_string( ut_utils.table_to_clob( a_run.run_paths,',' ), null ) );
+ ut_session_context.set_context( 'coverage_run_id', rawtohex( a_run.coverage_options.coverage_run_id ) );
dbms_application_info.set_module( 'utPLSQL', null );
end;
member procedure after_calling_run(self in out nocopy ut_session_info, a_run in ut_run) is
begin
ut_session_context.clear_context( 'run_paths' );
+ ut_session_context.clear_context( 'coverage_run_id' );
dbms_application_info.set_module( module, action );
dbms_application_info.set_client_info( client_info );
end;
diff --git a/source/core/session_context/ut_session_info.tps b/source/core/session_context/ut_session_info.tps
index 863b0dc3e..d682a6bad 100644
--- a/source/core/session_context/ut_session_info.tps
+++ b/source/core/session_context/ut_session_info.tps
@@ -1,7 +1,7 @@
-create or replace type ut_session_info under ut_event_listener (
+create or replace noneditionable type ut_session_info under ut_event_listener (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_console_reporter_base.tpb b/source/core/types/ut_console_reporter_base.tpb
index df256e832..0825b8ba2 100644
--- a/source/core/types/ut_console_reporter_base.tpb
+++ b/source/core/types/ut_console_reporter_base.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_console_reporter_base is
+create or replace noneditionable type body ut_console_reporter_base is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_console_reporter_base.tps b/source/core/types/ut_console_reporter_base.tps
index 6c0a08502..46b344e6e 100644
--- a/source/core/types/ut_console_reporter_base.tps
+++ b/source/core/types/ut_console_reporter_base.tps
@@ -1,7 +1,7 @@
-create or replace type ut_console_reporter_base under ut_output_reporter_base(
+create or replace noneditionable type ut_console_reporter_base under ut_output_reporter_base(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_coverage_options.tpb b/source/core/types/ut_coverage_options.tpb
index 6eb4921a7..2accd960a 100644
--- a/source/core/types/ut_coverage_options.tpb
+++ b/source/core/types/ut_coverage_options.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_coverage_options as
+create or replace noneditionable type body ut_coverage_options as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -22,7 +22,11 @@ create or replace type body ut_coverage_options as
schema_names ut_varchar2_rows := null,
exclude_objects ut_varchar2_rows := null,
include_objects ut_varchar2_rows := null,
- file_mappings ut_file_mappings := null
+ file_mappings ut_file_mappings := null,
+ include_schema_expr varchar2 := null,
+ include_object_expr varchar2 := null,
+ exclude_schema_expr varchar2 := null,
+ exclude_object_expr varchar2 := null
) return self as result is
function to_ut_object_list(a_names ut_varchar2_rows, a_schema_names ut_varchar2_rows) return ut_object_names is
l_result ut_object_names;
@@ -54,10 +58,15 @@ create or replace type body ut_coverage_options as
if exclude_objects is not empty then
self.exclude_objects := to_ut_object_list(exclude_objects, self.schema_names);
end if;
--- self.exclude_objects := self.exclude_objects multiset union all ut_suite_manager.get_schema_ut_packages(schema_names);
self.include_objects := to_ut_object_list(include_objects, self.schema_names);
+ self.include_schema_expr := include_schema_expr;
+ self.include_object_expr := include_object_expr;
+ self.exclude_schema_expr := exclude_schema_expr;
+ self.exclude_object_expr := exclude_object_expr;
+
+
return;
end;
end;
diff --git a/source/core/types/ut_coverage_options.tps b/source/core/types/ut_coverage_options.tps
index d6e86b653..f2db1dafe 100644
--- a/source/core/types/ut_coverage_options.tps
+++ b/source/core/types/ut_coverage_options.tps
@@ -1,7 +1,7 @@
-create or replace type ut_coverage_options force as object (
+create or replace noneditionable type ut_coverage_options force as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,13 +21,21 @@ create or replace type ut_coverage_options force as object (
exclude_objects ut_object_names,
include_objects ut_object_names,
file_mappings ut_file_mappings,
+ include_schema_expr varchar2(4000),
+ include_object_expr varchar2(4000),
+ exclude_schema_expr varchar2(4000),
+ exclude_object_expr varchar2(4000),
constructor function ut_coverage_options(
self in out nocopy ut_coverage_options,
coverage_run_id raw,
schema_names ut_varchar2_rows := null,
exclude_objects ut_varchar2_rows := null,
include_objects ut_varchar2_rows := null,
- file_mappings ut_file_mappings := null
+ file_mappings ut_file_mappings := null,
+ include_schema_expr varchar2 := null,
+ include_object_expr varchar2 := null,
+ exclude_schema_expr varchar2 := null,
+ exclude_object_expr varchar2 := null
) return self as result
)
/
diff --git a/source/core/types/ut_executable.tpb b/source/core/types/ut_executable.tpb
index 826a5aaa1..2fa3c1b5d 100644
--- a/source/core/types/ut_executable.tpb
+++ b/source/core/types/ut_executable.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_executable is
+create or replace noneditionable type body ut_executable is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -38,20 +38,30 @@ create or replace type body ut_executable is
return ut_metadata.form_name(l_owner_name, object_name, procedure_name);
end;
- member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) is
+ member procedure do_execute(
+ self in out nocopy ut_executable,
+ a_item in out nocopy ut_suite_item,
+ a_ignored_exception_names in ut_varchar2_rows := null,
+ a_ignored_exception_numbers in ut_varchar2_rows :=null)
+ is
l_completed_without_errors boolean;
begin
- l_completed_without_errors := self.do_execute(a_item);
+ l_completed_without_errors := self.do_execute(a_item, a_ignored_exception_names, a_ignored_exception_numbers);
end do_execute;
- member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean is
- l_statement varchar2(4000);
- l_status number;
- l_cursor_number number;
- l_completed_without_errors boolean := true;
- l_failed_with_invalid_pck boolean := true;
- l_start_transaction_id varchar2(250);
- l_end_transaction_id varchar2(250);
+ member function do_execute(
+ self in out nocopy ut_executable,
+ a_item in out nocopy ut_suite_item,
+ a_ignored_exception_names in ut_varchar2_rows := null,
+ a_ignored_exception_numbers in ut_varchar2_rows :=null)
+ return boolean is
+ l_statement varchar2(4000);
+ l_status number;
+ l_cursor_number number;
+ l_completed_without_errors boolean := true;
+ l_failed_with_invalid_pck boolean := true;
+ l_start_transaction_id varchar2(250);
+ l_end_transaction_id varchar2(250);
function is_defined return boolean is
l_result boolean := false;
@@ -111,19 +121,33 @@ create or replace type body ut_executable is
if l_completed_without_errors then
l_statement :=
'declare' || chr(10) ||
- ' l_error_stack varchar2(32767);' || chr(10) ||
- ' l_error_backtrace varchar2(32767);' || chr(10) ||
+ ' l_error_stack varchar2(32767);' || chr(10) ||
+ ' l_error_backtrace varchar2(32767);' || chr(10) ||
+ ' l_ignored_exception_detected integer := 0;' || chr(10) ||
'begin' || chr(10) ||
' begin' || chr(10) ||
' ' || self.form_name( a_skip_current_user_schema => true ) || ';' || chr(10) ||
' exception' || chr(10) ||
+ case when a_ignored_exception_names is not empty then
+ ' when ' || ut_utils.table_to_clob( a_ignored_exception_names, ' or ' ) || ' then ' || chr(10) ||
+ ' l_ignored_exception_detected := 1;'
+ end ||
' when others then ' || chr(10) ||
+ case when a_ignored_exception_numbers is not empty then
+ ' if sqlcode in (' || ut_utils.table_to_clob( a_ignored_exception_numbers, ', ' ) || ') then ' || chr(10) ||
+ ' l_ignored_exception_detected := 1; ' || chr(10) ||
+ ' else ' || chr(10) ||
+ ' l_error_stack := dbms_utility.format_error_stack;' || chr(10) ||
+ ' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) ||
+ ' end if;'
+ else
' l_error_stack := dbms_utility.format_error_stack;' || chr(10) ||
- ' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) ||
- ' --raise on ORA-04068, ORA-04061: existing state of packages has been discarded to avoid unrecoverable session exception' || chr(10) ||
+ ' l_error_backtrace := dbms_utility.format_error_backtrace;'
+ end || chr(10) ||
' end;' || chr(10) ||
' :a_error_stack := l_error_stack;' || chr(10) ||
' :a_error_backtrace := l_error_backtrace;' || chr(10) ||
+ ' :a_ignored_exception_detected := l_ignored_exception_detected;' || chr(10) ||
'end;';
ut_utils.debug_log('ut_executable.do_execute l_statement: ' || l_statement);
@@ -139,9 +163,11 @@ create or replace type body ut_executable is
dbms_sql.parse(l_cursor_number, statement => l_statement, language_flag => dbms_sql.native);
dbms_sql.bind_variable(l_cursor_number, 'a_error_stack', to_char(null), 32767);
dbms_sql.bind_variable(l_cursor_number, 'a_error_backtrace', to_char(null), 32767);
+ dbms_sql.bind_variable(l_cursor_number, 'a_ignored_exception_detected', to_number(null));
l_status := dbms_sql.execute(l_cursor_number);
dbms_sql.variable_value(l_cursor_number, 'a_error_stack', self.error_stack);
dbms_sql.variable_value(l_cursor_number, 'a_error_backtrace', self.error_backtrace);
+ dbms_sql.variable_value(l_cursor_number, 'a_ignored_exception_detected', self.ignored_exception_detected);
dbms_sql.close_cursor(l_cursor_number);
exception
when ut_utils.ex_invalid_package then
diff --git a/source/core/types/ut_executable.tps b/source/core/types/ut_executable.tps
index 786d926ad..0945a8996 100644
--- a/source/core/types/ut_executable.tps
+++ b/source/core/types/ut_executable.tps
@@ -1,7 +1,7 @@
-create or replace type ut_executable under ut_event_item(
+create or replace noneditionable type ut_executable under ut_event_item(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -18,26 +18,37 @@ create or replace type ut_executable under ut_event_item(
/**
* The name of the event to be executed before and after the executable is invoked
*/
- executable_type varchar2(250 char),
- owner_name varchar2(250 char),
- object_name varchar2(250 char),
- procedure_name varchar2(250 char),
- error_backtrace varchar2(4000),
- error_stack varchar2(4000),
- serveroutput clob,
+ executable_type varchar2(250 char),
+ owner_name varchar2(250 char),
+ object_name varchar2(250 char),
+ procedure_name varchar2(250 char),
+ error_backtrace varchar2(4000),
+ error_stack varchar2(4000),
+ ignored_exception_detected integer,
+ serveroutput clob,
/**
* Used for ordering of executables, as Oracle doesn not guarantee ordering of items in a nested table.
*/
seq_no integer,
constructor function ut_executable( self in out nocopy ut_executable, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_executable_type varchar2) return self as result,
member function form_name(a_skip_current_user_schema boolean := false) return varchar2,
- member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item),
+ member procedure do_execute(
+ self in out nocopy ut_executable,
+ a_item in out nocopy ut_suite_item,
+ a_ignored_exception_names in ut_varchar2_rows := null,
+ a_ignored_exception_numbers in ut_varchar2_rows := null
+ ),
/**
* executes the defines executable
* returns true if executed without exceptions
* returns false if exceptions were raised
*/
- member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean,
+ member function do_execute(
+ self in out nocopy ut_executable,
+ a_item in out nocopy ut_suite_item,
+ a_ignored_exception_names in ut_varchar2_rows := null,
+ a_ignored_exception_numbers in ut_varchar2_rows :=null
+ ) return boolean,
member function get_error_stack_trace return varchar2
) not final
/
diff --git a/source/core/types/ut_executable_test.tpb b/source/core/types/ut_executable_test.tpb
index 6f25ea1c8..8e48bd70a 100644
--- a/source/core/types/ut_executable_test.tpb
+++ b/source/core/types/ut_executable_test.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_executable_test as
+create or replace noneditionable type body ut_executable_test as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -31,141 +31,129 @@ create or replace type body ut_executable_test as
member procedure do_execute(
self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item,
- a_expected_error_codes in ut_varchar2_rows
+ a_expected_errors in ut_varchar2_rows
) is
l_completed_without_errors boolean;
begin
- l_completed_without_errors := self.do_execute(a_item, a_expected_error_codes);
+ l_completed_without_errors := self.do_execute(a_item, a_expected_errors);
end do_execute;
member function do_execute(
self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item,
- a_expected_error_codes in ut_varchar2_rows
+ a_expected_errors in ut_varchar2_rows
) return boolean is
- l_expected_except_message varchar2(4000);
- l_expected_error_numbers ut_integer_list;
-
- function build_exception_numbers_list(
- a_item in out nocopy ut_suite_item,
- a_expected_error_codes in ut_varchar2_rows
- ) return ut_integer_list is
+ l_failure_message varchar2(32767);
+ l_expected_error_names ut_varchar2_rows;
+ l_expected_error_numbers ut_varchar2_rows;
+ l_all_expected_errors ut_varchar2_rows;
+
+ procedure build_expected_exceptions(
+ a_item in out nocopy ut_suite_item,
+ a_exception_definitions in ut_varchar2_rows,
+ a_exception_names_list out ut_varchar2_rows,
+ a_exception_numbers_list out ut_varchar2_rows
+ ) is
l_exception_number integer;
- l_exception_number_list ut_integer_list := ut_integer_list();
- c_regexp_for_exception_no constant varchar2(30) := '^-?[[:digit:]]{1,5}$';
-
- c_integer_exception constant varchar2(1) := 'I';
- c_named_exception constant varchar2(1) := 'N';
-
- function is_valid_qualified_name (a_name varchar2) return boolean is
- l_name varchar2(500);
- begin
- l_name := dbms_assert.qualified_sql_name(a_name);
- return true;
- exception when others then
- return false;
- end;
-
- function check_exception_type(a_exception_name in varchar2) return varchar2 is
- l_exception_type varchar2(50);
- begin
- --check if it is a predefined exception
+ l_exception_name varchar2(128);
+ function to_exception_number (a_exception_var in varchar2, a_item ut_suite_item) return integer is
+ function get_exception_no(a_exception_var in varchar2) return integer is
+ l_exc_no integer;
begin
- execute immediate 'begin null; exception when '||a_exception_name||' then null; end;';
- l_exception_type := c_named_exception;
+ execute immediate 'declare l_exception integer; begin :l_exception := '||a_exception_var||'; end;'
+ using out l_exc_no;
+ return l_exc_no;
exception
when others then
- if dbms_utility.format_error_stack() like '%PLS-00485%' then
- declare
- e_invalid_number exception;
- pragma exception_init ( e_invalid_number, -6502 );
- begin
- execute immediate 'declare x integer := '||a_exception_name||'; begin null; end;';
- l_exception_type := c_integer_exception;
- exception
- when others then
- null;
- end;
- end if;
+ return null;
end;
- return l_exception_type;
- end;
-
- function get_exception_number (a_exception_var in varchar2) return integer is
- l_exc_no integer;
- l_exc_type varchar2(50);
function remap_no_data_found (a_number integer) return integer is
begin
return case a_number when 100 then -1403 else a_number end;
end;
begin
- l_exc_type := check_exception_type(a_exception_var);
-
- execute immediate
- case l_exc_type
- when c_integer_exception then
- 'declare l_exception number; begin :l_exception := '||a_exception_var||'; end;'
- when c_named_exception then
- 'begin raise '||a_exception_var||'; exception when others then :l_exception := sqlcode; end;'
- else
- 'begin :l_exception := null; end;'
- end
- using out l_exc_no;
+ return remap_no_data_found(
+ coalesce(
+ get_exception_no(a_exception_var),
+ get_exception_no(a_item.object_name||'.'||a_exception_var),
+ get_exception_no(a_item.object_owner||'.'||a_exception_var),
+ get_exception_no(a_item.object_owner||'.'||a_item.object_name||'.'||a_exception_var)
+ )
+ );
+ end;
+
+ function to_exception_name (a_exception_var in varchar2, a_item ut_suite_item) return varchar2 is
+ function verify_name(a_exception_name in varchar2) return varchar2 is
+ begin
+ --check if it is an existing, named exception
+ execute immediate 'begin null; exception when '||a_exception_name||' then null; end;';
+ return a_exception_name;
+ exception
+ when others then
+ return null;
+ end;
+ begin
+ return coalesce(
+ verify_name(a_exception_var),
+ verify_name(a_item.object_name||'.'||a_exception_var),
+ verify_name(a_item.object_owner||'.'||a_exception_var),
+ verify_name(a_item.object_owner||'.'||a_item.object_name||'.'||a_exception_var)
+ );
- return remap_no_data_found(l_exc_no);
end;
begin
- if a_expected_error_codes is not empty then
- for i in 1 .. a_expected_error_codes.count loop
- /**
- * Check if its a valid qualified name and if so try to resolve name to an exception number
- */
- if is_valid_qualified_name(a_expected_error_codes(i)) then
- l_exception_number := get_exception_number(a_expected_error_codes(i));
- elsif regexp_like(a_expected_error_codes(i), c_regexp_for_exception_no) then
- l_exception_number := a_expected_error_codes(i);
- end if;
-
- if l_exception_number is null then
+ a_exception_names_list := ut_varchar2_rows();
+ a_exception_numbers_list := ut_varchar2_rows();
+ if a_exception_definitions is not empty then
+ for i in 1 .. a_exception_definitions.count loop
+ l_exception_number :=
+ coalesce(
+ to_exception_number(a_exception_definitions(i), a_item),
+ to_number(a_exception_definitions(i) default null on conversion error )
+ );
+ l_exception_name := to_exception_name(a_exception_definitions(i), a_item);
+
+ if l_exception_number is null and l_exception_name is null then
a_item.put_warning(
- 'Invalid parameter value "'||a_expected_error_codes(i)||'" for "--%throws" annotation. Parameter ignored.',
+ 'Invalid parameter value "'||a_exception_definitions(i)||'" for "--%throws" annotation. Parameter ignored.',
self.procedure_name,
a_item.line_no
);
elsif l_exception_number >= 0 then
a_item.put_warning(
- 'Invalid parameter value "'||a_expected_error_codes(i)||'" for "--%throws" annotation. Exception value must be a negative integer. Parameter ignored.',
+ 'Invalid parameter value "'||a_exception_definitions(i)||'" for "--%throws" annotation. Exception value must be a negative integer. Parameter ignored.',
self.procedure_name,
a_item.line_no
);
- else
- l_exception_number_list.extend;
- l_exception_number_list(l_exception_number_list.last) := l_exception_number;
+ elsif l_exception_number is not null then
+ a_exception_numbers_list.extend;
+ a_exception_numbers_list(a_exception_numbers_list.last) := l_exception_number;
+ elsif l_exception_name is not null then
+ a_exception_names_list.extend;
+ a_exception_names_list(a_exception_names_list.last) := l_exception_name;
end if;
- l_exception_number := null;
end loop;
end if;
-
- return l_exception_number_list;
end;
- function failed_expec_errnum_message(a_expected_error_codes in ut_integer_list) return varchar is
+
+ function get_exception_failure_message(a_expected_errors_list ut_varchar2_rows) return varchar2 is
l_actual_error_no integer;
- l_expected_error_codes varchar2(4000);
+ l_expected_errors varchar2(4000);
l_fail_message varchar2(4000);
begin
--Convert the ut_varchar2_list to string to can construct the message
- l_expected_error_codes := ut_utils.table_to_clob(a_expected_error_codes, ', ');
+ l_expected_errors := ut_utils.table_to_clob(a_expected_errors_list, ', ');
if self.error_stack is null then
- l_fail_message := 'Expected one of exceptions ('||l_expected_error_codes||') but nothing was raised.';
+ l_fail_message := 'Expected one of exceptions ('|| l_expected_errors|| ') but nothing was raised.';
else
- l_actual_error_no := regexp_substr(self.error_stack, '^[a-zA-Z]{3}(-[0-9]+)', subexpression=>1);
- if not l_actual_error_no member of a_expected_error_codes or l_actual_error_no is null then
+ l_actual_error_no := regexp_substr(self.error_stack, '^[[:alpha:]]{3}(-[0-9]+)', subexpression=>1);
+ if not l_actual_error_no member of a_expected_errors_list or l_actual_error_no is null then
l_fail_message := 'Actual: '||l_actual_error_no||' was expected to ';
- if cardinality(a_expected_error_codes) > 1 then
- l_fail_message := l_fail_message || 'be one of: ('||l_expected_error_codes||')';
+ if cardinality(a_expected_errors_list) > 1 then
+ l_fail_message := l_fail_message || 'be one of: ('|| l_expected_errors|| ')';
else
- l_fail_message := l_fail_message || 'equal: '||l_expected_error_codes;
+ l_fail_message := l_fail_message || 'equal: '||l_expected_errors;
end if;
l_fail_message := substr( l_fail_message||chr(10)||self.error_stack||chr(10)||self.error_backtrace, 1, 4000 );
end if;
@@ -175,14 +163,16 @@ create or replace type body ut_executable_test as
end;
begin
--Create a ut_executable object and call do_execute after that get the data to know the test's execution result
- self.do_execute(a_item);
- l_expected_error_numbers := build_exception_numbers_list(a_item, a_expected_error_codes);
- if l_expected_error_numbers is not null and l_expected_error_numbers is not empty then
- l_expected_except_message := failed_expec_errnum_message( l_expected_error_numbers );
+ build_expected_exceptions(a_item, a_expected_errors, l_expected_error_names, l_expected_error_numbers );
+
+ self.do_execute(a_item, l_expected_error_names, l_expected_error_numbers);
+ l_all_expected_errors := l_expected_error_names multiset union all l_expected_error_numbers;
+ if l_all_expected_errors is not empty and self.ignored_exception_detected = 0 then
+ l_failure_message := get_exception_failure_message( l_all_expected_errors);
- if l_expected_except_message is not null then
+ if l_failure_message is not null then
ut_expectation_processor.add_expectation_result(
- ut_expectation_result(ut_utils.gc_failure, null, l_expected_except_message, false)
+ ut_expectation_result(ut_utils.gc_failure, null, l_failure_message, false)
);
end if;
self.error_stack := null;
diff --git a/source/core/types/ut_executable_test.tps b/source/core/types/ut_executable_test.tps
index 88500c9be..d477456f6 100644
--- a/source/core/types/ut_executable_test.tps
+++ b/source/core/types/ut_executable_test.tps
@@ -1,7 +1,7 @@
-create or replace type ut_executable_test authid current_user under ut_executable (
+create or replace noneditionable type ut_executable_test authid current_user under ut_executable (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -22,12 +22,12 @@ create or replace type ut_executable_test authid current_user under ut_executabl
member procedure do_execute(
self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item,
- a_expected_error_codes in ut_varchar2_rows
+ a_expected_errors in ut_varchar2_rows
),
member function do_execute(
self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item,
- a_expected_error_codes in ut_varchar2_rows
+ a_expected_errors in ut_varchar2_rows
) return boolean
) final;
diff --git a/source/core/types/ut_executables.tps b/source/core/types/ut_executables.tps
index f5732d5a1..7718c44b8 100644
--- a/source/core/types/ut_executables.tps
+++ b/source/core/types/ut_executables.tps
@@ -1,7 +1,7 @@
-create or replace type ut_executables as
+create or replace noneditionable type ut_executables as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_expectation_result.tpb b/source/core/types/ut_expectation_result.tpb
index 9aa8b81c0..3ffd11789 100644
--- a/source/core/types/ut_expectation_result.tpb
+++ b/source/core/types/ut_expectation_result.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_expectation_result is
+create or replace noneditionable type body ut_expectation_result is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_expectation_result.tps b/source/core/types/ut_expectation_result.tps
index 815d90820..e7eedd5ce 100644
--- a/source/core/types/ut_expectation_result.tps
+++ b/source/core/types/ut_expectation_result.tps
@@ -1,7 +1,7 @@
-create or replace type ut_expectation_result under ut_event_item(
+create or replace noneditionable type ut_expectation_result under ut_event_item(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_expectation_results.tps b/source/core/types/ut_expectation_results.tps
index 963c726f8..168941112 100644
--- a/source/core/types/ut_expectation_results.tps
+++ b/source/core/types/ut_expectation_results.tps
@@ -1,7 +1,7 @@
-create or replace type ut_expectation_results as
+create or replace noneditionable type ut_expectation_results as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_file_mapping.tpb b/source/core/types/ut_file_mapping.tpb
index 247aa5ac8..d693ac633 100644
--- a/source/core/types/ut_file_mapping.tpb
+++ b/source/core/types/ut_file_mapping.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_file_mapping as
+create or replace noneditionable type body ut_file_mapping as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_file_mapping.tps b/source/core/types/ut_file_mapping.tps
index c032e0b77..d28ef40ae 100644
--- a/source/core/types/ut_file_mapping.tps
+++ b/source/core/types/ut_file_mapping.tps
@@ -1,7 +1,7 @@
-create or replace type ut_file_mapping as object(
+create or replace noneditionable type ut_file_mapping as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_file_mappings.tps b/source/core/types/ut_file_mappings.tps
index a48c3e872..715530ee0 100644
--- a/source/core/types/ut_file_mappings.tps
+++ b/source/core/types/ut_file_mappings.tps
@@ -1,7 +1,7 @@
-create or replace type ut_file_mappings as
+create or replace noneditionable type ut_file_mappings as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_integer_list.tps b/source/core/types/ut_integer_list.tps
index 25f1679b2..b15cf743c 100644
--- a/source/core/types/ut_integer_list.tps
+++ b/source/core/types/ut_integer_list.tps
@@ -1,7 +1,7 @@
-create or replace type ut_integer_list as
+create or replace noneditionable type ut_integer_list as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_key_value_pair.tps b/source/core/types/ut_key_value_pair.tps
index 873a354fd..e26fff031 100644
--- a/source/core/types/ut_key_value_pair.tps
+++ b/source/core/types/ut_key_value_pair.tps
@@ -1,7 +1,7 @@
-create or replace type ut_key_value_pair force as object(
+create or replace noneditionable type ut_key_value_pair force as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_key_value_pairs.tps b/source/core/types/ut_key_value_pairs.tps
index 28fa87cf0..d9e829dc9 100644
--- a/source/core/types/ut_key_value_pairs.tps
+++ b/source/core/types/ut_key_value_pairs.tps
@@ -1,7 +1,7 @@
-create or replace type ut_key_value_pairs as
+create or replace noneditionable type ut_key_value_pairs as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_logical_suite.tpb b/source/core/types/ut_logical_suite.tpb
index 74c7ff81c..c91320d8a 100644
--- a/source/core/types/ut_logical_suite.tpb
+++ b/source/core/types/ut_logical_suite.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_logical_suite as
+create or replace noneditionable type body ut_logical_suite as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -16,12 +16,12 @@ create or replace type body ut_logical_suite as
limitations under the License.
*/
- overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite) is
+ overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_skip_reason in varchar2) is
begin
ut_event_manager.trigger_event(ut_event_manager.gc_before_suite, self);
self.start_time := current_timestamp;
for i in 1 .. self.items.count loop
- self.items(i).mark_as_skipped();
+ self.items(i).mark_as_skipped(coalesce(a_skip_reason,self.disabled_reason));
end loop;
self.end_time := self.start_time;
ut_event_manager.trigger_event(ut_event_manager.gc_after_suite, self);
diff --git a/source/core/types/ut_logical_suite.tps b/source/core/types/ut_logical_suite.tps
index 71cfdb23d..245c422a1 100644
--- a/source/core/types/ut_logical_suite.tps
+++ b/source/core/types/ut_logical_suite.tps
@@ -1,7 +1,7 @@
-create or replace type ut_logical_suite under ut_suite_item (
+create or replace noneditionable type ut_logical_suite force under ut_suite_item (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ create or replace type ut_logical_suite under ut_suite_item (
*/
items ut_suite_items,
- overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite),
+ overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_skip_reason in varchar2),
overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer, a_force boolean := false),
overriding member function do_execute(self in out nocopy ut_logical_suite) return boolean,
overriding member procedure calc_execution_result(self in out nocopy ut_logical_suite),
diff --git a/source/core/types/ut_object_name.tpb b/source/core/types/ut_object_name.tpb
index cf1ab2a78..67834a9ed 100644
--- a/source/core/types/ut_object_name.tpb
+++ b/source/core/types/ut_object_name.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_object_name as
+create or replace noneditionable type body ut_object_name as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_object_name.tps b/source/core/types/ut_object_name.tps
index 28e41958f..7f6341a32 100644
--- a/source/core/types/ut_object_name.tps
+++ b/source/core/types/ut_object_name.tps
@@ -1,7 +1,7 @@
-create or replace type ut_object_name as object (
+create or replace noneditionable type ut_object_name as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_object_names.tps b/source/core/types/ut_object_names.tps
index 2f7e5ea16..879493a63 100644
--- a/source/core/types/ut_object_names.tps
+++ b/source/core/types/ut_object_names.tps
@@ -1,7 +1,7 @@
-create or replace type ut_object_names as
+create or replace noneditionable type ut_object_names as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_output_reporter_base.tpb b/source/core/types/ut_output_reporter_base.tpb
index c4ba73ba5..4553f1780 100644
--- a/source/core/types/ut_output_reporter_base.tpb
+++ b/source/core/types/ut_output_reporter_base.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_output_reporter_base is
+create or replace noneditionable type body ut_output_reporter_base is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -41,13 +41,6 @@ create or replace type body ut_output_reporter_base is
return l_result;
end;
- overriding member procedure before_calling_run(self in out nocopy ut_output_reporter_base, a_run in ut_run) is
- l_output_table_buffer ut_output_table_buffer;
- begin
- (self as ut_reporter_base).before_calling_run(a_run);
- l_output_table_buffer := treat(self.output_buffer as ut_output_table_buffer);
- end;
-
member procedure print_text(self in out nocopy ut_output_reporter_base, a_text varchar2, a_item_type varchar2 := null) is
begin
self.output_buffer.send_line(a_text, a_item_type);
@@ -65,7 +58,7 @@ create or replace type body ut_output_reporter_base is
final member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural) return ut_output_data_rows pipelined is
begin
- for i in (select value(x) val from table(self.output_buffer.get_lines(a_initial_timeout, a_timeout_sec)) x ) loop
+ for i in (select /*+ no_parallel */ value(x) val from table(self.output_buffer.get_lines(a_initial_timeout, a_timeout_sec)) x ) loop
pipe row (i.val);
end loop;
end;
@@ -87,6 +80,7 @@ create or replace type body ut_output_reporter_base is
overriding member procedure on_initialize(self in out nocopy ut_output_reporter_base, a_run in ut_run) is
begin
+ self.output_buffer.lock_buffer();
self.output_buffer.send_line(null, 'initialize');
end;
diff --git a/source/core/types/ut_output_reporter_base.tps b/source/core/types/ut_output_reporter_base.tps
index 01350208a..b9ec4f7cd 100644
--- a/source/core/types/ut_output_reporter_base.tps
+++ b/source/core/types/ut_output_reporter_base.tps
@@ -1,7 +1,7 @@
-create or replace type ut_output_reporter_base under ut_reporter_base(
+create or replace noneditionable type ut_output_reporter_base under ut_reporter_base(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -20,8 +20,7 @@ create or replace type ut_output_reporter_base under ut_reporter_base(
member procedure init(self in out nocopy ut_output_reporter_base, a_self_type varchar2, a_output_buffer ut_output_buffer_base := null),
overriding member procedure set_reporter_id(self in out nocopy ut_output_reporter_base, a_reporter_id raw),
member function set_reporter_id(self in ut_output_reporter_base, a_reporter_id raw) return ut_output_reporter_base,
- overriding member procedure before_calling_run(self in out nocopy ut_output_reporter_base, a_run in ut_run),
-
+
member procedure print_text(self in out nocopy ut_output_reporter_base, a_text varchar2, a_item_type varchar2 := null),
member procedure print_text_lines(self in out nocopy ut_output_reporter_base, a_text_lines ut_varchar2_rows, a_item_type varchar2 := null),
member procedure print_clob(self in out nocopy ut_output_reporter_base, a_clob clob, a_item_type varchar2 := null),
diff --git a/source/core/types/ut_path_item.tpb b/source/core/types/ut_path_item.tpb
new file mode 100644
index 000000000..486fde865
--- /dev/null
+++ b/source/core/types/ut_path_item.tpb
@@ -0,0 +1,42 @@
+create or replace noneditionable type body ut_path_item as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+ constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, object_name varchar2,procedure_name varchar2) return self as result is
+ begin
+ self.schema_name := schema_name;
+ self.object_name := object_name;
+ self.procedure_name := procedure_name;
+ return;
+ end;
+
+ constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2,suite_path varchar2) return self as result is
+ begin
+ self.schema_name := schema_name;
+ self.suite_path := suite_path;
+ return;
+ end;
+
+ constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, object_name varchar2,procedure_name varchar2,suite_path varchar2) return self as result is
+ begin
+ self.schema_name := schema_name;
+ self.object_name := object_name;
+ self.procedure_name := procedure_name;
+ self.suite_path := suite_path;
+ return;
+ end;
+end;
+/
diff --git a/source/core/types/ut_path_item.tps b/source/core/types/ut_path_item.tps
new file mode 100644
index 000000000..fdc1e8ac2
--- /dev/null
+++ b/source/core/types/ut_path_item.tps
@@ -0,0 +1,26 @@
+create or replace noneditionable type ut_path_item as object (
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+ schema_name varchar2(4000),
+ object_name varchar2(250),
+ procedure_name varchar2(250),
+ suite_path varchar2(4000),
+ constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, object_name varchar2,procedure_name varchar2) return self as result,
+ constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, suite_path varchar2) return self as result,
+ constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, object_name varchar2,procedure_name varchar2,suite_path varchar2) return self as result
+)
+/
diff --git a/source/core/types/ut_path_items.tps b/source/core/types/ut_path_items.tps
new file mode 100644
index 000000000..64f8dd5fe
--- /dev/null
+++ b/source/core/types/ut_path_items.tps
@@ -0,0 +1,19 @@
+create or replace noneditionable type ut_path_items as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+ table of ut_path_item
+/
diff --git a/source/core/types/ut_reporter_base.tpb b/source/core/types/ut_reporter_base.tpb
index 94d518af4..e457645f4 100644
--- a/source/core/types/ut_reporter_base.tpb
+++ b/source/core/types/ut_reporter_base.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_reporter_base is
+create or replace noneditionable type body ut_reporter_base is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -165,52 +165,54 @@ create or replace type body ut_reporter_base is
end;
overriding member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) is
+ /* Resolves issue with ORA-21779 being thrown see issue: 1309 -> ( https://github.com/utPLSQL/utPLSQL/issues/1309#issuecomment-4020289898 ) */
+ l_event_item ut_event_item := a_event_item;
begin
case a_event_name
when ut_event_manager.gc_initialize
- then self.on_initialize(treat(a_event_item as ut_run));
+ then self.on_initialize(treat(l_event_item as ut_run));
when ut_event_manager.gc_before_run
- then self.before_calling_run(treat(a_event_item as ut_run));
+ then self.before_calling_run(treat(l_event_item as ut_run));
when ut_event_manager.gc_before_suite
- then self.before_calling_suite(treat(a_event_item as ut_logical_suite));
+ then self.before_calling_suite(treat(l_event_item as ut_logical_suite));
when ut_event_manager.gc_before_before_all
- then self.before_calling_before_all(treat(a_event_item as ut_executable));
+ then self.before_calling_before_all(treat(l_event_item as ut_executable));
when ut_event_manager.gc_before_before_each
- then self.before_calling_before_each(treat(a_event_item as ut_executable));
+ then self.before_calling_before_each(treat(l_event_item as ut_executable));
when ut_event_manager.gc_before_test
- then self.before_calling_test(treat(a_event_item as ut_test));
+ then self.before_calling_test(treat(l_event_item as ut_test));
when ut_event_manager.gc_before_before_test
- then self.before_calling_before_test(treat(a_event_item as ut_executable));
+ then self.before_calling_before_test(treat(l_event_item as ut_executable));
when ut_event_manager.gc_before_test_execute
- then self.before_calling_test_execute(treat(a_event_item as ut_executable));
+ then self.before_calling_test_execute(treat(l_event_item as ut_executable));
when ut_event_manager.gc_before_after_test
- then self.before_calling_after_test(treat(a_event_item as ut_executable));
+ then self.before_calling_after_test(treat(l_event_item as ut_executable));
when ut_event_manager.gc_before_after_each
- then self.before_calling_after_each(treat(a_event_item as ut_executable));
+ then self.before_calling_after_each(treat(l_event_item as ut_executable));
when ut_event_manager.gc_before_after_all
- then self.before_calling_after_all(treat(a_event_item as ut_executable));
+ then self.before_calling_after_all(treat(l_event_item as ut_executable));
when ut_event_manager.gc_after_run
- then self.after_calling_run(treat(a_event_item as ut_run));
+ then self.after_calling_run(treat(l_event_item as ut_run));
when ut_event_manager.gc_after_suite
- then self.after_calling_suite(treat(a_event_item as ut_logical_suite));
+ then self.after_calling_suite(treat(l_event_item as ut_logical_suite));
when ut_event_manager.gc_after_before_all
- then self.after_calling_before_all(treat(a_event_item as ut_executable));
+ then self.after_calling_before_all(treat(l_event_item as ut_executable));
when ut_event_manager.gc_after_before_each
- then self.after_calling_before_each(treat(a_event_item as ut_executable));
+ then self.after_calling_before_each(treat(l_event_item as ut_executable));
when ut_event_manager.gc_after_test
- then self.after_calling_test(treat(a_event_item as ut_test));
+ then self.after_calling_test(treat(l_event_item as ut_test));
when ut_event_manager.gc_after_before_test
- then self.after_calling_before_test(treat(a_event_item as ut_executable));
+ then self.after_calling_before_test(treat(l_event_item as ut_executable));
when ut_event_manager.gc_after_test_execute
- then self.after_calling_test_execute(treat(a_event_item as ut_executable));
+ then self.after_calling_test_execute(treat(l_event_item as ut_executable));
when ut_event_manager.gc_after_after_test
- then self.after_calling_after_test(treat(a_event_item as ut_executable));
+ then self.after_calling_after_test(treat(l_event_item as ut_executable));
when ut_event_manager.gc_after_after_each
- then self.after_calling_after_each(treat(a_event_item as ut_executable));
+ then self.after_calling_after_each(treat(l_event_item as ut_executable));
when ut_event_manager.gc_after_after_all
- then self.after_calling_after_all(treat(a_event_item as ut_executable));
+ then self.after_calling_after_all(treat(l_event_item as ut_executable));
when ut_event_manager.gc_finalize
- then self.on_finalize(treat(a_event_item as ut_run));
+ then self.on_finalize(treat(l_event_item as ut_run));
else null;
end case;
end;
diff --git a/source/core/types/ut_reporter_base.tps b/source/core/types/ut_reporter_base.tps
index 36af70776..af0472dc5 100644
--- a/source/core/types/ut_reporter_base.tps
+++ b/source/core/types/ut_reporter_base.tps
@@ -1,7 +1,7 @@
-create or replace type ut_reporter_base under ut_event_listener (
+create or replace noneditionable type ut_reporter_base under ut_event_listener (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_reporter_info.tps b/source/core/types/ut_reporter_info.tps
index d1f159211..29c7559f1 100644
--- a/source/core/types/ut_reporter_info.tps
+++ b/source/core/types/ut_reporter_info.tps
@@ -1,7 +1,7 @@
-create or replace type ut_reporter_info as object (
+create or replace noneditionable type ut_reporter_info as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_reporters.tps b/source/core/types/ut_reporters.tps
index b16e74653..fba22480f 100644
--- a/source/core/types/ut_reporters.tps
+++ b/source/core/types/ut_reporters.tps
@@ -1,7 +1,7 @@
-create or replace type ut_reporters as
+create or replace noneditionable type ut_reporters as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_reporters_info.tps b/source/core/types/ut_reporters_info.tps
index 6e9a866ef..4f6f054a3 100644
--- a/source/core/types/ut_reporters_info.tps
+++ b/source/core/types/ut_reporters_info.tps
@@ -1,7 +1,7 @@
-create or replace type ut_reporters_info as
+create or replace noneditionable type ut_reporters_info as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_results_counter.tpb b/source/core/types/ut_results_counter.tpb
index 0007acd9b..54f9e0b48 100644
--- a/source/core/types/ut_results_counter.tpb
+++ b/source/core/types/ut_results_counter.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_results_counter as
+create or replace noneditionable type body ut_results_counter as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_results_counter.tps b/source/core/types/ut_results_counter.tps
index 1b26b673a..477d4b217 100644
--- a/source/core/types/ut_results_counter.tps
+++ b/source/core/types/ut_results_counter.tps
@@ -1,7 +1,7 @@
-create or replace type ut_results_counter as object(
+create or replace noneditionable type ut_results_counter as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_run.tpb b/source/core/types/ut_run.tpb
index 395b164f0..80621b5ad 100644
--- a/source/core/types/ut_run.tpb
+++ b/source/core/types/ut_run.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_run as
+create or replace noneditionable type body ut_run as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ create or replace type body ut_run as
a_test_file_mappings ut_file_mappings := null,
a_client_character_set varchar2 := null,
a_random_test_order_seed positive := null,
- a_run_tags ut_varchar2_rows := null
+ a_run_tags varchar2 := null
) return self as result is
begin
self.run_paths := a_run_paths;
@@ -39,7 +39,7 @@ create or replace type body ut_run as
return;
end;
- overriding member procedure mark_as_skipped(self in out nocopy ut_run) is
+ overriding member procedure mark_as_skipped(self in out nocopy ut_run,a_skip_reason in varchar2) is
begin
null;
end;
diff --git a/source/core/types/ut_run.tps b/source/core/types/ut_run.tps
index 617347b25..551e56ac4 100644
--- a/source/core/types/ut_run.tps
+++ b/source/core/types/ut_run.tps
@@ -1,7 +1,7 @@
-create or replace type ut_run under ut_suite_item (
+create or replace noneditionable type ut_run under ut_suite_item (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ create or replace type ut_run under ut_suite_item (
project_name varchar2(4000),
items ut_suite_items,
run_paths ut_varchar2_list,
- run_tags ut_varchar2_rows,
+ run_tags varchar2(4000),
coverage_options ut_coverage_options,
test_file_mappings ut_file_mappings,
client_character_set varchar2(100),
@@ -34,9 +34,9 @@ create or replace type ut_run under ut_suite_item (
a_test_file_mappings ut_file_mappings := null,
a_client_character_set varchar2 := null,
a_random_test_order_seed positive := null,
- a_run_tags ut_varchar2_rows := null
+ a_run_tags varchar2 := null
) return self as result,
- overriding member procedure mark_as_skipped(self in out nocopy ut_run),
+ overriding member procedure mark_as_skipped(self in out nocopy ut_run,a_skip_reason in varchar2),
overriding member function do_execute(self in out nocopy ut_run) return boolean,
overriding member procedure set_rollback_type(self in out nocopy ut_run, a_rollback_type integer, a_force boolean := false),
overriding member procedure calc_execution_result(self in out nocopy ut_run),
diff --git a/source/core/types/ut_run_info.tpb b/source/core/types/ut_run_info.tpb
index 8e06d6137..3de74b2b4 100644
--- a/source/core/types/ut_run_info.tpb
+++ b/source/core/types/ut_run_info.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_run_info as
+create or replace noneditionable type body ut_run_info as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -20,27 +20,27 @@ create or replace type body ut_run_info as
begin
self.self_type := $$plsql_unit;
execute immediate
- 'select '||l_ut_owner||'.ut.version() from dual'
+ 'select /*+ no_parallel */ '||l_ut_owner||'.ut.version() from dual'
into self.ut_version;
dbms_utility.db_version( self.db_version, self.db_compatibility );
db_os_type := dbms_utility.port_string();
execute immediate
- 'select '||l_ut_owner||'.ut_key_value_pair(x.product, x.version) from product_component_version x'
+ 'select /*+ no_parallel */ '||l_ut_owner||'.ut_key_value_pair(x.product, x.version) from product_component_version x'
bulk collect into self.db_component_version;
execute immediate
- 'select '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value)
+ 'select /*+ no_parallel */ '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value)
from nls_session_parameters x'
bulk collect into self.nls_session_params;
execute immediate
- 'select '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_instance_parameters x'
+ 'select /*+ no_parallel */ '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_instance_parameters x'
bulk collect into self.nls_instance_params;
execute immediate
- 'select '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_database_parameters x'
+ 'select /*+ no_parallel */ '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_database_parameters x'
bulk collect into self.nls_db_params;
return;
end;
diff --git a/source/core/types/ut_run_info.tps b/source/core/types/ut_run_info.tps
index f59c22c05..00919b172 100644
--- a/source/core/types/ut_run_info.tps
+++ b/source/core/types/ut_run_info.tps
@@ -1,7 +1,7 @@
-create or replace type ut_run_info under ut_event_item (
+create or replace noneditionable type ut_run_info under ut_event_item (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_stack.tpb b/source/core/types/ut_stack.tpb
new file mode 100644
index 000000000..4298ae35c
--- /dev/null
+++ b/source/core/types/ut_stack.tpb
@@ -0,0 +1,58 @@
+create or replace noneditionable type body ut_stack as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ constructor function ut_stack( self in out nocopy ut_stack) return self as result is
+ begin
+ self.tokens := ut_varchar2_list();
+ self.top := 0;
+ return;
+ end ut_stack;
+
+ member function peek(self in out nocopy ut_stack) return varchar2 is
+ l_token varchar2(32767);
+ begin
+ if self.tokens.count =0 or self.tokens is null then
+ l_token := null;
+ else
+ l_token := self.tokens(self.tokens.last);
+ end if;
+ return l_token;
+ end;
+
+ member procedure push(self in out nocopy ut_stack, a_token varchar2) is
+ begin
+ self.tokens.extend;
+ self.tokens(self.tokens.last) := a_token;
+ self.top := self.tokens.count;
+ end push;
+
+ member procedure pop(self in out nocopy ut_stack,a_cnt in integer default 1) is
+ begin
+ self.tokens.trim(a_cnt);
+ self.top := self.tokens.count;
+ end pop;
+
+ member function pop(self in out nocopy ut_stack) return varchar2 is
+ l_token varchar2(32767) := self.tokens(self.tokens.last);
+ begin
+ self.pop();
+ return l_token;
+ end;
+end;
+/
+
diff --git a/source/core/types/ut_stack.tps b/source/core/types/ut_stack.tps
new file mode 100644
index 000000000..ed79bea55
--- /dev/null
+++ b/source/core/types/ut_stack.tps
@@ -0,0 +1,26 @@
+create or replace noneditionable type ut_stack as object (
+ top integer,
+ tokens ut_varchar2_list,
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+ constructor function ut_stack( self in out nocopy ut_stack) return self as result,
+ member function peek(self in out nocopy ut_stack) return varchar2,
+ member procedure push(self in out nocopy ut_stack, a_token varchar2),
+ member procedure pop(self in out nocopy ut_stack,a_cnt in integer default 1),
+ member function pop(self in out nocopy ut_stack) return varchar2
+)
+/
\ No newline at end of file
diff --git a/source/core/types/ut_suite.tpb b/source/core/types/ut_suite.tpb
index baaaf044d..b4e3eabf0 100644
--- a/source/core/types/ut_suite.tpb
+++ b/source/core/types/ut_suite.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_suite as
+create or replace noneditionable type body ut_suite as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ create or replace type body ut_suite as
ut_utils.debug_log('ut_suite.execute');
if self.get_disabled_flag() then
- self.mark_as_skipped();
+ self.mark_as_skipped(a_skip_reason => self.disabled_reason);
else
self.start_time := current_timestamp;
ut_event_manager.trigger_event(ut_event_manager.gc_before_suite, self);
diff --git a/source/core/types/ut_suite.tps b/source/core/types/ut_suite.tps
index ffe2d2d42..edce778f1 100644
--- a/source/core/types/ut_suite.tps
+++ b/source/core/types/ut_suite.tps
@@ -1,7 +1,7 @@
-create or replace type ut_suite under ut_logical_suite (
+create or replace noneditionable type ut_suite under ut_logical_suite (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_suite_cache_row.tps b/source/core/types/ut_suite_cache_row.tps
index d9f542de3..1ee371a88 100644
--- a/source/core/types/ut_suite_cache_row.tps
+++ b/source/core/types/ut_suite_cache_row.tps
@@ -1,7 +1,7 @@
-create type ut_suite_cache_row as object (
+create or replace noneditionable type ut_suite_cache_row as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ create type ut_suite_cache_row as object (
description varchar2(4000 byte),
rollback_type number,
disabled_flag number,
+ disabled_reason varchar2(4000 byte),
warnings ut_varchar2_rows,
before_all_list ut_executables,
after_all_list ut_executables,
diff --git a/source/core/types/ut_suite_cache_rows.tps b/source/core/types/ut_suite_cache_rows.tps
index 660f934cf..84bcc9f75 100644
--- a/source/core/types/ut_suite_cache_rows.tps
+++ b/source/core/types/ut_suite_cache_rows.tps
@@ -1,7 +1,7 @@
-create type ut_suite_cache_rows as
+create or replace noneditionable type ut_suite_cache_rows as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_suite_context.tpb b/source/core/types/ut_suite_context.tpb
index 3669d3d7e..dad24e42e 100644
--- a/source/core/types/ut_suite_context.tpb
+++ b/source/core/types/ut_suite_context.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_suite_context as
+create or replace noneditionable type body ut_suite_context as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_suite_context.tps b/source/core/types/ut_suite_context.tps
index a328e7657..b890e2633 100644
--- a/source/core/types/ut_suite_context.tps
+++ b/source/core/types/ut_suite_context.tps
@@ -1,7 +1,7 @@
-create or replace type ut_suite_context under ut_suite (
+create or replace noneditionable type ut_suite_context under ut_suite (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_suite_item.tpb b/source/core/types/ut_suite_item.tpb
index 9b939727a..8e8c6fd90 100644
--- a/source/core/types/ut_suite_item.tpb
+++ b/source/core/types/ut_suite_item.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_suite_item as
+create or replace noneditionable type body ut_suite_item as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ create or replace type body ut_suite_item as
self.line_no := a_line_no;
self.transaction_invalidators := ut_varchar2_list();
self.disabled_flag := ut_utils.boolean_to_int(false);
+ self.disabled_reason := null;
end;
member function get_disabled_flag return boolean is
diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps
index 87f577459..2e990353f 100644
--- a/source/core/types/ut_suite_item.tps
+++ b/source/core/types/ut_suite_item.tps
@@ -1,7 +1,7 @@
-create or replace type ut_suite_item force under ut_event_item (
+create or replace noneditionable type ut_suite_item force under ut_event_item (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -46,6 +46,10 @@ create or replace type ut_suite_item force under ut_event_item (
*/
disabled_flag integer(1),
/**
+ * Indicates reason whysa test is to be disabled by execution
+ */
+ disabled_reason varchar2(4000),
+ /**
* Line no where annotation identifying this item is placed in package
*/
line_no integer,
@@ -66,7 +70,7 @@ create or replace type ut_suite_item force under ut_event_item (
tags ut_varchar2_rows,
member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_line_no integer),
member function get_disabled_flag return boolean,
- not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item),
+ not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item, a_skip_reason in varchar2),
member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer, a_force boolean := false),
member function get_rollback_type return integer,
member function create_savepoint_if_needed return varchar2,
diff --git a/source/core/types/ut_suite_items.tps b/source/core/types/ut_suite_items.tps
index e80bcc7b4..b7ce9873c 100644
--- a/source/core/types/ut_suite_items.tps
+++ b/source/core/types/ut_suite_items.tps
@@ -1,7 +1,7 @@
-create or replace type ut_suite_items as
+create or replace noneditionable type ut_suite_items as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb
index 4ed3f665e..61a1e39f8 100644
--- a/source/core/types/ut_test.tpb
+++ b/source/core/types/ut_test.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_test as
+create or replace noneditionable type body ut_test as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -35,11 +35,12 @@ create or replace type body ut_test as
return;
end;
- overriding member procedure mark_as_skipped(self in out nocopy ut_test) is
+ overriding member procedure mark_as_skipped(self in out nocopy ut_test, a_skip_reason in varchar2) is
begin
ut_event_manager.trigger_event(ut_event_manager.gc_before_test, self);
self.start_time := current_timestamp;
self.result := ut_utils.gc_disabled;
+ self.disabled_reason := coalesce(a_skip_reason,self.disabled_reason);
ut_utils.debug_log('ut_test.execute - disabled');
self.results_count.set_counter_values(self.result);
self.end_time := self.start_time;
@@ -54,7 +55,7 @@ create or replace type body ut_test as
ut_utils.debug_log('ut_test.execute');
if self.get_disabled_flag() then
- mark_as_skipped();
+ mark_as_skipped(self.disabled_reason);
else
self.start_time := current_timestamp;
ut_event_manager.trigger_event(ut_event_manager.gc_before_test, self);
diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps
index 85caf7380..cb50c2fd4 100644
--- a/source/core/types/ut_test.tps
+++ b/source/core/types/ut_test.tps
@@ -1,7 +1,7 @@
-create or replace type ut_test force under ut_suite_item (
+create or replace noneditionable type ut_test force under ut_suite_item (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ create or replace type ut_test force under ut_suite_item (
self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2,
a_line_no integer, a_expected_error_codes ut_varchar2_rows := null, a_tags ut_varchar2_rows := null
) return self as result,
- overriding member procedure mark_as_skipped(self in out nocopy ut_test),
+ overriding member procedure mark_as_skipped(self in out nocopy ut_test, a_skip_reason in varchar2),
overriding member function do_execute(self in out nocopy ut_test) return boolean,
overriding member procedure calc_execution_result(self in out nocopy ut_test),
overriding member procedure mark_as_errored(self in out nocopy ut_test, a_error_stack_trace varchar2),
diff --git a/source/core/types/ut_varchar2_list.tps b/source/core/types/ut_varchar2_list.tps
index dd15f0d8f..8203a4bab 100644
--- a/source/core/types/ut_varchar2_list.tps
+++ b/source/core/types/ut_varchar2_list.tps
@@ -1,7 +1,7 @@
-create or replace type ut_varchar2_list as
+create or replace noneditionable type ut_varchar2_list as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/types/ut_varchar2_rows.tps b/source/core/types/ut_varchar2_rows.tps
index d56eeee46..a2e25e9fb 100644
--- a/source/core/types/ut_varchar2_rows.tps
+++ b/source/core/types/ut_varchar2_rows.tps
@@ -1,7 +1,7 @@
-create or replace type ut_varchar2_rows as
+create or replace noneditionable type ut_varchar2_rows as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/ut_dbms_output_cache.sql b/source/core/ut_dbms_output_cache.sql
index 1fe4251c7..802f30942 100644
--- a/source/core/ut_dbms_output_cache.sql
+++ b/source/core/ut_dbms_output_cache.sql
@@ -1,15 +1,16 @@
/*
-utPLSQL - Version 3
-Copyright 2016 - 2019 utPLSQL Project
-Licensed under the Apache License, Version 2.0 (the "License"):
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
*/
/*
* This table is not a global temporary table as it needs to allow cross-session data exchange
@@ -19,7 +20,7 @@ limitations under the License.
declare
l_tab_exist number;
begin
- select count(*) into l_tab_exist from
+ select /*+ no_parallel */ count(*) into l_tab_exist from
(select table_name from all_tables where table_name = 'UT_DBMS_OUTPUT_CACHE' and owner = sys_context('USERENV','CURRENT_SCHEMA')
union all
select synonym_name from all_synonyms where synonym_name = 'UT_DBMS_OUTPUT_CACHE' and owner = sys_context('USERENV','CURRENT_SCHEMA'));
diff --git a/source/core/ut_expectation_processor.pkb b/source/core/ut_expectation_processor.pkb
index d65d210c8..3fb39a5b9 100644
--- a/source/core/ut_expectation_processor.pkb
+++ b/source/core/ut_expectation_processor.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_expectation_processor as
+create or replace noneditionable package body ut_expectation_processor as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -104,7 +104,7 @@ create or replace package body ut_expectation_processor as
function get_session_parameters return tt_nls_params is
l_session_params tt_nls_params;
begin
- select nsp.parameter, nsp.value
+ select /*+ no_parallel */ nsp.parameter, nsp.value
bulk collect into l_session_params
from nls_session_parameters nsp
where parameter
@@ -159,17 +159,17 @@ create or replace package body ut_expectation_processor as
-- when 11g and 12c reports only package name
function cut_header_and_expectations( a_stack varchar2 ) return varchar2 is
begin
- return regexp_substr( a_stack, '(.*\.(UT_EXPECTATION[A-Z0-9#_$]*|UT|UTASSERT2?)(\.[A-Z0-9#_$]+)?\s+)+((.|\s)*)', 1, 1, 'm', 4);
+ return regexp_substr( a_stack, '(.*\.(UT_EQUAL|UT_BE_WITHIN[[:alnum:]$#_]*|UT_EXPECTATION[[:alnum:]$#_]*|UT|UTASSERT2?)(\.[[:alnum:]$#_]+)?\s+)+((.|\s)*)', 1, 1, 'm', 4);
end;
function cut_address_columns( a_stack varchar2 ) return varchar2 is
begin
- return regexp_replace( a_stack, '^(0x)?[0-9a-f]+\s+', '', 1, 0, 'mi' );
+ return regexp_replace( a_stack, '^(0x)?[[:digit:]abcdef]+\s+', '', 1, 0, 'mi' );
end;
function cut_framework_stack( a_stack varchar2 ) return varchar2 is
begin
return regexp_replace(
a_stack,
- '[0-9]+\s+anonymous\s+block\s+[0-9]+\s+package\s+body\s+sys\.dbms_sql(\.execute)?\s+[0-9]+\s+[0-9_$#a-z ]+\.ut_executable.*',
+ '[0-9]+\s+anonymous\s+block\s+[0-9]+\s+package\s+body\s+sys\.dbms_sql(\.execute)?\s+[0-9]+\s+[[:alnum:]_$# ]+\.ut_executable.*',
'',
1, 1, 'mni'
);
@@ -178,7 +178,7 @@ create or replace package body ut_expectation_processor as
begin
return regexp_replace(
a_stack,
- '([0-9]+)\s+(.* )?((anonymous block)|(([0-9_$#a-z]+\.[0-9_$#a-z]+(\.([0-9_$#a-z])+)?)))',
+ '([0-9]+)\s+(.* )?((anonymous block)|(([[:alnum:]$#_]+\.[[:alnum:]$#_]+(\.([[:alnum:]$#_])+)?)))',
'at "\3", line \1', 1, 0, 'i'
);
end;
@@ -190,8 +190,8 @@ create or replace package body ut_expectation_processor as
l_caller_stack_line := regexp_substr(l_call_stack,'^(.*)');
if l_caller_stack_line like '%.%' then
l_line_no := to_number( regexp_substr( l_caller_stack_line, ', line (\d+)', subexpression => 1 ) );
- l_owner := regexp_substr( l_caller_stack_line, 'at "([A-Za-z0-9$#_]+)\.(([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?)", line (\d+)', subexpression => 1 );
- l_object_name := regexp_substr( l_caller_stack_line, 'at "([A-Za-z0-9$#_]+)\.(([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?)", line (\d+)', subexpression => 3 );
+ l_owner := regexp_substr( l_caller_stack_line, 'at "([[:alnum:]$#_]+)\.(([[:alnum:]$#_]+)(\.([[:alnum:]$#_]+))?)", line (\d+)', subexpression => 1 );
+ l_object_name := regexp_substr( l_caller_stack_line, 'at "([[:alnum:]$#_]+)\.(([[:alnum:]$#_]+)(\.([[:alnum:]$#_]+))?)", line (\d+)', subexpression => 3 );
l_result :=
l_caller_stack_line || ' ' || rtrim(ut_metadata.get_source_definition_line(l_owner, l_object_name, l_line_no),chr(10))
|| replace( l_call_stack, l_caller_stack_line );
diff --git a/source/core/ut_expectation_processor.pks b/source/core/ut_expectation_processor.pks
index cfcae8b42..5349ee70a 100644
--- a/source/core/ut_expectation_processor.pks
+++ b/source/core/ut_expectation_processor.pks
@@ -1,7 +1,7 @@
-create or replace package ut_expectation_processor authid current_user as
+create or replace noneditionable package ut_expectation_processor authid current_user as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/ut_file_mapper.pkb b/source/core/ut_file_mapper.pkb
index adecb7dc8..40204a5da 100644
--- a/source/core/ut_file_mapper.pkb
+++ b/source/core/ut_file_mapper.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_file_mapper is
+create or replace noneditionable package body ut_file_mapper is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/ut_file_mapper.pks b/source/core/ut_file_mapper.pks
index 4ed670ee5..1363e81ec 100644
--- a/source/core/ut_file_mapper.pks
+++ b/source/core/ut_file_mapper.pks
@@ -1,7 +1,7 @@
-create or replace package ut_file_mapper authid current_user is
+create or replace noneditionable package ut_file_mapper authid current_user is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/ut_metadata.pkb b/source/core/ut_metadata.pkb
index 727dd6318..d9939ad45 100644
--- a/source/core/ut_metadata.pkb
+++ b/source/core/ut_metadata.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_metadata as
+create or replace noneditionable package body ut_metadata as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ create or replace package body ut_metadata as
l_view_name varchar2(200) := get_objects_view_name;
begin
- execute immediate q'[select count(*)
+ execute immediate q'[select /*+ no_parallel */ count(*)
from ]'||l_view_name||q'[
where owner = :a_owner_name
and object_name = :a_package_name
@@ -58,7 +58,7 @@ create or replace package body ut_metadata as
l_view_name varchar2(200) := get_dba_view('dba_procedures');
begin
execute immediate
- 'select count(*) from '||l_view_name
+ 'select /*+ no_parallel */ count(*) from '||l_view_name
||' where owner = :l_schema and object_name = :l_package_name and procedure_name = :l_procedure_name and rownum = 1'
into l_cnt using a_owner_name, a_package_name, a_procedure_name;
@@ -77,7 +77,7 @@ create or replace package body ut_metadata as
if not nvl(c_key = g_cached_object, false) then
g_cached_object := c_key;
execute immediate
- 'select trim(text) text
+ 'select /*+ no_parallel */ trim(text) text
from '||l_view_name||q'[ s
where s.owner = :a_owner
and s.name = :a_object_name
@@ -123,7 +123,7 @@ create or replace package body ut_metadata as
function user_has_execute_any_proc return boolean is
l_has_execute_any varchar2(1);
begin
- select decode( count( 1 ), 0, 'N', 'Y' )
+ select /*+ no_parallel */ decode( count( 1 ), 0, 'N', 'Y' )
into l_has_execute_any
from dual
where
@@ -157,7 +157,7 @@ create or replace package body ut_metadata as
l_cnt number;
c_current_schema constant all_tables.owner%type := sys_context('USERENV','CURRENT_SCHEMA');
begin
- select count(*)
+ select /*+ no_parallel */ count(*)
into l_cnt
from all_objects t
where t.object_name = a_object_name
@@ -304,7 +304,7 @@ create or replace package body ut_metadata as
begin
l_result := regexp_substr(
a_full_object_name,
- '^([A-Za-z0-9$#_]+|".*?")\.([A-Za-z0-9$#_]+|".*?")', subexpression => 2
+ '^([[:alnum:]$#_]+|".*?")\.([[:alnum:]$#_]+|".*?")', subexpression => 2
);
if not l_result like '"%"' then
l_result := upper(l_result);
diff --git a/source/core/ut_metadata.pks b/source/core/ut_metadata.pks
index bf62c4e52..42f136504 100644
--- a/source/core/ut_metadata.pks
+++ b/source/core/ut_metadata.pks
@@ -1,7 +1,7 @@
-create or replace package ut_metadata authid current_user as
+create or replace noneditionable package ut_metadata authid current_user as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/ut_savepoint_seq.sql b/source/core/ut_savepoint_seq.sql
index b87d18e6d..2026ab0e3 100644
--- a/source/core/ut_savepoint_seq.sql
+++ b/source/core/ut_savepoint_seq.sql
@@ -1,7 +1,7 @@
create sequence ut_savepoint_seq
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb
index d13cbaed9..4f0e28606 100644
--- a/source/core/ut_suite_builder.pkb
+++ b/source/core/ut_suite_builder.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_suite_builder is
+create or replace noneditionable package body ut_suite_builder is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -355,6 +355,7 @@ create or replace package body ut_suite_builder is
l_annotation_texts tt_annotation_texts;
l_proc_annotations tt_annotations_by_name := a_annotations.by_proc(a_procedure_name);
begin
+
if not l_proc_annotations.exists(gc_test) then
return;
end if;
@@ -412,6 +413,12 @@ create or replace package body ut_suite_builder is
end if;
l_test.disabled_flag := ut_utils.boolean_to_int( l_proc_annotations.exists( gc_disabled));
+ if l_proc_annotations.exists(gc_disabled) then
+ l_annotation_texts := l_proc_annotations( gc_disabled);
+ --take the last definition if more than one was provided
+ l_test.disabled_reason := l_annotation_texts(l_annotation_texts.first);
+ end if;
+
a_suite_items.extend;
a_suite_items( a_suite_items.last ) := l_test;
@@ -560,8 +567,12 @@ create or replace package body ut_suite_builder is
if a_annotations.by_name.exists(gc_tags) then
add_tags_to_suite_item(a_suite, a_annotations.by_name(gc_tags),a_suite.tags);
end if;
+
a_suite.disabled_flag := ut_utils.boolean_to_int(a_annotations.by_name.exists(gc_disabled));
-
+ if a_annotations.by_name.exists(gc_disabled) then
+ a_suite.disabled_reason := a_annotations.by_name(gc_disabled)(a_annotations.by_name(gc_disabled).first);
+ end if;
+
--process procedure annotations for suite
get_annotated_procedures(a_annotations, a_suite, a_suite_items, l_before_each_list, l_after_each_list, l_before_all_list, l_after_all_list);
@@ -743,7 +754,7 @@ create or replace package body ut_suite_builder is
l_used_context_names(l_context_name) := true;
l_context := ut_suite_context(a_parent.object_owner, a_parent.object_name, l_context_name, l_context_pos );
- l_context.path := a_parent.path||'.'||l_context_name;
+ l_context.path := a_parent.path||'.'||l_context.name;
l_context.description := coalesce( a_annotations.by_line( l_context_pos ).text, l_context_name );
l_context.parse_time := a_annotations.parse_time;
diff --git a/source/core/ut_suite_builder.pks b/source/core/ut_suite_builder.pks
index e982fdbc1..35f207ea4 100644
--- a/source/core/ut_suite_builder.pks
+++ b/source/core/ut_suite_builder.pks
@@ -1,7 +1,7 @@
-create or replace package ut_suite_builder authid current_user is
+create or replace noneditionable package ut_suite_builder authid current_user is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/core/ut_suite_cache.sql b/source/core/ut_suite_cache.sql
index a7f998f83..1d7389e9f 100644
--- a/source/core/ut_suite_cache.sql
+++ b/source/core/ut_suite_cache.sql
@@ -1,7 +1,7 @@
create table ut_suite_cache
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/ut_suite_cache_manager.pkb b/source/core/ut_suite_cache_manager.pkb
index 06c4c76a5..9aa3cf63d 100644
--- a/source/core/ut_suite_cache_manager.pkb
+++ b/source/core/ut_suite_cache_manager.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_suite_cache_manager is
+create or replace noneditionable package body ut_suite_cache_manager is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -19,26 +19,16 @@ create or replace package body ut_suite_cache_manager is
/*
* Private code
*/
-
- gc_get_cache_suite_sql constant varchar2(32767) :=
- q'[with
+ cursor c_get_bulk_cache_suite(cp_suite_items in ut_suite_cache_rows) is
+ with
suite_items as (
select /*+ cardinality(c 500) */ value(c) as obj
- from ut_suite_cache c
- where 1 = 1
- and c.object_owner = :l_object_owner
- and ( {:path:}
- and {:object_name:}
- and {:procedure_name:}
- )
- )
- ),
- {:tags:}
+ from table(cp_suite_items) c),
suitepaths as (
select distinct substr(c.obj.path,1,instr(c.obj.path,'.',-1)-1) as suitepath,
c.obj.path as path,
c.obj.object_owner as object_owner
- from {:suite_item_name:} c
+ from suite_items c
where c.obj.self_type = 'UT_SUITE'
),
gen as (
@@ -67,21 +57,16 @@ create or replace package body ut_suite_cache_manager is
select ut_suite_cache_row(
null,
s.self_type, s.path, s.object_owner, s.object_name,
- s.object_name, null, null, null, null, 0,
+ s.object_name, null, null, null, null, 0,null,
ut_varchar2_rows(),
s.x, s.x, s.x, s.x, s.x, s.x,
s.y, null, s.z
) as obj
from logical_suite_data s
- ),
- items as (
- select obj from {:suite_item_name:}
- union all
- select obj from logical_suites
)
- select c.obj
- from items c
- order by c.obj.object_owner,{:random_seed:}]';
+ select /*+ no_parallel */ obj from suite_items
+ union all
+ select /*+ no_parallel */ obj from logical_suites;
function get_missing_cache_objects(a_object_owner varchar2) return ut_varchar2_rows is
l_result ut_varchar2_rows;
@@ -89,7 +74,7 @@ create or replace package body ut_suite_cache_manager is
begin
l_data := ut_annotation_cache_manager.get_cached_objects_list(a_object_owner, 'PACKAGE');
- select i.object_name
+ select /*+ no_parallel */ i.object_name
bulk collect into l_result
from ut_suite_cache_package i
where not exists (
@@ -102,163 +87,208 @@ create or replace package body ut_suite_cache_manager is
return l_result;
end;
-
- function get_path_sql(a_path in varchar2) return varchar2 is
+ function group_paths_by_schema(a_paths ut_varchar2_list) return ut_path_items is
+ c_package_path_regex constant varchar2(100) := '^([[:alnum:]$#_]+)(\.([[:alnum:]$#_\*]+))?(\.([[:alnum:]$#_\*]+))?$';
+ l_results ut_path_items := ut_path_items();
+ l_path_item ut_path_item;
+ i pls_integer;
begin
- return case when a_path is not null then q'[
- :l_path||'.' like c.path || '.%' /*all parents and self*/
- or ( c.path||'.' like :l_path || '.%' /*all children and self*/
- ]'
- else ' :l_path is null and ( :l_path is null ' end;
- end;
-
- function get_object_name_sql(a_object_name in varchar2) return varchar2 is
- begin
- return case when a_object_name is not null
- then ' c.object_name = :a_object_name '
- else ' :a_object_name is null' end;
+ i := a_paths.first;
+ while (i is not null) loop
+ l_results.extend;
+ if a_paths(i) like '%:%' then
+ l_path_item := ut_path_item(schema_name => upper(regexp_substr(a_paths(i),'^[^.:]+')),
+ suite_path => ltrim(regexp_substr(a_paths(i),'[.:].*$'),':'));
+ l_results(l_results.last) := l_path_item;
+ else
+ l_path_item := ut_path_item(schema_name => regexp_substr(a_paths(i), c_package_path_regex, subexpression => 1),
+ object_name => regexp_substr(a_paths(i), c_package_path_regex, subexpression => 3),
+ procedure_name => regexp_substr(a_paths(i), c_package_path_regex, subexpression => 5));
+ l_results(l_results.last) := l_path_item;
+ end if;
+ i := a_paths.next(i);
+ end loop;
+
+ return l_results;
end;
-
- function get_procedure_name_sql(a_procedure_name in varchar2) return varchar2 is
- begin
- return case when a_procedure_name is not null
- then ' c.name = :a_procedure_name'
- else ' :a_procedure_name is null' end;
+
+ /*
+ First SQL queries for objects where procedure is null or its only wildcard.
+ We split that due to fact that we can use func min to combine rows.
+
+ Second union is responsible expanding paths where the procedure filter is given
+ We cannot select min here as filter can cover only half of tests within
+ package. Even if the filter doesnt return anything we still capture a proc filter
+ name for error reporting later on.
+
+ Third SQL cover scenario where a suitapath only is populated and wildcard is given
+
+ Fourth SQL cover scenario where suitepath is populated with no filters
+ */
+ function expand_paths(a_schema_paths ut_path_items) return ut_path_items is
+ l_schema_paths ut_path_items:= ut_path_items();
+ begin
+ with
+ schema_paths as (
+ select * from table(a_schema_paths)
+ ),
+ paths_for_object as (
+ select /*+ no_parallel */ min(path) as suite_path,sp.schema_name as schema_name,nvl(c.object_name,sp.object_name) as object_name,
+ null as procedure_name
+ from schema_paths sp left outer join ut_suite_cache c
+ on ( c.object_owner = upper(sp.schema_name)
+ and c.object_name like replace(upper(sp.object_name),'*','%'))
+ where sp.suite_path is null and sp.object_name is not null
+ and ( sp.procedure_name is null or sp.procedure_name = '*')
+ group by sp.schema_name,nvl(c.object_name,sp.object_name)
+ ),
+ paths_for_procedures as (
+ select /*+ no_parallel */ path as suite_path,sp.schema_name as schema_name,nvl(c.object_name,sp.object_name) as object_name,
+ nvl(c.name,sp.procedure_name) as procedure_name
+ from schema_paths sp left outer join ut_suite_cache c
+ on ( c.object_owner = upper(sp.schema_name)
+ and c.object_name like replace(upper(sp.object_name),'*','%')
+ and c.name like nvl(replace(upper(sp.procedure_name),'*','%'), c.name))
+ where sp.suite_path is null and sp.object_name is not null
+ and (sp.procedure_name is not null and sp.procedure_name != '*')
+ ),
+ paths_for_suite_path_with_ast as (
+ select /*+ no_parallel */ nvl(c.path,sp.suite_path) as suite_path,sp.schema_name,sp.object_name,sp.procedure_name as procedure_name
+ from schema_paths sp left outer join ut_suite_cache c on
+ ( c.object_owner = upper(sp.schema_name)
+ --and c.path like replace(sp.suite_path,'*','%'))
+ and regexp_like(c.path,'^'||replace(sp.suite_path,'*','[[:alnum:]$#_]*')))
+ where sp.suite_path is not null and instr(sp.suite_path,'*') > 0
+ ),
+ straigth_suite_paths as (
+ select /*+ no_parallel */ sp.suite_path as suite_path,sp.schema_name,sp.object_name,sp.procedure_name as procedure_name
+ from schema_paths sp
+ where
+ (sp.suite_path is not null and instr(sp.suite_path,'*') = 0)
+ or
+ (sp.suite_path is null and sp.object_name is null)
+ ),
+ all_suitepaths_together as (
+ select * from paths_for_object
+ union all
+ select * from paths_for_procedures
+ union all
+ select * from paths_for_suite_path_with_ast
+ union all
+ select * from straigth_suite_paths
+ )
+ select ut_path_item(schema_name,object_name,procedure_name,suite_path)
+ bulk collect into l_schema_paths
+ from
+ (select schema_name,object_name,procedure_name,suite_path,
+ row_number() over ( partition by schema_name,object_name,procedure_name,suite_path order by 1) as r_num
+ from all_suitepaths_together)
+ where r_num = 1 ;
+ return l_schema_paths;
end;
-
- function get_tags_sql(a_tags_count in integer) return varchar2 is
+
+ /*
+ Get a suite items rows that matching our criteria like
+ path,object_name etc.
+ We need to consider also an wildcard character on our procedures and object
+ names.
+ Were the path is populated we need to make sure we dont return duplicates
+ as the wildcard can produce multiple results from same path and
+ parents and child for each can be same resulting in duplicates
+ */
+ function get_suite_items (
+ a_schema_paths ut_path_items
+ ) return ut_suite_cache_rows is
+ l_suite_items ut_suite_cache_rows := ut_suite_cache_rows();
begin
- return case when a_tags_count > 0 then
- q'[included_tags as (
- select c.obj.path as path
- from suite_items c
- where c.obj.tags multiset intersect :a_include_tag_list is not empty or :a_include_tag_list is empty
- ),
- excluded_tags as (
- select c.obj.path as path
- from suite_items c
- where c.obj.tags multiset intersect :a_exclude_tag_list is not empty
- ),
- suite_items_tags as (
- select c.*
- from suite_items c
- where exists (
- select 1 from included_tags t
- where t.path||'.' like c.obj.path || '.%' /*all parents and self*/
- or c.obj.path||'.' like t.path || '.%' /*all children and self*/
- )
- and not exists (
- select 1 from excluded_tags t
- where c.obj.path||'.' like t.path || '.%' /*all children and self*/
- )
- ),]'
- else
- q'[dummy as (select 'x' from dual where :a_include_tag_list is null and :a_include_tag_list is null and :a_exclude_tag_list is null),]'
- end;
+ select obj bulk collect into l_suite_items
+ from (
+ select /*+ cardinality(c 500) */ value(c) as obj,row_number() over ( partition by path,object_owner order by path,object_owner asc) as r_num
+ from ut_suite_cache c,
+ table(a_schema_paths) sp
+ where c.object_owner = upper(sp.schema_name)
+ and ((sp.suite_path is not null and sp.suite_path||'.' like c.path||'.%' /*all parents and self*/
+ or
+ (
+ c.path||'.' like sp.suite_path||'.%' /*all children and self*/
+ and c.object_name like nvl(upper(sp.object_name),c.object_name)
+ and c.name like nvl(upper(sp.procedure_name),c.name)
+ ))
+ or
+ ( sp.suite_path is null
+ and c.object_name = nvl(upper(sp.object_name),c.object_name)
+ and c.name = nvl(upper(sp.procedure_name),c.name)))) where r_num =1;
+ return l_suite_items;
end;
-
- function get_random_seed_sql(a_random_seed positive) return varchar2 is
+
+ /*
+ We will sort a suites in hierarchical structure.
+ Sorting from bottom to top so when we consolidate
+ we will go in proper order.
+ For random seed we will add an extra sort that can be null.
+ The object owner is irrelevant on joing via path as we already
+ resolved a list of test we want to use so as long they share a suitepath
+ they are correct.
+ */
+ procedure sort_and_randomize_tests(
+ a_suite_rows in out ut_suite_cache_rows,
+ a_random_seed positive := null)
+ is
+ l_suite_rows ut_suite_cache_rows;
begin
- return case
- when a_random_seed is null then q'[
- nlssort(
- replace(
- /*suite path until objects name (excluding contexts and test path) with trailing dot (full stop)*/
- substr( c.obj.path, 1, instr( c.obj.path, lower(c.obj.object_name), -1 ) + length(c.obj.object_name) ),
- '.',
- /*'.' replaced with chr(0) to assure that child elements come before parent when sorting in descending order*/
- chr(0)
- ),
- 'nls_sort=binary'
- )desc nulls last,
- case when c.obj.self_type = 'UT_SUITE_CONTEXT' then
- ( select max( x.line_no ) + 1
- from ut_suite_cache x
- where c.obj.object_owner = x.object_owner
- and c.obj.object_name = x.object_name
- and x.path like c.obj.path || '.%'
- )
- else
- c.obj.line_no
- end,
- /*assures that child contexts come before parent contexts*/
- regexp_count(c.obj.path,'\.') desc,
- :a_random_seed]'
- else
- ' ut_runner.hash_suite_path(
- c.obj.path, :a_random_seed
- ) desc nulls last'
- end;
+ with
+ extract_parent_child as (
+ select s.path, substr(s.path,1,instr(s.path,'.',-1,1)-1) as parent_path,s.object_owner,
+ case when a_random_seed is null then s.line_no end line_no,
+ case when a_random_seed is not null then ut_utils.hash_suite_path(s.path, a_random_seed) end random_seed
+ from table(a_suite_rows) s),
+ t1(path,parent_path,object_owner,line_no,random_seed) as (
+ --Anchor member
+ select s.path, parent_path,s.object_owner,s.line_no,random_seed
+ from extract_parent_child s
+ where parent_path is null
+ union all
+ --Recursive member
+ select t2.path, t2.parent_path,t2.object_owner,t2.line_no,t2.random_seed
+ from t1,extract_parent_child t2
+ where t2.parent_path = t1.path
+ and t2.object_owner = t1.object_owner)
+ search depth first by line_no desc,random_seed desc nulls last set order1
+ select value(i) as obj
+ bulk collect into l_suite_rows
+ from t1 c
+ join table(a_suite_rows) i on i.object_owner = c.object_owner and i.path = c.path
+ order by order1 desc;
+
+ a_suite_rows := l_suite_rows;
end;
-
-
-
+
/*
* Public code
*/
+
+ function get_schema_paths(a_paths in ut_varchar2_list) return ut_path_items is
+ begin
+ return expand_paths(group_paths_by_schema(a_paths));
+ end;
+
function get_cached_suite_rows(
- a_object_owner varchar2,
- a_path varchar2 := null,
- a_object_name varchar2 := null,
- a_procedure_name varchar2 := null,
- a_random_seed positive := null,
- a_tags ut_varchar2_rows := null
+ a_suites_filtered ut_suite_cache_rows
) return ut_suite_cache_rows is
- l_path varchar2(4000);
l_results ut_suite_cache_rows := ut_suite_cache_rows();
- l_sql varchar2(32767);
- l_suite_item_name varchar2(20);
- l_tags ut_varchar2_rows := coalesce(a_tags,ut_varchar2_rows());
- l_include_tags ut_varchar2_rows;
- l_exclude_tags ut_varchar2_rows;
- l_object_owner varchar2(250) := ut_utils.qualified_sql_name(a_object_owner);
- l_object_name varchar2(250) := ut_utils.qualified_sql_name(a_object_name);
- l_procedure_name varchar2(250) := ut_utils.qualified_sql_name(a_procedure_name);
- begin
-
- select column_value
- bulk collect into l_include_tags
- from table(l_tags)
- where column_value not like '-%';
-
- select ltrim(column_value,'-')
- bulk collect into l_exclude_tags
- from table(l_tags)
- where column_value like '-%';
-
- if a_path is null and a_object_name is not null then
- select min(c.path)
- into l_path
- from ut_suite_cache c
- where c.object_owner = upper(l_object_owner)
- and c.object_name = upper(l_object_name)
- and c.name = nvl(upper(l_procedure_name), c.name);
- else
- l_path := lower(ut_utils.qualified_sql_name(a_path));
- end if;
- l_suite_item_name := case when l_tags.count > 0 then 'suite_items_tags' else 'suite_items' end;
-
- l_sql := gc_get_cache_suite_sql;
- l_sql := replace(l_sql,'{:suite_item_name:}',l_suite_item_name);
- l_sql := replace(l_sql,'{:object_owner:}',upper(l_object_owner));
- l_sql := replace(l_sql,'{:path:}',get_path_sql(l_path));
- l_sql := replace(l_sql,'{:object_name:}',get_object_name_sql(l_object_name));
- l_sql := replace(l_sql,'{:procedure_name:}',get_procedure_name_sql(l_procedure_name));
- l_sql := replace(l_sql,'{:tags:}',get_tags_sql(l_tags.count));
- l_sql := replace(l_sql,'{:random_seed:}',get_random_seed_sql(a_random_seed));
-
- ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('l_sql',l_sql) );
+ begin
- execute immediate l_sql
- bulk collect into l_results
- using upper(l_object_owner), l_path, l_path, upper(a_object_name), upper(a_procedure_name), l_include_tags, l_include_tags, l_exclude_tags, a_random_seed;
+ open c_get_bulk_cache_suite(a_suites_filtered);
+ fetch c_get_bulk_cache_suite bulk collect into l_results;
+ close c_get_bulk_cache_suite;
+
return l_results;
end;
-
+
function get_schema_parse_time(a_schema_name varchar2) return timestamp result_cache is
l_cache_parse_time timestamp;
begin
- select min(t.parse_time)
+ select /*+ no_parallel */ min(t.parse_time)
into l_cache_parse_time
from ut_suite_cache_schema t
where object_owner = upper(a_schema_name);
@@ -276,7 +306,7 @@ create or replace package body ut_suite_cache_manager is
l_object_owner varchar2(250) := upper(a_object_owner);
l_object_name varchar2(250) := upper(a_object_name);
begin
- if a_suite_items is not null and a_suite_items.count = 0 then
+ if a_suite_items is null or a_suite_items.count = 0 then
delete from ut_suite_cache t
where t.object_owner = l_object_owner
@@ -288,7 +318,7 @@ create or replace package body ut_suite_cache_manager is
else
- select min(parse_time)
+ select /*+ no_parallel */ min(parse_time)
into l_cached_parse_time
from ut_suite_cache_package t
where t.object_name = l_object_name
@@ -296,23 +326,23 @@ create or replace package body ut_suite_cache_manager is
if a_parse_time > l_cached_parse_time or l_cached_parse_time is null then
- update ut_suite_cache_schema t
- set t.parse_time = a_parse_time
+ update /*+ no_parallel */ ut_suite_cache_schema t
+ set t.parse_time = greatest(t.parse_time,a_parse_time)
where object_owner = l_object_owner;
if sql%rowcount = 0 then
- insert into ut_suite_cache_schema
+ insert /*+ no_parallel */ into ut_suite_cache_schema
(object_owner, parse_time)
values (l_object_owner, a_parse_time);
end if;
- update ut_suite_cache_package t
+ update /*+ no_parallel */ ut_suite_cache_package t
set t.parse_time = a_parse_time
where t.object_owner = l_object_owner
and t.object_name = l_object_name;
if sql%rowcount = 0 then
- insert into ut_suite_cache_package
+ insert /*+ no_parallel */ into ut_suite_cache_package
(object_owner, object_name, parse_time)
values (l_object_owner, l_object_name, a_parse_time );
end if;
@@ -321,11 +351,11 @@ create or replace package body ut_suite_cache_manager is
where t.object_owner = l_object_owner
and t.object_name = l_object_name;
- insert into ut_suite_cache t
+ insert /*+ no_parallel */ into ut_suite_cache t
(
id, self_type, path, object_owner, object_name, name,
line_no, parse_time, description,
- rollback_type, disabled_flag, warnings,
+ rollback_type, disabled_flag,disabled_reason, warnings,
before_all_list, after_all_list,
before_each_list, after_each_list,
before_test_list, after_test_list,
@@ -336,10 +366,10 @@ create or replace package body ut_suite_cache_manager is
select treat(value(x) as ut_suite) i
from table(a_suite_items) x
where x.self_type in( 'UT_SUITE', 'UT_SUITE_CONTEXT' ) )
- select ut_suite_cache_seq.nextval, s.i.self_type as self_type, s.i.path as path,
+ select /*+ no_parallel */ ut_suite_cache_seq.nextval, s.i.self_type as self_type, s.i.path as path,
upper(s.i.object_owner) as object_owner, upper(s.i.object_name) as object_name, upper(s.i.name) as name,
s.i.line_no as line_no, s.i.parse_time as parse_time, s.i.description as description,
- s.i.rollback_type as rollback_type, s.i.disabled_flag as disabled_flag, s.i.warnings as warnings,
+ s.i.rollback_type as rollback_type, s.i.disabled_flag as disabled_flag,s.i.disabled_reason as disabled_reason, s.i.warnings as warnings,
s.i.before_all_list as before_all_list, s.i.after_all_list as after_all_list,
null before_each_list, null after_each_list,
null before_test_list, null after_test_list,
@@ -347,11 +377,11 @@ create or replace package body ut_suite_cache_manager is
null item
from suites s;
- insert into ut_suite_cache t
+ insert /*+ no_parallel */ into ut_suite_cache t
(
id, self_type, path, object_owner, object_name, name,
line_no, parse_time, description,
- rollback_type, disabled_flag, warnings,
+ rollback_type, disabled_flag,disabled_reason, warnings,
before_all_list, after_all_list,
before_each_list, after_each_list,
before_test_list, after_test_list,
@@ -362,10 +392,10 @@ create or replace package body ut_suite_cache_manager is
select treat(value(x) as ut_test) t
from table ( a_suite_items ) x
where x.self_type in ( 'UT_TEST' ) )
- select ut_suite_cache_seq.nextval, s.t.self_type as self_type, s.t.path as path,
+ select /*+ no_parallel */ ut_suite_cache_seq.nextval, s.t.self_type as self_type, s.t.path as path,
upper(s.t.object_owner) as object_owner, upper(s.t.object_name) as object_name, upper(s.t.name) as name,
s.t.line_no as line_no, s.t.parse_time as parse_time, s.t.description as description,
- s.t.rollback_type as rollback_type, s.t.disabled_flag as disabled_flag, s.t.warnings as warnings,
+ s.t.rollback_type as rollback_type, s.t.disabled_flag as disabled_flag, s.t.disabled_reason as disabled_reason, s.t.warnings as warnings,
null before_all_list, null after_all_list,
s.t.before_each_list as before_each_list, s.t.after_each_list as after_each_list,
s.t.before_test_list as before_test_list, s.t.after_test_list as after_test_list,
@@ -385,35 +415,38 @@ create or replace package body ut_suite_cache_manager is
l_objects := get_missing_cache_objects(a_schema_name);
if l_objects is not empty then
- delete from ut_suite_cache i
+ delete /*+ no_parallel */ from ut_suite_cache i
where i.object_owner = a_schema_name
- and i.object_name in ( select column_value from table (l_objects) );
+ and i.object_name in ( select /*+ no_parallel */ column_value from table (l_objects) );
- delete from ut_suite_cache_package i
+ delete /*+ no_parallel */ from ut_suite_cache_package i
where i.object_owner = a_schema_name
- and i.object_name in ( select column_value from table (l_objects) );
+ and i.object_name in ( select /*+ no_parallel */ column_value from table (l_objects) );
end if;
commit;
end;
function get_cached_suite_info(
- a_object_owner varchar2,
- a_object_name varchar2
+ a_schema_paths ut_path_items
+ ) return ut_suite_cache_rows is
+ begin
+ return get_cached_suite_rows(get_suite_items(a_schema_paths));
+ end;
+
+ function get_suite_items_info(
+ a_suite_cache_items ut_suite_cache_rows
) return ut_suite_items_info is
- l_cache_rows ut_suite_cache_rows;
l_results ut_suite_items_info;
begin
- l_cache_rows := get_cached_suite_rows( a_object_owner => a_object_owner, a_object_name =>a_object_name );
- select ut_suite_item_info(
+ select /*+ no_parallel */ ut_suite_item_info(
c.object_owner, c.object_name, c.name,
c.description, c.self_type, c.line_no,
- c.path, c.disabled_flag, c.tags
+ c.path, c.disabled_flag, c.disabled_reason, c.tags
)
bulk collect into l_results
- from table(l_cache_rows) c;
-
- return l_results;
+ from table(a_suite_cache_items) c;
+ return l_results;
end;
function get_cached_packages(
@@ -421,7 +454,7 @@ create or replace package body ut_suite_cache_manager is
) return ut_object_names is
l_results ut_object_names;
begin
- select ut_object_name( c.object_owner, c.object_name )
+ select /*+ no_parallel */ ut_object_name( c.object_owner, c.object_name )
bulk collect into l_results
from ut_suite_cache_package c
join table ( a_schema_names ) s
@@ -437,7 +470,7 @@ create or replace package body ut_suite_cache_manager is
l_count integer;
begin
if a_procedure_name is not null then
- select count( 1 ) into l_count from dual
+ select /*+ no_parallel */ count( 1 ) into l_count from dual
where exists(
select 1
from ut_suite_cache c
@@ -446,7 +479,7 @@ create or replace package body ut_suite_cache_manager is
and c.name = a_procedure_name
);
elsif a_package_name is not null then
- select count( 1 ) into l_count from dual
+ select /*+ no_parallel */ count( 1 ) into l_count from dual
where exists(
select 1
from ut_suite_cache c
@@ -454,7 +487,7 @@ create or replace package body ut_suite_cache_manager is
and c.object_name = a_package_name
);
else
- select count( 1 ) into l_count from dual
+ select /*+ no_parallel */ count( 1 ) into l_count from dual
where exists(
select 1
from ut_suite_cache c
diff --git a/source/core/ut_suite_cache_manager.pks b/source/core/ut_suite_cache_manager.pks
index d5b31ccee..6ac1d7bd6 100644
--- a/source/core/ut_suite_cache_manager.pks
+++ b/source/core/ut_suite_cache_manager.pks
@@ -1,7 +1,7 @@
-create or replace package ut_suite_cache_manager authid definer is
+create or replace noneditionable package ut_suite_cache_manager authid definer is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -40,30 +40,43 @@ create or replace package ut_suite_cache_manager authid definer is
*/
procedure remove_missing_objs_from_cache(a_schema_name varchar2);
+ /*
+ * We will sort a suites in hierarchical structure.
+ * Sorting from bottom to top so when we consolidate
+ * we will go in proper order.
+ */
+ procedure sort_and_randomize_tests(
+ a_suite_rows in out ut_suite_cache_rows,
+ a_random_seed positive := null);
+
/*
* Retrieves suite items data from cache.
* Returned data is not filtered by user access rights.
* Not to be used publicly. Used internally for building suites at runtime.
*/
function get_cached_suite_rows(
- a_object_owner varchar2,
- a_path varchar2 := null,
- a_object_name varchar2 := null,
- a_procedure_name varchar2 := null,
- a_random_seed positive := null,
- a_tags ut_varchar2_rows := null
+ a_suites_filtered ut_suite_cache_rows
) return ut_suite_cache_rows;
+ function get_schema_paths(a_paths in ut_varchar2_list) return ut_path_items;
+
/*
* Retrieves suite item info rows from cache.
* Returned data is not filtered by user access rights.
* Not to be used publicly. Used internally for building suites info.
*/
function get_cached_suite_info(
- a_object_owner varchar2,
- a_object_name varchar2
+ a_schema_paths ut_path_items
+ ) return ut_suite_cache_rows;
+
+ function get_suite_items_info(
+ a_suite_cache_items ut_suite_cache_rows
) return ut_suite_items_info;
+ function get_suite_items (
+ a_schema_paths ut_path_items
+ ) return ut_suite_cache_rows;
+
/*
* Retrieves list of cached suite packages.
* Returned data is not filtered by user access rights.
@@ -83,6 +96,6 @@ create or replace package ut_suite_cache_manager authid definer is
a_package_name varchar2,
a_procedure_name varchar2
) return boolean;
-
+
end ut_suite_cache_manager;
/
diff --git a/source/core/ut_suite_cache_package.sql b/source/core/ut_suite_cache_package.sql
index 06f786949..40b7b8dec 100644
--- a/source/core/ut_suite_cache_package.sql
+++ b/source/core/ut_suite_cache_package.sql
@@ -1,7 +1,7 @@
create table ut_suite_cache_package (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/ut_suite_cache_schema.sql b/source/core/ut_suite_cache_schema.sql
index 46c7b0ef3..cace332e3 100644
--- a/source/core/ut_suite_cache_schema.sql
+++ b/source/core/ut_suite_cache_schema.sql
@@ -1,7 +1,7 @@
create table ut_suite_cache_schema (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/ut_suite_cache_seq.sql b/source/core/ut_suite_cache_seq.sql
index 36de28809..020c25c4d 100644
--- a/source/core/ut_suite_cache_seq.sql
+++ b/source/core/ut_suite_cache_seq.sql
@@ -1,7 +1,7 @@
create sequence ut_suite_cache_seq
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb
index 603e713ec..fa60be59e 100644
--- a/source/core/ut_suite_manager.pkb
+++ b/source/core/ut_suite_manager.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_suite_manager is
+create or replace noneditionable package body ut_suite_manager is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -17,19 +17,9 @@ create or replace package body ut_suite_manager is
*/
gc_suitpath_error_message constant varchar2(100) := 'Suitepath exceeds 1000 CHAR on: ';
-
- type t_path_item is record (
- object_name varchar2(250),
- procedure_name varchar2(250),
- suite_path varchar2(4000)
- );
- type t_path_items is table of t_path_item;
- type t_schema_paths is table of t_path_items index by varchar2(250 char);
-
cursor c_cached_suites_cursor is select * from table(ut_suite_cache_rows());
type tt_cached_suites is table of c_cached_suites_cursor%rowtype;
type t_cached_suites_cursor is ref cursor return c_cached_suites_cursor%rowtype;
-
type t_item_levels is table of ut_suite_items index by binary_integer;
------------------
@@ -41,7 +31,8 @@ create or replace package body ut_suite_manager is
else
for i in 1 .. a_paths.count loop
l_path := a_paths(i);
- if l_path is null or not (regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){0,2}$') or regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+)*$')) then
+ if l_path is null or not (
+ regexp_like(l_path, '^[[:alnum:]$#_\*]+(\.[[:alnum:]$#_\*]+){0,2}$') or regexp_like(l_path, '^([[:alnum:]$#_]+)?:[[:alnum:]$#_\*]+(\.[[:alnum:]$#_\*]+)*$')) then
raise_application_error(ut_utils.gc_invalid_path_format, 'Invalid path format: ' || nvl(l_path, 'NULL'));
end if;
end loop;
@@ -70,9 +61,9 @@ create or replace package body ut_suite_manager is
for i in 1 .. a_paths.count loop
--if path is suite-path
- if regexp_like(a_paths(i), '^([A-Za-z0-9$#_]+)?:') then
+ if regexp_like(a_paths(i), '^([[:alnum:]$#_]+)?:') then
--get schema name / path
- l_schema := regexp_substr(a_paths(i), '^([A-Za-z0-9$#_]+)?:',subexpression => 1);
+ l_schema := regexp_substr(a_paths(i), '^([[:alnum:]$#_]+)?:',subexpression => 1);
-- transform ":path1[.path2]" to "schema:path1[.path2]"
if l_schema is not null then
l_schema := sys.dbms_assert.schema_name(upper(l_schema));
@@ -84,12 +75,14 @@ create or replace package body ut_suite_manager is
-- get schema name / object.[procedure] name
-- When path is one of: schema or schema.package[.object] or package[.object]
-- transform it back to schema[.package[.object]]
+ -- Object name or procedure is allowed to have filter char
+ -- However this is not allowed on schema
begin
- l_object := regexp_substr(a_paths(i), '^[A-Za-z0-9$#_]+');
+ l_object := regexp_substr(a_paths(i), '^[[:alnum:]$#_\*]+');
l_schema := sys.dbms_assert.schema_name(upper(l_object));
exception
when sys.dbms_assert.invalid_schema_name then
- if ut_metadata.package_exists_in_cur_schema(upper(l_object)) then
+ if l_object like '%*%' or ut_metadata.package_exists_in_cur_schema(upper(l_object)) then
a_paths(i) := c_current_schema || '.' || a_paths(i);
l_schema := c_current_schema;
else
@@ -110,34 +103,6 @@ create or replace package body ut_suite_manager is
l_schema_names := resolve_schema_names(a_paths);
end;
- function group_paths_by_schema(a_paths ut_varchar2_list) return t_schema_paths is
- c_package_path_regex constant varchar2(100) := '^([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?(\.([A-Za-z0-9$#_]+))?$';
- l_schema varchar2(4000);
- l_empty_result t_path_item;
- l_result t_path_item;
- l_results t_schema_paths;
- begin
- for i in 1 .. a_paths.count loop
- l_result := l_empty_result;
- if a_paths(i) like '%:%' then
- l_schema := upper(regexp_substr(a_paths(i),'^[^.:]+'));
- l_result.suite_path := ltrim(regexp_substr(a_paths(i),'[.:].*$'),':');
- else
- l_schema := regexp_substr(a_paths(i), c_package_path_regex, subexpression => 1);
- l_result.object_name := regexp_substr(a_paths(i), c_package_path_regex, subexpression => 3);
- l_result.procedure_name := regexp_substr(a_paths(i), c_package_path_regex, subexpression => 5);
- end if;
- if l_results.exists(l_schema) then
- l_results(l_schema).extend;
- l_results(l_schema)(l_results(l_schema).last) := l_result;
- else
- l_results(l_schema) := t_path_items(l_result);
- end if;
- end loop;
- return l_results;
- end;
-
-
function sort_by_seq_no(
a_list ut_executables
) return ut_executables is
@@ -187,7 +152,7 @@ create or replace package body ut_suite_manager is
self_type => a_rows( a_idx ).self_type,
object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name),
name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path,
- rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag,
+ rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason,
line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time,
start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings,
results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(),
@@ -200,7 +165,7 @@ create or replace package body ut_suite_manager is
self_type => a_rows( a_idx ).self_type,
object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name),
name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path,
- rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag,
+ rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason,
line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time,
start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings,
results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(),
@@ -216,7 +181,7 @@ create or replace package body ut_suite_manager is
self_type => a_rows( a_idx ).self_type,
object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name),
name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path,
- rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag,
+ rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason,
line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time,
start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings,
results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(),
@@ -229,7 +194,7 @@ create or replace package body ut_suite_manager is
self_type => a_rows( a_idx ).self_type,
object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name),
name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path,
- rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag,
+ rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason,
line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time,
start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings,
results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(),
@@ -245,7 +210,7 @@ create or replace package body ut_suite_manager is
self_type => a_rows( a_idx ).self_type,
object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name),
name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path,
- rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag,
+ rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason,
line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time,
start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings,
results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(),
@@ -256,7 +221,7 @@ create or replace package body ut_suite_manager is
self_type => a_rows( a_idx ).self_type,
object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name),
name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path,
- rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag,
+ rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason,
line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time,
start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings,
results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(),
@@ -269,7 +234,7 @@ create or replace package body ut_suite_manager is
self_type => a_rows(a_idx).self_type,
object_owner => a_rows(a_idx).object_owner, object_name => lower(a_rows(a_idx).object_name),
name => lower(a_rows(a_idx).name), description => a_rows(a_idx).description, path => a_rows(a_idx).path,
- rollback_type => a_rows(a_idx).rollback_type, disabled_flag => a_rows(a_idx).disabled_flag,
+ rollback_type => a_rows(a_idx).rollback_type, disabled_flag => a_rows(a_idx).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason,
line_no => a_rows(a_idx).line_no, parse_time => a_rows(a_idx).parse_time,
start_time => null, end_time => null, result => null, warnings => a_rows(a_idx).warnings,
results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(),
@@ -331,42 +296,80 @@ create or replace package body ut_suite_manager is
close a_suite_data_cursor;
end reconstruct_from_cache;
- function get_cached_suite_data(
- a_object_owner varchar2,
- a_path varchar2 := null,
- a_object_name varchar2 := null,
- a_procedure_name varchar2 := null,
- a_skip_all_objects boolean := false,
- a_random_seed positive,
- a_tags ut_varchar2_rows := null
- ) return t_cached_suites_cursor is
- l_unfiltered_rows ut_suite_cache_rows;
- l_result t_cached_suites_cursor;
+ function get_filtered_cursor(
+ a_unfiltered_rows in ut_suite_cache_rows,
+ a_skip_all_objects boolean := false
+ )
+ return ut_suite_cache_rows is
+ l_result ut_suite_cache_rows := ut_suite_cache_rows();
begin
- l_unfiltered_rows := ut_suite_cache_manager.get_cached_suite_rows(
- a_object_owner,
- a_path,
- a_object_name,
- a_procedure_name,
- a_random_seed,
- a_tags
- );
- if a_skip_all_objects then
- open l_result for
- select c.* from table(l_unfiltered_rows) c;
+ if ut_metadata.user_has_execute_any_proc() or a_skip_all_objects then
+ l_result := a_unfiltered_rows;
else
- open l_result for
- select c.* from table(l_unfiltered_rows) c
- where exists
+ select obj bulk collect into l_result
+ from (
+ select /*+ no_parallel */ value(c) as obj from table(a_unfiltered_rows) c
+ where sys_context( 'userenv', 'current_user' ) = upper(c.object_owner)
+ union all
+ select /*+ no_parallel */ value(c) as obj from table(a_unfiltered_rows) c
+ where sys_context( 'userenv', 'current_user' ) != upper(c.object_owner)
+ and ( exists
( select 1
from all_objects a
where a.object_name = c.object_name
- and a.owner = c.object_owner
- and a.object_type = 'PACKAGE'
- )
- or c.self_type = 'UT_LOGICAL_SUITE';
+ and a.owner = c.object_owner
+ and a.object_type = 'PACKAGE'
+ )
+ or c.self_type = 'UT_LOGICAL_SUITE'));
end if;
-
+ return l_result;
+ end;
+
+ procedure reconcile_paths_and_suites(
+ a_schema_paths ut_path_items,
+ a_filtered_rows ut_suite_cache_rows
+ ) is
+ begin
+ for i in ( select /*+ no_parallel */ sp.schema_name,sp.object_name,sp.procedure_name,
+ sp.suite_path,sc.path
+ from table(a_schema_paths) sp left outer join
+ table(a_filtered_rows) sc on
+ (( upper(sp.schema_name) = upper(sc.object_owner) and upper(sp.object_name) = upper(sc.object_name)
+ and nvl(upper(sp.procedure_name),sc.name) = sc.name )
+ or (sc.path = sp.suite_path))
+ where sc.path is null)
+ loop
+ if i.suite_path is not null then
+ raise_application_error(ut_utils.gc_suite_package_not_found,'No suite packages found for path '||i.schema_name||':'||i.suite_path|| '.');
+ elsif i.procedure_name is not null then
+ raise_application_error(ut_utils.gc_suite_package_not_found,'Suite test '||i.schema_name||'.'||i.object_name|| '.'||i.procedure_name||' does not exist');
+ elsif i.object_name is not null then
+ raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||i.schema_name||'.'||i.object_name|| ' does not exist');
+ end if;
+ end loop;
+ end;
+
+ function get_cached_suite_data(
+ a_schema_paths ut_path_items,
+ a_random_seed positive,
+ a_tags varchar2 := null,
+ a_skip_all_objects boolean := false
+ ) return t_cached_suites_cursor is
+ l_unfiltered_rows ut_suite_cache_rows;
+ l_tag_filter_applied ut_suite_cache_rows;
+ l_filtered_rows ut_suite_cache_rows;
+ l_result t_cached_suites_cursor;
+ begin
+ l_unfiltered_rows := ut_suite_cache_manager.get_suite_items(a_schema_paths);
+
+ l_tag_filter_applied := ut_suite_tag_filter.apply(l_unfiltered_rows,a_tags);
+ l_filtered_rows := get_filtered_cursor(ut_suite_cache_manager.get_cached_suite_rows(l_tag_filter_applied),a_skip_all_objects);
+ reconcile_paths_and_suites(a_schema_paths,l_filtered_rows);
+
+ ut_suite_cache_manager.sort_and_randomize_tests(l_filtered_rows,a_random_seed);
+
+ open l_result for
+ select * from table(l_filtered_rows);
return l_result;
end;
@@ -442,31 +445,21 @@ create or replace package body ut_suite_manager is
ut_event_manager.trigger_event('refresh_cache - end');
end;
- procedure add_suites_for_path(
- a_owner_name varchar2,
- a_path varchar2 := null,
- a_object_name varchar2 := null,
- a_procedure_name varchar2 := null,
+ procedure add_suites_for_paths(
+ a_schema_paths ut_path_items,
a_suites in out nocopy ut_suite_items,
a_random_seed positive,
- a_tags ut_varchar2_rows := null
+ a_tags varchar2 := null
) is
begin
- refresh_cache(a_owner_name);
-
reconstruct_from_cache(
a_suites,
get_cached_suite_data(
- a_owner_name,
- a_path,
- a_object_name,
- a_procedure_name,
- can_skip_all_objects_scan(a_owner_name),
+ a_schema_paths,
a_random_seed,
a_tags
)
);
-
end;
-----------------------------------------------
@@ -482,31 +475,37 @@ create or replace package body ut_suite_manager is
a_skip_all_objects boolean := false
) return ut_suite_items is
l_suites ut_suite_items := ut_suite_items();
+ l_schema_paths ut_path_items;
begin
build_and_cache_suites(a_owner_name, a_annotated_objects);
-
+ l_schema_paths := ut_path_items(ut_path_item(a_owner_name,a_object_name,a_procedure_name,a_path));
reconstruct_from_cache(
l_suites,
get_cached_suite_data(
- a_owner_name,
- a_path,
- a_object_name,
- a_procedure_name,
- a_skip_all_objects,
+ l_schema_paths,
null,
- null
+ null,
+ a_skip_all_objects
)
);
return l_suites;
end;
- function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names is
+ function get_schema_ut_packages(a_schema_names ut_varchar2_rows, a_schema_name_expr varchar2) return ut_object_names is
+ l_schema_names ut_varchar2_rows := a_schema_names;
begin
- for i in 1 .. a_schema_names.count loop
- refresh_cache(a_schema_names(i));
+ if a_schema_name_expr is not null then
+ select username
+ bulk collect into l_schema_names
+ from all_users
+ where regexp_like(username,a_schema_name_expr,'i');
+ end if;
+
+ for i in 1 .. l_schema_names.count loop
+ refresh_cache(l_schema_names(i));
end loop;
- return ut_suite_cache_manager.get_cached_packages( a_schema_names );
+ return ut_suite_cache_manager.get_cached_packages( l_schema_names );
end;
function get_schema_names(a_paths ut_varchar2_list) return ut_varchar2_rows is
@@ -527,92 +526,72 @@ create or replace package body ut_suite_manager is
a_paths ut_varchar2_list,
a_suites out nocopy ut_suite_items,
a_random_seed positive := null,
- a_tags ut_varchar2_rows := ut_varchar2_rows()
+ a_tags varchar2 := null
) is
l_paths ut_varchar2_list := a_paths;
- l_path_items t_path_items;
- l_path_item t_path_item;
+ l_schema_names ut_varchar2_rows;
+ l_schema_paths ut_path_items;
l_schema varchar2(4000);
- l_suites_count pls_integer := 0;
- l_index varchar2(4000 char);
- l_schema_paths t_schema_paths;
begin
ut_event_manager.trigger_event('configure_execution_by_path - start');
a_suites := ut_suite_items();
--resolve schema names from paths and group paths by schema name
- resolve_schema_names(l_paths);
-
- l_schema_paths := group_paths_by_schema(l_paths);
+ l_schema_names := resolve_schema_names(l_paths);
- l_schema := l_schema_paths.first;
+ --refresh cache
+ l_schema := l_schema_names.first;
while l_schema is not null loop
- l_path_items := l_schema_paths(l_schema);
- for i in 1 .. l_path_items.count loop
- l_path_item := l_path_items(i);
- add_suites_for_path(
- upper(l_schema),
- l_path_item.suite_path,
- l_path_item.object_name,
- l_path_item.procedure_name,
- a_suites,
- a_random_seed,
- a_tags
- );
- if a_suites.count = l_suites_count then
- if l_path_item.suite_path is not null then
- raise_application_error(ut_utils.gc_suite_package_not_found,'No suite packages found for path '||l_schema||':'||l_path_item.suite_path|| '.');
- elsif l_path_item.procedure_name is not null then
- raise_application_error(ut_utils.gc_suite_package_not_found,'Suite test '||l_schema||'.'||l_path_item.object_name|| '.'||l_path_item.procedure_name||' does not exist');
- elsif l_path_item.object_name is not null then
- raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||l_schema||'.'||l_path_item.object_name|| ' does not exist');
- end if;
- end if;
- l_index := a_suites.first;
- l_suites_count := a_suites.count;
- end loop;
- l_schema := l_schema_paths.next(l_schema);
+ refresh_cache(upper(l_schema_names(l_schema)));
+ l_schema := l_schema_names.next(l_schema);
end loop;
+ l_schema_paths := ut_suite_cache_manager.get_schema_paths(l_paths);
+
+ --We will get a single list of paths rather than loop by loop.
+ add_suites_for_paths(
+ l_schema_paths,
+ a_suites,
+ a_random_seed,
+ a_tags
+ );
+
--propagate rollback type to suite items after organizing suites into hierarchy
for i in 1 .. a_suites.count loop
a_suites(i).set_rollback_type( a_suites(i).get_rollback_type() );
end loop;
- ut_event_manager.trigger_event('configure_execution_by_path - start');
+ ut_event_manager.trigger_event('configure_execution_by_path - end');
end configure_execution_by_path;
function get_suites_info(
- a_owner_name varchar2,
- a_package_name varchar2 := null
+ a_paths ut_varchar2_list
) return sys_refcursor is
- l_result sys_refcursor;
- l_all_suite_info ut_suite_items_info;
- l_owner_name varchar2(250) := ut_utils.qualified_sql_name(a_owner_name);
- l_package_name varchar2(250) := ut_utils.qualified_sql_name(a_package_name);
- begin
+ l_result sys_refcursor;
+ l_all_suite_info ut_suite_items_info;
+ l_schema_names ut_varchar2_rows;
+ l_schema_paths ut_path_items;
+ l_paths ut_varchar2_list := a_paths;
+ l_schema varchar2(4000);
+ l_unfiltered_rows ut_suite_cache_rows;
+ l_filtered_rows ut_suite_cache_rows;
- refresh_cache(l_owner_name);
+ begin
+ l_schema_names := resolve_schema_names(l_paths);
+ --refresh cache
+ l_schema := l_schema_names.first;
+ while l_schema is not null loop
+ refresh_cache(upper(l_schema_names(l_schema)));
+ l_schema := l_schema_names.next(l_schema);
+ end loop;
+ l_schema_paths := ut_suite_cache_manager.get_schema_paths(l_paths);
+ l_unfiltered_rows := ut_suite_cache_manager.get_cached_suite_info(l_schema_paths);
+ l_filtered_rows := get_filtered_cursor(l_unfiltered_rows);
+ l_all_suite_info := ut_suite_cache_manager.get_suite_items_info(l_filtered_rows);
+ open l_result for
+ select /*+ no_parallel */ value(c)
+ from table(l_all_suite_info) c
+ order by c.object_owner, c.object_name, c.item_line_no;
- l_all_suite_info := ut_suite_cache_manager.get_cached_suite_info( l_owner_name, l_package_name );
- if can_skip_all_objects_scan( l_owner_name ) then
- open l_result for
- select value(c)
- from table(l_all_suite_info) c
- order by c.object_owner, c.object_name, c.item_line_no;
- else
- open l_result for
- select value(c)
- from table(l_all_suite_info) c
- where exists
- ( select 1
- from all_objects a
- where a.object_name = c.object_name
- and a.owner = c.object_owner
- and a.object_type = 'PACKAGE'
- )
- or c.item_type = 'UT_LOGICAL_SUITE'
- order by c.object_owner, c.object_name, c.item_line_no;
- end if;
return l_result;
end;
@@ -631,7 +610,7 @@ create or replace package body ut_suite_manager is
refresh_cache(l_owner_name);
l_item_exists := ut_suite_cache_manager.suite_item_exists( l_owner_name, l_package_name, l_procedure_name );
if not can_skip_all_objects_scan( l_owner_name ) and l_package_name is not null then
- select count(1)
+ select /*+ no_parallel */ count(1)
into l_count
from dual c
where exists
diff --git a/source/core/ut_suite_manager.pks b/source/core/ut_suite_manager.pks
index 093201b36..60c3595bb 100644
--- a/source/core/ut_suite_manager.pks
+++ b/source/core/ut_suite_manager.pks
@@ -1,7 +1,7 @@
-create or replace package ut_suite_manager authid current_user is
+create or replace noneditionable package ut_suite_manager authid current_user is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ create or replace package ut_suite_manager authid current_user is
* @param a_schema_names list of schemas to return the information for
* @return array containing unit test schema and object names
*/
- function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names;
+ function get_schema_ut_packages(a_schema_names ut_varchar2_rows, a_schema_name_expr varchar2) return ut_object_names;
/**
* Builds a hierarchical suites based on given suite-paths
@@ -39,6 +39,7 @@ create or replace package ut_suite_manager authid current_user is
* @return array containing root suites-ready to be executed
*
*/
+ --TODO:Zerknij czy mozna wywalic
function configure_execution_by_path(a_paths ut_varchar2_list, a_random_seed positive := null) return ut_suite_items;
/**
@@ -52,7 +53,7 @@ create or replace package ut_suite_manager authid current_user is
a_paths in ut_varchar2_list,
a_suites out nocopy ut_suite_items,
a_random_seed in positive := null,
- a_tags ut_varchar2_rows := ut_varchar2_rows()
+ a_tags in varchar2 := null
);
/**
@@ -79,15 +80,11 @@ create or replace package ut_suite_manager authid current_user is
/**
* Returns a ref cursor containing information about unit test suites and the tests contained in them
*
- * @param a_owner owner of unit tests to retrieve
- * @param a_package_name name of test package (optional)
- * @param a_procedure_name name of test procedure (optional)
- * @return ut_suite_items_info table of objects
+ * @param a_paths list of paths to be resolved and return a suites.
*/
function get_suites_info(
- a_owner_name varchar2,
- a_package_name varchar2 := null
- ) return sys_refcursor;
+ a_paths ut_varchar2_list
+ ) return sys_refcursor;
/**
* Returns true if given suite item exists
diff --git a/source/core/ut_suite_tag_filter.pkb b/source/core/ut_suite_tag_filter.pkb
new file mode 100644
index 000000000..447ba47d5
--- /dev/null
+++ b/source/core/ut_suite_tag_filter.pkb
@@ -0,0 +1,290 @@
+create or replace noneditionable package body ut_suite_tag_filter is
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ /**
+ * Constants use in postfix and infix transformations
+ */
+ gc_operators constant ut_varchar2_list := ut_varchar2_list('|','&','!');
+ gc_unary_operators constant ut_varchar2_list := ut_varchar2_list('!'); -- right side associative operator
+ gc_binary_operators constant ut_varchar2_list := ut_varchar2_list('|','&'); -- left side associative operator
+ gc_reserved_tag_words constant ut_varchar2_list := ut_varchar2_list('none','any');
+ gc_exception_msg constant varchar2(200) := 'Invalid tag expression';
+
+ type t_precedence_table is table of number index by varchar2(1);
+ g_precedence t_precedence_table;
+
+ function tokenize_tags_string(a_tags in varchar2) return ut_varchar2_list is
+ l_tags_tokens ut_varchar2_list := ut_varchar2_list();
+ begin
+ --Tokenize a string into operators and tags
+ select regexp_substr(a_tags,'([^!()|&]+)|([!()|&])', 1, level) as string_parts
+ bulk collect into l_tags_tokens
+ from dual connect by regexp_substr (a_tags, '([^!()|&]+)|([!()|&])', 1, level) is not null;
+
+ return l_tags_tokens;
+ end;
+
+ /*
+ To support a legact tag notation
+ , = OR
+ - = NOT
+ we will perform a replace of that characters into
+ new notation.
+ | = OR
+ & = AND
+ ! = NOT
+ */
+ function replace_legacy_tag_notation(a_tags varchar2
+ ) return varchar2 is
+ l_tags ut_varchar2_list := ut_utils.string_to_table(a_tags,',');
+ l_tags_include varchar2(4000);
+ l_tags_exclude varchar2(4000);
+ l_return_tag varchar2(4000);
+ begin
+ if instr(a_tags,',') > 0 or instr(a_tags,'-') > 0 then
+
+ select '('||listagg( t.column_value,'|')
+ within group( order by column_value)||')'
+ into l_tags_include
+ from table(l_tags) t
+ where t.column_value not like '-%';
+
+ select '('||listagg( replace(t.column_value,'-','!'),' & ')
+ within group( order by column_value)||')'
+ into l_tags_exclude
+ from table(l_tags) t
+ where t.column_value like '-%';
+
+
+ l_return_tag:=
+ case
+ when l_tags_include <> '()' and l_tags_exclude <> '()'
+ then l_tags_include || ' & ' || l_tags_exclude
+ when l_tags_include <> '()'
+ then l_tags_include
+ when l_tags_exclude <> '()'
+ then l_tags_exclude
+ end;
+ else
+ l_return_tag := a_tags;
+ end if;
+ return l_return_tag;
+ end;
+
+ /*
+ https://stackoverflow.com/questions/29634992/shunting-yard-validate-expression
+ */
+ function shunt_logical_expression(a_tags in ut_varchar2_list) return ut_varchar2_list is
+ l_operator_stack ut_stack := ut_stack();
+ l_rnp_tokens ut_varchar2_list := ut_varchar2_list();
+ l_token varchar2(32767);
+ l_expect_operand boolean := true;
+ l_expect_operator boolean := false;
+ l_idx pls_integer;
+ begin
+ l_idx := a_tags.first;
+ --Exuecute modified shunting algorithm
+ WHILE (l_idx is not null) loop
+ l_token := a_tags(l_idx);
+ if (l_token member of gc_operators and l_token member of gc_binary_operators) then
+ if not(l_expect_operator) then
+ raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
+ end if;
+ while l_operator_stack.top > 0 and (g_precedence(l_operator_stack.peek) > g_precedence(l_token)) loop
+ l_rnp_tokens.extend;
+ l_rnp_tokens(l_rnp_tokens.last) := l_operator_stack.pop;
+ end loop;
+ l_operator_stack.push(a_tags(l_idx));
+ l_expect_operand := true;
+ l_expect_operator:= false;
+ elsif (l_token member of gc_operators and l_token member of gc_unary_operators) then
+ if not(l_expect_operand) then
+ raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
+ end if;
+ l_operator_stack.push(a_tags(l_idx));
+ l_expect_operand := true;
+ l_expect_operator:= false;
+ elsif l_token = '(' then
+ if not(l_expect_operand) then
+ raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
+ end if;
+ l_operator_stack.push(a_tags(l_idx));
+ l_expect_operand := true;
+ l_expect_operator:= false;
+ elsif l_token = ')' then
+ if not(l_expect_operator) then
+ raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
+ end if;
+ while l_operator_stack.peek <> '(' loop
+ l_rnp_tokens.extend;
+ l_rnp_tokens(l_rnp_tokens.last) := l_operator_stack.pop;
+ end loop;
+ l_operator_stack.pop; --Pop the open bracket and discard it
+ l_expect_operand := false;
+ l_expect_operator:= true;
+ else
+ if not(l_expect_operand) then
+ raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
+ end if;
+ l_rnp_tokens.extend;
+ l_rnp_tokens(l_rnp_tokens.last) :=l_token;
+ l_expect_operator := true;
+ l_expect_operand := false;
+ end if;
+
+ l_idx := a_tags.next(l_idx);
+ end loop;
+
+ while l_operator_stack.peek is not null loop
+ if l_operator_stack.peek in ('(',')') then
+ raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
+ end if;
+ l_rnp_tokens.extend;
+ l_rnp_tokens(l_rnp_tokens.last):=l_operator_stack.pop;
+ end loop;
+
+ return l_rnp_tokens;
+ end shunt_logical_expression;
+
+ function conv_postfix_to_infix_sql(a_postfix_exp in ut_varchar2_list,a_tags_column_name in varchar2)
+ return varchar2 is
+ l_infix_stack ut_stack := ut_stack();
+ l_right_side varchar2(32767);
+ l_left_side varchar2(32767);
+ l_infix_exp varchar2(32767);
+ l_member_token varchar2(20) := ' member of '||a_tags_column_name;
+ l_idx pls_integer;
+ begin
+ l_idx := a_postfix_exp.first;
+ while ( l_idx is not null) loop
+ --If the token we got is a none or any keyword
+ if a_postfix_exp(l_idx) member of gc_reserved_tag_words then
+
+ l_infix_stack.push(
+ case
+ when a_postfix_exp(l_idx) = 'none' then '('||a_tags_column_name||' is empty or '||a_tags_column_name||' is null)'
+ else a_tags_column_name||' is not empty'
+ end
+ );
+ --If token is operand but also single tag
+ elsif regexp_count(a_postfix_exp(l_idx),'[!()|&]') = 0 then
+ l_infix_stack.push(q'[']'||a_postfix_exp(l_idx)||q'[']'||l_member_token);
+ --If token is unary operator not
+ elsif a_postfix_exp(l_idx) member of gc_unary_operators then
+ l_right_side := l_infix_stack.pop;
+ l_infix_exp := a_postfix_exp(l_idx)||'('||l_right_side||')';
+ l_infix_stack.push(l_infix_exp);
+ --If token is binary operator
+ elsif a_postfix_exp(l_idx) member of gc_binary_operators then
+ l_right_side := l_infix_stack.pop;
+ l_left_side := l_infix_stack.pop;
+ l_infix_exp := '('||l_left_side||a_postfix_exp(l_idx)||l_right_side||')';
+ l_infix_stack.push(l_infix_exp);
+ end if;
+ l_idx := a_postfix_exp.next(l_idx);
+ end loop;
+
+ return l_infix_stack.pop;
+ end conv_postfix_to_infix_sql;
+
+ function create_where_filter(a_tags varchar2
+ ) return varchar2 is
+ l_tags varchar2(4000);
+ begin
+ l_tags := replace(replace_legacy_tag_notation(a_tags),' ');
+ l_tags := conv_postfix_to_infix_sql(shunt_logical_expression(tokenize_tags_string(l_tags)),'tags');
+ l_tags := replace(l_tags, '|',' or ');
+ l_tags := replace(l_tags ,'&',' and ');
+ l_tags := replace(l_tags ,'!','not');
+ return l_tags;
+ end;
+
+
+ /*
+ Having a base set of suites we will do a further filter down if there are
+ any tags defined.
+ */
+ function get_tags_suites (
+ a_suite_items ut_suite_cache_rows,
+ a_tags varchar2
+ ) return ut_suite_cache_rows is
+ l_results ut_suite_cache_rows;
+ l_sql varchar2(32000);
+ l_tags_filter_clause varchar2(4000):= create_where_filter(a_tags);
+ begin
+ l_sql :=
+ q'[
+ with
+ suites_mv as (
+ select c.id,value(c) as obj,c.path as path,c.self_type,c.object_owner,c.tags as tags
+ from table(:suite_items) c
+ ),
+ propagate_tags_from_ancestors as (
+ select c.id, c.self_type, c.path, c.object_owner, cast(collect(c_tags.tag) as ut_varchar2_rows) as tags
+ from suites_mv c
+ left join suites_mv t
+ on t.self_type in ('UT_SUITE','UT_SUITE_CONTEXT')
+ and c.path like t.path||'.%' /* all descendants */
+ and c.object_owner = t.object_owner
+ outer apply (select column_value tag from table(c.tags) union select column_value tag from table(t.tags) ) c_tags
+ group by c.id, c.self_type, c.path, c.object_owner
+ ),
+ filter_by_tags as (
+ select *
+ from propagate_tags_from_ancestors x
+ where ( ]'||l_tags_filter_clause||q'[ )
+ ),
+ only_items_with_tests as (
+ select item.obj
+ from suites_mv item
+ where exists (
+ select 1
+ from filter_by_tags tst
+ where tst.self_type in ('UT_TEST')
+ and (tst.path||'.' like item.path || '.%' /*all descendants and self*/ and item.object_owner = tst.object_owner)
+ )
+ )
+ select obj from only_items_with_tests]';
+ execute immediate l_sql bulk collect into l_results using a_suite_items;
+
+ return l_results;
+ end;
+
+ function apply(
+ a_unfiltered_rows ut_suite_cache_rows,
+ a_tags varchar2 := null
+ ) return ut_suite_cache_rows is
+ l_suite_items ut_suite_cache_rows := a_unfiltered_rows;
+ begin
+ if length(a_tags) > 0 then
+ l_suite_items := get_tags_suites(l_suite_items,a_tags);
+ end if;
+
+ return l_suite_items;
+ end;
+
+begin
+ --Define operators precedence
+ g_precedence('!'):=4;
+ g_precedence('&'):=3;
+ g_precedence('|'):=2;
+ g_precedence(')'):=1;
+ g_precedence('('):=1;
+
+end ut_suite_tag_filter;
+/
diff --git a/source/core/ut_suite_tag_filter.pks b/source/core/ut_suite_tag_filter.pks
new file mode 100644
index 000000000..bb3d4ad73
--- /dev/null
+++ b/source/core/ut_suite_tag_filter.pks
@@ -0,0 +1,55 @@
+create or replace noneditionable package ut_suite_tag_filter authid definer is
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ /**
+ * Package that will filter suites by tags
+ *
+ */
+
+ /*
+ * Return table of tokens character by character
+ */
+ function tokenize_tags_string(a_tags in varchar2) return ut_varchar2_list;
+
+ /*
+ * Function that uses Dijkstra algorithm to parse mathematical and logical expression
+ * and return a list of elements in Reverse Polish Notation ( postfix )
+ * As part of execution it will validate expression.
+ */
+ function shunt_logical_expression(a_tags in ut_varchar2_list) return ut_varchar2_list;
+
+ /*
+ * Function that converts postfix notation into infix and creating a string of sql filter
+ * that checking a tags collections for tags according to posted logic.
+ */
+ function conv_postfix_to_infix_sql(a_postfix_exp in ut_varchar2_list,a_tags_column_name in varchar2)
+ return varchar2;
+
+ /*
+ * Generates a part where clause sql
+ */
+ function create_where_filter(a_tags varchar2)
+ return varchar2;
+
+ function apply(
+ a_unfiltered_rows ut_suite_cache_rows,
+ a_tags varchar2 := null
+ ) return ut_suite_cache_rows;
+
+end ut_suite_tag_filter;
+/
diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb
index 05339a831..c8ced3bb9 100644
--- a/source/core/ut_utils.pkb
+++ b/source/core/ut_utils.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_utils is
+create or replace noneditionable package body ut_utils is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -19,10 +19,13 @@ create or replace package body ut_utils is
/**
* Constants regex used to validate XML name
*/
- gc_invalid_first_xml_char constant varchar2(50) := '[^_a-zA-Z]';
- gc_invalid_xml_char constant varchar2(50) := '[^_a-zA-Z0-9\.-]';
- gc_full_valid_xml_name constant varchar2(50) := '^([_a-zA-Z])([_a-zA-Z0-9\.-])*$';
+ gc_invalid_first_xml_char constant varchar2(50) := '[^_[:alpha:]]';
+ gc_invalid_xml_char constant varchar2(50) := '[^_[:alnum:]\.-]';
+ gc_full_valid_xml_name constant varchar2(50) := '^([[:alpha:]])([_[:alnum:]\.-])*$';
gc_owner_hash constant integer(11) := dbms_utility.get_hash_value( ut_owner(), 0, power(2,31)-1);
+ gc_open_chars constant varchar2(4):= '[{(<';
+ gc_close_chars constant varchar2(4):= ']})>';
+ gc_max_plsql_source_len constant integer := 32767;
function surround_with(a_value varchar2, a_quote_char varchar2) return varchar2 is
begin
@@ -91,8 +94,8 @@ create or replace package body ut_utils is
a_max_output_len in number := gc_max_output_string_length
) return varchar2 is
l_result varchar2(32767);
- c_length constant integer := coalesce( length( a_value ), 0 );
- c_max_input_string_length constant integer := a_max_output_len - coalesce( length( a_quote_char ) * 2, 0 );
+ c_length constant integer := coalesce( lengthb( a_value ), 0 );
+ c_max_input_string_length constant integer := a_max_output_len - coalesce( lengthb( a_quote_char ) * 2, 0 );
c_overflow_substr_len constant integer := c_max_input_string_length - gc_more_data_string_len;
begin
if c_length = 0 then
@@ -111,8 +114,8 @@ create or replace package body ut_utils is
a_max_output_len in number := gc_max_output_string_length
) return varchar2 is
l_result varchar2(32767);
- c_length constant integer := coalesce(dbms_lob.getlength(a_value), 0);
- c_max_input_string_length constant integer := a_max_output_len - coalesce( length( a_quote_char ) * 2, 0 );
+ c_length constant integer := coalesce(ut_utils.lengthb_clob(a_value), 0);
+ c_max_input_string_length constant integer := a_max_output_len - coalesce( lengthb( a_quote_char ) * 2, 0 );
c_overflow_substr_len constant integer := c_max_input_string_length - gc_more_data_string_len;
begin
if a_value is null then
@@ -134,7 +137,7 @@ create or replace package body ut_utils is
) return varchar2 is
l_result varchar2(32767);
c_length constant integer := coalesce(dbms_lob.getlength(a_value), 0);
- c_max_input_string_length constant integer := a_max_output_len - coalesce( length( a_quote_char ) * 2, 0 );
+ c_max_input_string_length constant integer := a_max_output_len - coalesce( lengthb( a_quote_char ) * 2, 0 );
c_overflow_substr_len constant integer := c_max_input_string_length - gc_more_data_string_len;
begin
if a_value is null then
@@ -350,7 +353,7 @@ create or replace package body ut_utils is
function get_utplsql_objects_list return ut_object_names is
l_result ut_object_names;
begin
- select distinct ut_object_name(sys_context('userenv','current_user'), o.object_name)
+ select /*+ no_parallel */ distinct ut_object_name(sys_context('userenv','current_user'), o.object_name)
bulk collect into l_result
from user_objects o
where o.object_name = 'UT' or object_name like 'UT\_%' escape '\'
@@ -411,7 +414,7 @@ create or replace package body ut_utils is
if a_list is null then
a_list := ut_varchar2_rows();
end if;
- if length(a_item) > gc_max_storage_varchar2_len then
+ if lengthb(a_item) > gc_max_storage_varchar2_len then
append_to_list(
a_list,
ut_utils.convert_collection(
@@ -467,7 +470,7 @@ create or replace package body ut_utils is
l_result := ut_varchar2_rows();
for i in 1 .. a_collection.count loop
l_result.extend();
- l_result(i) := substr(a_collection(i),1,gc_max_storage_varchar2_len);
+ l_result(i) := substrb(a_collection(i),1,gc_max_storage_varchar2_len);
end loop;
end if;
return l_result;
@@ -532,8 +535,8 @@ create or replace package body ut_utils is
procedure flush_lines(a_lines ut_varchar2_rows, a_offset integer) is
begin
if a_lines is not empty then
- insert into ut_dbms_output_cache (seq_no,text)
- select rownum+a_offset, column_value
+ insert /*+ no_parallel */ into ut_dbms_output_cache (seq_no,text)
+ select /*+ no_parallel */ rownum+a_offset, column_value
from table(a_lines);
end if;
end;
@@ -558,7 +561,7 @@ create or replace package body ut_utils is
c_lines_limit constant integer := 10000;
pragma autonomous_transaction;
begin
- open l_lines_data for select text from ut_dbms_output_cache order by seq_no;
+ open l_lines_data for select /*+ no_parallel */ text from ut_dbms_output_cache order by seq_no;
loop
fetch l_lines_data bulk collect into l_lines limit c_lines_limit;
for i in 1 .. l_lines.count loop
@@ -570,6 +573,7 @@ create or replace package body ut_utils is
end loop;
exit when l_lines_data%notfound;
end loop;
+ close l_lines_data;
execute immediate 'truncate table ut_dbms_output_cache';
commit;
end;
@@ -646,104 +650,244 @@ create or replace package body ut_utils is
function xmlgen_escaped_string(a_string in varchar2) return varchar2 is
l_result varchar2(4000) := a_string;
- l_sql varchar2(32767) := q'!select q'[!'||a_string||q'!]' as "!'||a_string||'" from dual';
+ l_sql varchar2(32767) := q'!select /*+ no_parallel */ q'[!'||a_string||q'!]' as "!'||a_string||'" from dual';
begin
if a_string is not null then
- select extract(dbms_xmlgen.getxmltype(l_sql),'/*/*/*').getRootElement()
+ select /*+ no_parallel */ extract(dbms_xmlgen.getxmltype(l_sql),'/*/*/*').getRootElement()
into l_result
from dual;
end if;
return l_result;
end;
- function replace_multiline_comments(a_source clob) return clob is
- l_result clob;
- l_ml_comment_start binary_integer := 1;
- l_comment_start binary_integer := 1;
- l_text_start binary_integer := 1;
- l_escaped_text_start binary_integer := 1;
- l_escaped_text_end_char varchar2(1 char);
- l_end binary_integer := 1;
- l_ml_comment clob;
- l_newlines_count binary_integer;
- l_offset binary_integer := 1;
- l_length binary_integer := coalesce(dbms_lob.getlength(a_source), 0);
- begin
- l_ml_comment_start := instr(a_source,'/*');
- l_comment_start := instr(a_source,'--');
- l_text_start := instr(a_source,'''');
- l_escaped_text_start := instr(a_source,q'[q']');
- while l_offset > 0 and l_ml_comment_start > 0 loop
-
- if l_ml_comment_start > 0 and (l_ml_comment_start < l_comment_start or l_comment_start = 0)
- and (l_ml_comment_start < l_text_start or l_text_start = 0)and (l_ml_comment_start < l_escaped_text_start or l_escaped_text_start = 0)
- then
- l_end := instr(a_source,'*/',l_ml_comment_start+2);
- append_to_clob(l_result, dbms_lob.substr(a_source, l_ml_comment_start-l_offset, l_offset));
+ function scan_line(a_line in varchar2, a_in_ml_comment in out boolean) return varchar2 is
+ -- Normal scan
+ l_remaining varchar2(32767) := a_line;
+ l_line varchar2(32767);
+ l_ml_start binary_integer;
+ l_comment_start binary_integer;
+ l_text_start binary_integer;
+ l_eq_text_start binary_integer;
+ l_eq_end_char varchar2(1 char);
+ l_pos binary_integer;
+ l_end binary_integer;
+ l_ml_end binary_integer;
+ begin
+ if a_in_ml_comment then
+ l_ml_end := instr(l_remaining, '*/');
+ if l_ml_end > 0 then
+ a_in_ml_comment := false;
+ l_remaining := substr(l_remaining, l_ml_end + 2);
+ else
+ return null;
+ end if;
+ end if;
+
+ loop
+ exit when l_remaining is null;
+ l_ml_start := instr(l_remaining, '/*');
+ l_comment_start := instr(l_remaining, '--');
+ l_text_start := instr(l_remaining, '''');
+ -- q' always puts ' at l_text_start; just check the char immediately before it
+ l_eq_text_start := case
+ when l_text_start > 1 and substr(l_remaining, l_text_start - 1, 1) = 'q'
+ then l_text_start - 1
+ else 0
+ end;
+ -- Sentinel gc_max_plsql_source_len means "not present"; 32767 is beyond any VARCHAR2 position
+ l_pos := least(
+ case when l_ml_start > 0 then l_ml_start else gc_max_plsql_source_len end,
+ case when l_comment_start > 0 then l_comment_start else gc_max_plsql_source_len end,
+ case when l_text_start > 0 then l_text_start else gc_max_plsql_source_len end,
+ case when l_eq_text_start > 0 then l_eq_text_start else gc_max_plsql_source_len end
+ );
+
+ if l_pos = gc_max_plsql_source_len then
+ l_line := l_line || l_remaining;
+ exit;
+ end if;
+
+ l_line := l_line || substr(l_remaining, 1, l_pos - 1);
+ l_remaining := substr(l_remaining, l_pos);
+ -- l_remaining now starts exactly at the token; all branch offsets below are relative to 1
+ if l_pos = l_eq_text_start then
+ -- q-quoted string: l_remaining starts at 'q', delimiter is at position 3
+ l_eq_end_char := translate(substr(l_remaining, 3, 1), gc_open_chars, gc_close_chars);
+ l_end := instr(l_remaining, l_eq_end_char || '''', 4);
if l_end > 0 then
- l_ml_comment := substr(a_source, l_ml_comment_start, l_end-l_ml_comment_start);
- l_newlines_count := length( l_ml_comment ) - length( translate( l_ml_comment, 'a'||chr(10), 'a') );
- if l_newlines_count > 0 then
- append_to_clob(l_result, lpad( chr(10), l_newlines_count, chr(10) ) );
- end if;
- l_end := l_end + 2;
+ l_line := l_line || substr(l_remaining, 1, l_end + 1);
+ l_remaining := substr(l_remaining, l_end + 2);
+ else
+ l_line := l_line || l_remaining;
+ exit;
end if;
- else
- if l_comment_start > 0 and (l_comment_start < l_ml_comment_start or l_ml_comment_start = 0)
- and (l_comment_start < l_text_start or l_text_start = 0) and (l_comment_start < l_escaped_text_start or l_escaped_text_start = 0)
- then
- l_end := instr(a_source,chr(10),l_comment_start+2);
- if l_end > 0 then
- l_end := l_end + 1;
- end if;
- elsif l_text_start > 0 and (l_text_start < l_ml_comment_start or l_ml_comment_start = 0)
- and (l_text_start < l_comment_start or l_comment_start = 0) and (l_text_start < l_escaped_text_start or l_escaped_text_start = 0)
- then
- l_end := instr(a_source,q'[']',l_text_start+1);
-
- --skip double quotes while searching for end of quoted text
- while l_end > 0 and l_end = instr(a_source,q'['']',l_text_start+1) loop
- l_end := instr(a_source,q'[']',l_end+1);
- end loop;
- if l_end > 0 then
- l_end := l_end + 1;
+ elsif l_pos = l_ml_start then
+ -- Multi-line comment: l_remaining starts at '/*', so end search starts at 3
+ l_ml_end := instr(l_remaining, '*/', 3);
+ if l_ml_end > 0 then
+ l_remaining := substr(l_remaining, l_ml_end + 2);
+ else
+ a_in_ml_comment := true;
+ -- preserve trailing newline if present — it belongs to this line, not the comment
+ if substr(l_remaining, -1) = chr(10) then
+ l_line := l_line || chr(10);
end if;
+ return l_line;
+ end if;
+
+ elsif l_pos = l_comment_start then
+ -- Single-line comment: everything from here is comment, keep and stop
+ l_line := l_line || l_remaining;
+ return l_line;
- elsif l_escaped_text_start > 0 and (l_escaped_text_start < l_ml_comment_start or l_ml_comment_start = 0)
- and (l_escaped_text_start < l_comment_start or l_comment_start = 0) and (l_escaped_text_start < l_text_start or l_text_start = 0)
- then
- --translate char "[" from the start of quoted text "q'[someting]'" into "]"
- l_escaped_text_end_char := translate( substr(a_source, l_escaped_text_start + 2, 1), '[{(<', ']})>');
- l_end := instr(a_source,l_escaped_text_end_char||'''',l_escaped_text_start + 3 );
- if l_end > 0 then
- l_end := l_end + 2;
+ else
+ -- Regular string literal: l_remaining starts at the opening quote
+ -- scan from position 2 to skip the opening quote
+ l_end := 2;
+ loop
+ l_end := instr(l_remaining, '''', l_end);
+ exit when l_end = 0;
+ if substr(l_remaining, l_end, 2) = '''''' then
+ l_end := l_end + 2; -- skip escaped quote pair
+ else
+ exit; -- real closing quote
end if;
- end if;
+ end loop;
- if l_end = 0 then
- append_to_clob(l_result, substr(a_source, l_offset, l_length-l_offset));
+ if l_end > 0 then
+ l_line := l_line || substr(l_remaining, 1, l_end);
+ l_remaining := substr(l_remaining, l_end + 1);
else
- append_to_clob(l_result, substr(a_source, l_offset, l_end-l_offset));
+ l_line := l_line || l_remaining;
+ exit;
end if;
+
end if;
- l_offset := l_end;
- if l_offset >= l_ml_comment_start then
- l_ml_comment_start := instr(a_source,'/*',l_offset);
+ end loop;
+
+ return l_line;
+ end;
+
+ function replace_multiline_comments(a_source dbms_preprocessor.source_lines_t)
+ return dbms_preprocessor.source_lines_t
+ is
+ l_result dbms_preprocessor.source_lines_t;
+ l_line varchar2(32767);
+ l_remaining varchar2(32767);
+ l_in_ml_comment boolean := false;
+ l_ml_end binary_integer;
+ l_has_ml_comment boolean := false;
+ begin
+ if a_source.count = 0 then
+ return a_source;
+ end if;
+
+ -- Fast pre-scan to check for presence of multi-line comments; if none, return original source unmodified
+ for i in 1 .. a_source.count loop
+ if instr(a_source(i), '/*') > 0 then
+ l_has_ml_comment := true;
+ exit;
end if;
- if l_offset >= l_comment_start then
- l_comment_start := instr(a_source,'--',l_offset);
+ end loop;
+
+ if not l_has_ml_comment then
+ return a_source;
+ end if;
+
+ for i in 1 .. a_source.count loop
+ l_line := a_source(i);
+
+ -- Fast path: inside multi-line comment
+ if l_in_ml_comment then
+ l_ml_end := instr(l_line, '*/');
+ if l_ml_end > 0 then
+ l_in_ml_comment := false;
+ l_line := substr(l_line, l_ml_end + 2);
+ -- fall through to normal scan
+ else
+ l_result(i) := '';
+ continue;
+ end if;
end if;
- if l_offset >= l_text_start then
- l_text_start := instr(a_source,'''',l_offset);
+
+ -- Fast path: no special tokens on this line
+ if instr(l_line, '/') = 0
+ and instr(l_line, '-') = 0
+ and instr(l_line, '''') = 0
+ then
+ l_result(i) := l_line;
+ continue;
end if;
- if l_offset >= l_escaped_text_start then
- l_escaped_text_start := instr(a_source,q'[q']',l_offset);
+
+ -- Normal scan
+ l_remaining := l_line;
+ l_line := scan_line(l_remaining, l_in_ml_comment);
+ if l_line is null then
+ l_line := '';
end if;
+ l_result(i) := l_line;
end loop;
- append_to_clob(l_result, substr(a_source, l_end));
+
return l_result;
- end;
+ end replace_multiline_comments;
+
+ function strip_create_header_lines(a_source dbms_preprocessor.source_lines_t)
+ return dbms_preprocessor.source_lines_t
+ is
+ l_result dbms_preprocessor.source_lines_t := a_source;
+ l_rebased dbms_preprocessor.source_lines_t;
+ l_create_line pls_integer;
+ l_header_line pls_integer;
+ l_header_pos pls_integer := 0;
+ begin
+ if l_result.count = 0 then
+ return l_result;
+ end if;
+
+ -- remove comment lines that contain "-- create or replace" and find first CREATE
+ for i in 1..l_result.count loop
+ l_result(i) := regexp_replace(l_result(i), '^.*[-]{2,}\s*create(\s+or\s+replace).*$', null, 1, 1, 'i');
+ if l_create_line is null and regexp_like(l_result(i), '(^|[[:space:]])create([[:space:]]|$)', 'i') then
+ l_create_line := i;
+ end if;
+ end loop;
+
+ -- find first occurrence of object keyword after CREATE (may be on later line)
+ if l_create_line is not null then
+ for i in l_create_line..l_result.count loop
+ l_header_pos := regexp_instr(
+ l_result(i),
+ '(^|[[:space:]])(package|type|procedure|function)([[:space:]]|$)',
+ 1, 1, 0, 'i', 2
+ );
+ if l_header_pos > 0 then
+ l_header_line := i;
+ exit;
+ end if;
+ end loop;
+
+ if l_header_line is not null then
+ -- keep from keyword onward on the header line
+ l_result(l_header_line) := substr(l_result(l_header_line), l_header_pos);
+ -- remove "OWNER." from create or replace statement.
+ -- Owner is not supported along with AUTHID - see issue https://github.com/utPLSQL/utPLSQL/issues/1088
+ l_result(l_header_line) := regexp_replace(
+ l_result(l_header_line),
+ '^(package|type|procedure|function)\s+("?[[:alpha:]][[:alnum:]$#_]*"?\.)(.*)',
+ '\1 \3', 1, 1, 'ni'
+ );
+
+ -- rebase so header line becomes line 1 (matches preprocessor expectations)
+ for i in l_header_line .. l_result.count loop
+ l_rebased(i - l_header_line + 1) := l_result(i);
+ end loop;
+ return l_rebased;
+ end if;
+ end if;
+
+ return l_result;
+ end strip_create_header_lines;
function get_child_reporters(a_for_reporters ut_reporters_info := null) return ut_reporters_info is
l_for_reporters ut_reporters_info := a_for_reporters;
@@ -753,7 +897,7 @@ create or replace package body ut_utils is
l_for_reporters := ut_reporters_info(ut_reporter_info('UT_REPORTER_BASE','N','N','N'));
end if;
- select /*+ cardinality(f 10) */
+ select /*+ no_parallel cardinality(f 10) */
ut_reporter_info(
object_name => t.type_name,
is_output_reporter =>
@@ -787,9 +931,9 @@ create or replace package body ut_utils is
/**
* Change string into unicode to match xmlgen format _00_
+ * See the section of Oracle documentation called: Escape of Characters in Generated XML Data
* https://docs.oracle.com/en/database/oracle/oracle-database/12.2/adxdb/generation-of-XML-data-from-relational-data.html#GUID-5BE09A7D-80D8-4734-B9AF-4A61F27FA9B2
- * secion v3.1.11.3475-develop
- */
+ */
function char_to_xmlgen_unicode(a_character varchar2) return varchar2 is
begin
return '_x00'||rawtohex(utl_raw.cast_to_raw(a_character))||'_';
@@ -801,7 +945,7 @@ create or replace package body ut_utils is
function build_valid_xml_name(a_preprocessed_name varchar2) return varchar2 is
l_post_processed varchar2(4000);
begin
- for i in (select regexp_substr( a_preprocessed_name ,'(.{1})', 1, level, null, 1 ) AS string_char,level level_no
+ for i in (select /*+ no_parallel */ regexp_substr( a_preprocessed_name ,'(.{1})', 1, level, null, 1 ) AS string_char,level level_no
from dual connect by level <= regexp_count(a_preprocessed_name, '(.{1})'))
loop
if i.level_no = 1 and regexp_like(i.string_char,gc_invalid_first_xml_char) then
@@ -880,14 +1024,47 @@ create or replace package body ut_utils is
return regexp_replace(a_item,a_prefix||a_connector);
end;
- function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash is
+ function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh256) return t_hash is
begin
- return dbms_crypto.hash(a_data, a_hash_type);
+ --We cannot run hash on null
+ return case when a_data is null then null else dbms_crypto.hash(a_data, a_hash_type) end;
end;
- function get_hash(a_data clob, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash is
+ function get_hash(a_data clob, a_hash_type binary_integer := dbms_crypto.hash_sh256) return t_hash is
begin
- return dbms_crypto.hash(a_data, a_hash_type);
+ --We cannot run hash on null
+ return case when a_data is null then null else dbms_crypto.hash(a_data, a_hash_type) end;
+ end;
+
+ function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2 is
+ l_start_pos pls_integer := 1;
+ l_end_pos pls_integer := 1;
+ l_result varchar2(4000);
+ l_item varchar2(4000);
+ l_at_end boolean := false;
+ begin
+ if a_random_seed is null then
+ l_result := a_path;
+ end if;
+ if a_path is not null then
+ loop
+ l_end_pos := instr(a_path,'.',l_start_pos);
+ if l_end_pos = 0 then
+ l_end_pos := length(a_path)+1;
+ l_at_end := true;
+ end if;
+ l_item := substr(a_path,l_start_pos,l_end_pos-l_start_pos);
+ if l_item is not null then
+ l_result :=
+ l_result ||
+ ut_utils.get_hash( to_char( dbms_utility.get_hash_value( l_item, 1, a_random_seed ) ) );
+ end if;
+ exit when l_at_end;
+ l_result := l_result || chr(0);
+ l_start_pos := l_end_pos + 1;
+ end loop;
+ end if;
+ return l_result;
end;
function qualified_sql_name(a_name varchar2) return varchar2 is
@@ -899,5 +1076,95 @@ create or replace package body ut_utils is
end;
end;
+ function interval_to_text(a_interval dsinterval_unconstrained) return varchar2 is
+ l_day varchar2(100) := extract(day from a_interval);
+ l_hour varchar2(100) := extract(hour from a_interval);
+ l_minute varchar2(100) := extract(minute from a_interval);
+ l_second varchar2(100) := extract(second from a_interval);
+ l_result varchar2(32767);
+ begin
+ l_result := case
+ when l_day = 1 then l_day ||' day'
+ when l_day > 1 then l_day ||' days'
+ end ||
+ case
+ when l_hour = 1 then ' '|| l_hour ||' hour'
+ when l_hour > 1 then ' '|| l_hour ||' hours'
+ end ||
+ case
+ when l_minute = 1 then ' '||l_minute ||' minute'
+ when l_minute > 1 then ' '||l_minute ||' minutes'
+ end ||
+ case
+ when l_second > 1 then ' '||l_second ||' seconds'
+ when l_second = 1 then ' '||l_second ||' second'
+ when l_second > 0 then ' '||l_second ||' seconds'
+ end;
+ l_result :=
+ case
+ when a_interval is null then 'NULL'
+ when l_result is null then '0 seconds'
+ else ltrim(l_result,' ')
+ end;
+
+ return l_result;
+ end;
+
+ function interval_to_text(a_interval yminterval_unconstrained) return varchar2 is
+ l_year varchar2(4) := extract(year from a_interval);
+ l_month varchar2(20) := extract(month from a_interval);
+ l_result varchar2(32767);
+ begin
+ l_result := case
+ when l_year = 1 then l_year ||' year'
+ when l_year > 1 then l_year ||' years'
+ end ||
+ case
+ when l_month > 1 then ' '||l_month ||' months'
+ when l_month = 1 then ' '||l_month ||' month'
+ end;
+ l_result :=
+ case
+ when a_interval is null then 'NULL'
+ when l_result is null then '0 months'
+ else ltrim(l_result,' ')
+ end;
+
+ return l_result;
+ end;
+
+
+ /*
+ * Inspired by
+ * https://stackoverflow.com/a/48782891
+ */
+ function lengthb_clob( a_clob clob) return integer is
+ l_blob blob;
+ l_desc_offset PLS_INTEGER := 1;
+ l_src_offset PLS_INTEGER := 1;
+ l_lang PLS_INTEGER := 0;
+ l_warning PLS_INTEGER := 0;
+ l_result integer;
+ begin
+ if a_clob = empty_clob() then
+ l_result := 0;
+ elsif a_clob is not null then
+ dbms_lob.createtemporary(l_blob,true);
+ dbms_lob.converttoblob
+ ( l_blob
+ , a_clob
+ , dbms_lob.getlength(a_clob)
+ , l_desc_offset
+ , l_src_offset
+ , dbms_lob.default_csid
+ , l_lang
+ , l_warning
+ );
+ l_result := length(l_blob);
+ dbms_lob.freetemporary(l_blob);
+ end if;
+ return l_result;
+ end;
+
end ut_utils;
/
diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks
index 3faa88f99..46c28f0f0 100644
--- a/source/core/ut_utils.pks
+++ b/source/core/ut_utils.pks
@@ -1,7 +1,7 @@
-create or replace package ut_utils authid definer is
+create or replace noneditionable package ut_utils authid definer is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ create or replace package ut_utils authid definer is
*
*/
- gc_version constant varchar2(50) := 'v3.1.11.3475-develop';
+ gc_version constant varchar2(50) := 'v3.2.4.4532-develop';
subtype t_executable_type is varchar2(30);
gc_before_all constant t_executable_type := 'beforeall';
@@ -121,15 +121,19 @@ create or replace package ut_utils authid definer is
ex_failed_open_cur exception;
gc_failed_open_cur constant pls_integer := -20218;
pragma exception_init (ex_failed_open_cur, -20218);
+
+ ex_invalid_tag_expression exception;
+ gc_invalid_tag_expression constant pls_integer := -20219;
+ pragma exception_init (ex_invalid_tag_expression, -20219);
gc_max_storage_varchar2_len constant integer := 4000;
gc_max_output_string_length constant integer := 4000;
gc_more_data_string constant varchar2(5) := '[...]';
gc_more_data_string_len constant integer := length( gc_more_data_string );
gc_number_format constant varchar2(100) := 'TM9';
- gc_date_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ss';
- gc_timestamp_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff';
- gc_timestamp_tz_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff tzh:tzm';
+ gc_date_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ss';
+ gc_timestamp_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ssxff';
+ gc_timestamp_tz_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ssxff tzh:tzm';
gc_null_string constant varchar2(4) := 'NULL';
gc_empty_string constant varchar2(5) := 'EMPTY';
@@ -401,7 +405,15 @@ create or replace package ut_utils authid definer is
/**
* Replaces multi-line comments in given source-code with empty lines
*/
- function replace_multiline_comments(a_source clob) return clob;
+ function replace_multiline_comments(a_source dbms_preprocessor.source_lines_t)
+ return dbms_preprocessor.source_lines_t;
+
+ /**
+ * Strips the CREATE header (possibly split across lines) so source starts at
+ * package/type/procedure/function keyword, preserving line numbers.
+ */
+ function strip_create_header_lines(a_source dbms_preprocessor.source_lines_t)
+ return dbms_preprocessor.source_lines_t;
/**
* Returns list of sub-type reporters for given list of super-type reporters
@@ -445,18 +457,38 @@ create or replace package ut_utils authid definer is
/*
* Wrapper function for calling dbms_crypto.hash
*/
- function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash;
+ function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh256) return t_hash;
/*
* Wrapper function for calling dbms_crypto.hash
*/
- function get_hash(a_data clob, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash;
+ function get_hash(a_data clob, a_hash_type binary_integer := dbms_crypto.hash_sh256) return t_hash;
+
+ /*
+ * Returns a hash value of suitepath based on input path and random seed
+ */
+ function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2;
/*
* Verifies that the input string is a qualified SQL name using sys.dbms_assert.qualified_sql_name
* If null value passed returns null
*/
function qualified_sql_name(a_name varchar2) return varchar2;
+
+ /*
+ * Return value of interval in plain english
+ */
+ function interval_to_text(a_interval dsinterval_unconstrained) return varchar2;
+
+ /*
+ * Return value of interval in plain english
+ */
+ function interval_to_text(a_interval yminterval_unconstrained) return varchar2;
+ /*
+ * Return length of CLOB in bytes. Null for NULL
+ */
+ function lengthb_clob( a_clob clob) return integer;
+
end ut_utils;
/
diff --git a/source/create_grants.sql b/source/create_grants.sql
index db26faa24..9318a7220 100644
--- a/source/create_grants.sql
+++ b/source/create_grants.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -60,6 +60,7 @@ grant execute on &&ut3_owner..ut_file_mapping to &ut3_user;
grant execute on &&ut3_owner..ut_file_mapper to &ut3_user;
grant execute on &&ut3_owner..ut_suite_items_info to &ut3_user;
grant execute on &&ut3_owner..ut_suite_item_info to &ut3_user;
+grant execute on &&ut3_owner..ut_suite_cache_rows to &ut3_user;
grant execute on &&ut3_owner..ut_run_info to &ut3_user;
grant execute on &&ut3_owner..ut_coverage_options to &ut3_user;
@@ -88,6 +89,8 @@ grant execute on &&ut3_owner..ut_be_like to &ut3_user;
grant execute on &&ut3_owner..ut_be_not_null to &ut3_user;
grant execute on &&ut3_owner..ut_be_null to &ut3_user;
grant execute on &&ut3_owner..ut_be_true to &ut3_user;
+grant execute on &&ut3_owner..ut_be_within to &ut3_user;
+grant execute on &&ut3_owner..ut_be_within_pct to &ut3_user;
grant execute on &&ut3_owner..ut_contain to &ut3_user;
grant execute on &&ut3_owner..ut_equal to &ut3_user;
grant execute on &&ut3_owner..ut_have_count to &ut3_user;
@@ -101,10 +104,10 @@ grant execute on &&ut3_owner..ut_tfs_junit_reporter to &ut3_user;
grant execute on &&ut3_owner..ut_documentation_reporter to &ut3_user;
grant execute on &&ut3_owner..ut_sonar_test_reporter to &ut3_user;
grant execute on &&ut3_owner..ut_realtime_reporter to &ut3_user;
+grant execute on &&ut3_owner..ut_tap_reporter to &ut3_user;
--reporters - coverage
grant execute on &&ut3_owner..ut_coverage_html_reporter to &ut3_user;
grant execute on &&ut3_owner..ut_coverage_sonar_reporter to &ut3_user;
-grant execute on &&ut3_owner..ut_coveralls_reporter to &ut3_user;
grant execute on &&ut3_owner..ut_coverage_cobertura_reporter to &ut3_user;
--reporters - debug
grant execute on &&ut3_owner..ut_debug_reporter to &ut3_user;
@@ -120,6 +123,7 @@ grant execute on &&ut3_owner..ut_console_reporter_base to &ut3_user;
grant execute on &&ut3_owner..ut_output_buffer_base to &ut3_user;
grant execute on &&ut3_owner..ut_output_table_buffer to &ut3_user;
grant execute on &&ut3_owner..ut_output_clob_table_buffer to &ut3_user;
+grant execute on &&ut3_owner..ut_output_bulk_buffer to &ut3_user;
--needed internally for selecting from annotation objects within packages that use invoker rights
grant execute on &&ut3_owner..ut_annotation_objs_cache_info to &ut3_user;
diff --git a/source/create_synonyms.sql b/source/create_synonyms.sql
index 3f18ba3ed..ab338f4f7 100644
--- a/source/create_synonyms.sql
+++ b/source/create_synonyms.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -36,10 +36,10 @@ set termout off
spool params.sql.tmp
select
case
- when upper('&&ut3_user') = 'PUBLIC' then q'[define action_type='or replace public'
+ when upper('&&ut3_user') = 'PUBLIC' then q'[define action_type='or replace noneditionable public'
]'||q'[define ut3_user=''
]'||q'[define grantee='PUBLIC']'
- else q'[define action_type='or replace'
+ else q'[define action_type='or replace noneditionable'
]'||q'[define grantee='&&ut3_user']
]'||q'[define ut3_user='&&ut3_user..']'
end
@@ -104,6 +104,8 @@ create &action_type. synonym &ut3_user.be_like for &&ut3_owner..be_like;
create &action_type. synonym &ut3_user.be_not_null for &&ut3_owner..be_not_null;
create &action_type. synonym &ut3_user.be_null for &&ut3_owner..be_null;
create &action_type. synonym &ut3_user.be_true for &&ut3_owner..be_true;
+create &action_type. synonym &ut3_user.be_within for &&ut3_owner..be_within;
+create &action_type. synonym &ut3_user.be_within_pct for &&ut3_owner..be_within_pct;
create &action_type. synonym &ut3_user.contain for &&ut3_owner..contain;
create &action_type. synonym &ut3_user.equal for &&ut3_owner..equal;
create &action_type. synonym &ut3_user.have_count for &&ut3_owner..have_count;
@@ -117,10 +119,10 @@ create &action_type. synonym &ut3_user.ut_tfs_junit_reporter for &&ut3_owner..ut
create &action_type. synonym &ut3_user.ut_documentation_reporter for &&ut3_owner..ut_documentation_reporter;
create &action_type. synonym &ut3_user.ut_sonar_test_reporter for &&ut3_owner..ut_sonar_test_reporter;
create &action_type. synonym &ut3_user.ut_realtime_reporter for &&ut3_owner..ut_realtime_reporter;
+create &action_type. synonym &ut3_user.ut_tap_reporter for &&ut3_owner..ut_tap_reporter;
--reporters - coverage
create &action_type. synonym &ut3_user.ut_coverage_html_reporter for &&ut3_owner..ut_coverage_html_reporter;
create &action_type. synonym &ut3_user.ut_coverage_sonar_reporter for &&ut3_owner..ut_coverage_sonar_reporter;
-create &action_type. synonym &ut3_user.ut_coveralls_reporter for &&ut3_owner..ut_coveralls_reporter;
create &action_type. synonym &ut3_user.ut_coverage_cobertura_reporter for &&ut3_owner..ut_coverage_cobertura_reporter;
--reporters - debug
create &action_type. synonym &ut3_user.ut_debug_reporter for &&ut3_owner..ut_debug_reporter;
diff --git a/source/create_synonyms_and_grants_for_public.sql b/source/create_synonyms_and_grants_for_public.sql
index 6d43c6b7a..25f14217e 100644
--- a/source/create_synonyms_and_grants_for_public.sql
+++ b/source/create_synonyms_and_grants_for_public.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/create_user_grants.sql b/source/create_user_grants.sql
index 4ca721cf4..c1b81efa4 100644
--- a/source/create_user_grants.sql
+++ b/source/create_user_grants.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/create_user_synonyms.sql b/source/create_user_synonyms.sql
index ee9f60fdd..7b0e49553 100644
--- a/source/create_user_synonyms.sql
+++ b/source/create_user_synonyms.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/create_utplsql_owner.sql b/source/create_utplsql_owner.sql
index 81325e925..a5b70ffda 100644
--- a/source/create_utplsql_owner.sql
+++ b/source/create_utplsql_owner.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -31,16 +31,13 @@ create user &ut3_owner_schema identified by "&ut3_password" default tablespace &
grant create session, create sequence, create procedure, create type, create table, create view, create synonym to &ut3_owner_schema;
-begin
- $if dbms_db_version.version < 18 $then
- execute immediate 'grant execute on dbms_lock to &ut3_owner_schema';
- $else
- null;
- $end
-end;
-/
-
+grant execute on dbms_lock to &ut3_owner_schema;
grant execute on dbms_crypto to &ut3_owner_schema;
+grant execute on dbms_lob to &ut3_owner_schema;
+grant execute on dbms_xmlgen to &ut3_owner_schema;
+grant execute on dbms_sql to &ut3_owner_schema;
+grant execute on dbms_random to &ut3_owner_schema;
+
grant alter session to &ut3_owner_schema;
diff --git a/source/define_ut3_owner_param.sql b/source/define_ut3_owner_param.sql
index 1b183b581..192e810b7 100644
--- a/source/define_ut3_owner_param.sql
+++ b/source/define_ut3_owner_param.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_compound_data_diff_tmp.sql b/source/expectations/data_values/ut_compound_data_diff_tmp.sql
index e96d716b5..edd27f766 100644
--- a/source/expectations/data_values/ut_compound_data_diff_tmp.sql
+++ b/source/expectations/data_values/ut_compound_data_diff_tmp.sql
@@ -1,7 +1,7 @@
create global temporary table ut_compound_data_diff_tmp(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb
index 69ba90811..763c133d7 100644
--- a/source/expectations/data_values/ut_compound_data_helper.pkb
+++ b/source/expectations/data_values/ut_compound_data_helper.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_compound_data_helper is
+create or replace noneditionable package body ut_compound_data_helper is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ create or replace package body ut_compound_data_helper is
where x.data_id = :act_guid
) ucd
)
- select
+ select /*+ no_parallel */
a."UT3$_Item#Data" as act_item_data,
a."UT3$_Data#Id" act_data_id,
e."UT3$_Item#Data" as exp_item_data,
@@ -103,7 +103,7 @@ create or replace package body ut_compound_data_helper is
full outer join actual_cols a
on e.exp_column_name = a.act_column_name
)
- select case
+ select /*+ no_parallel */ case
when exp_col_pos is null and act_col_pos is not null then '+'
when exp_col_pos is not null and act_col_pos is null then '-'
when exp_col_type_compare != act_col_type_compare then 't'
@@ -278,7 +278,7 @@ create or replace package body ut_compound_data_helper is
begin
if a_data_info is not empty then
for i in 1..a_data_info.count loop
- if a_data_info(i).has_nested_col = 0 then
+ if a_data_info(i).has_nested_col = 0 and a_data_info(i).column_type <> 'OBJECT' then
--Get XMLTABLE column list
add_element_to_list(l_xmltab_list,generate_xmltab_stmt(a_data_info(i)));
--Get Select statment list of columns
@@ -300,8 +300,8 @@ create or replace package body ut_compound_data_helper is
ut_utils.append_to_clob(a_partition_stmt,' row_number() over (partition by '||l_partition_tmp||' order by '||l_partition_tmp||' ) ');
if a_pk_table.count > 0 then
- -- If key defined do the join or these and where on diffrences
- a_join_by_stmt := ut_utils.table_to_clob(l_join_by_list, ' and ');
+ -- If key defined do the join or these and where on diffrences as well as on duplicate number when rows are same.
+ a_join_by_stmt := ' e."UT3$_Dup#No" = a."UT3$_Dup#No" and '||ut_utils.table_to_clob(l_join_by_list, ' and ');
elsif a_unordered then
-- If no key defined do the join on all columns
a_join_by_stmt := ' e."UT3$_Dup#No" = a."UT3$_Dup#No" and '||ut_utils.table_to_clob(l_equal_list, ' and ');
@@ -398,8 +398,12 @@ create or replace package body ut_compound_data_helper is
l_column_list ut_varchar2_list := ut_varchar2_list();
begin
for i in 1..a_cursor_info.count loop
- l_column_list.extend;
- l_column_list(l_column_list.last) := a_cursor_info(i).access_path;
+ --This avoids extracting single columns from nested objects.
+ --as we can go down to any level but we will lose visibility of parent.
+ if a_cursor_info(i).hierarchy_level = 1 then
+ l_column_list.extend;
+ l_column_list(l_column_list.last) := a_cursor_info(i).access_path;
+ end if;
end loop;
return l_column_list;
end;
@@ -486,7 +490,7 @@ create or replace package body ut_compound_data_helper is
)
unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:') )
)
- select rn, diff_type, diffed_row, pk_value
+ select /*+ no_parallel */ rn, diff_type, diffed_row, pk_value
from (
select rn, diff_type, diffed_row, pk_value,
case when diff_type = 'Actual:' then 1 else 2 end rnk,
@@ -564,7 +568,7 @@ create or replace package body ut_compound_data_helper is
procedure insert_diffs_result(a_diff_tab t_diff_tab, a_diff_id raw) is
begin
forall idx in 1..a_diff_tab.count save exceptions
- insert into ut_compound_data_diff_tmp
+ insert /*+ no_parallel */ into ut_compound_data_diff_tmp
( diff_id, act_item_data, act_data_id, exp_item_data, exp_data_id, item_no, duplicate_no )
values
(a_diff_id,
@@ -639,13 +643,13 @@ create or replace package body ut_compound_data_helper is
procedure save_cursor_data_for_diff(a_data_id raw, a_set_id integer, a_xml xmltype) is
begin
- insert into ut_compound_data_tmp (data_id, item_no, item_data) values (a_data_id, a_set_id, a_xml);
+ insert /*+ no_parallel */ into ut_compound_data_tmp (data_id, item_no, item_data) values (a_data_id, a_set_id, a_xml);
end;
function get_row_data_as_xml(a_data_id raw, a_max_rows integer) return ut_utils.t_clob_tab is
l_results ut_utils.t_clob_tab;
begin
- select xmlserialize( content ucd.item_data no indent)
+ select /*+ no_parallel */ xmlserialize( content ucd.item_data no indent)
bulk collect into l_results
from ut_compound_data_tmp tmp
,xmltable ( '/ROWSET' passing tmp.item_data
@@ -720,7 +724,7 @@ create or replace package body ut_compound_data_helper is
or (a.json_type != e.json_type)
or (decode(a.element_value,e.element_value,1,0) = 0)
)
- select difference_type,
+ select /*+ no_parallel */ difference_type,
act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path,
exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path
bulk collect into l_result_diff
@@ -742,7 +746,7 @@ create or replace package body ut_compound_data_helper is
l_diffs tt_json_diff_tab := compare_json_data(a_act_json_data,a_exp_json_data);
begin
forall i in 1..l_diffs.count
- insert into ut_json_data_diff_tmp (
+ insert /*+ no_parallel */ into ut_json_data_diff_tmp (
diff_id, difference_type,
act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path,
exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path
@@ -759,7 +763,7 @@ create or replace package body ut_compound_data_helper is
function get_json_diffs_type(a_diff_id raw) return tt_json_diff_type_tab is
l_diffs_summary tt_json_diff_type_tab := tt_json_diff_type_tab();
begin
- select d.difference_type,count(1)
+ select /*+ no_parallel */ d.difference_type,count(1)
bulk collect into l_diffs_summary
from ut_json_data_diff_tmp d
where diff_id = a_diff_id
@@ -771,7 +775,7 @@ create or replace package body ut_compound_data_helper is
function get_json_diffs_tmp(a_diff_id raw) return tt_json_diff_tab is
l_diffs tt_json_diff_tab;
begin
- select difference_type,
+ select /*+ no_parallel */ difference_type,
act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path,
exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path
bulk collect into l_diffs
@@ -781,6 +785,15 @@ create or replace package body ut_compound_data_helper is
return l_diffs;
end;
+ function get_json_object(a_json_t json) return json_element_t is
+ l_obj json_element_t;
+ begin
+ $if dbms_db_version.version >= 21 $then
+ l_obj := case when a_json_t is null then cast (null as json_element_t ) else json_element_t.parse(json_query(a_json_t, '$' returning clob)) end;
+ $end
+ return l_obj;
+ end;
+
begin
g_anytype_name_map(dbms_types.typecode_date) := 'DATE';
g_anytype_name_map(dbms_types.typecode_number) := 'NUMBER';
diff --git a/source/expectations/data_values/ut_compound_data_helper.pks b/source/expectations/data_values/ut_compound_data_helper.pks
index b39df0039..6069920c9 100644
--- a/source/expectations/data_values/ut_compound_data_helper.pks
+++ b/source/expectations/data_values/ut_compound_data_helper.pks
@@ -1,7 +1,7 @@
-create or replace package ut_compound_data_helper authid definer is
+create or replace noneditionable package ut_compound_data_helper authid definer is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -134,5 +134,7 @@ create or replace package ut_compound_data_helper authid definer is
function get_json_diffs_type(a_diff_id raw) return tt_json_diff_type_tab;
+ function get_json_object(a_json_t json) return json_element_t;
+
end;
/
diff --git a/source/expectations/data_values/ut_compound_data_tmp.sql b/source/expectations/data_values/ut_compound_data_tmp.sql
index c860a1872..01cedd97b 100644
--- a/source/expectations/data_values/ut_compound_data_tmp.sql
+++ b/source/expectations/data_values/ut_compound_data_tmp.sql
@@ -1,7 +1,7 @@
create global temporary table ut_compound_data_tmp(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/expectations/data_values/ut_compound_data_value.tpb b/source/expectations/data_values/ut_compound_data_value.tpb
index 627e14680..7db47827e 100644
--- a/source/expectations/data_values/ut_compound_data_value.tpb
+++ b/source/expectations/data_values/ut_compound_data_value.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_compound_data_value as
+create or replace noneditionable type body ut_compound_data_value as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_compound_data_value.tps b/source/expectations/data_values/ut_compound_data_value.tps
index 027b5ca26..c84a59db6 100644
--- a/source/expectations/data_values/ut_compound_data_value.tps
+++ b/source/expectations/data_values/ut_compound_data_value.tps
@@ -1,7 +1,7 @@
-create or replace type ut_compound_data_value force under ut_data_value(
+create or replace noneditionable type ut_compound_data_value force under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_cursor_column.tpb b/source/expectations/data_values/ut_cursor_column.tpb
index 13b1b3158..3513de23e 100644
--- a/source/expectations/data_values/ut_cursor_column.tpb
+++ b/source/expectations/data_values/ut_cursor_column.tpb
@@ -1,5 +1,21 @@
-create or replace type body ut_cursor_column as
-
+create or replace noneditionable type body ut_cursor_column as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
member procedure init(
self in out nocopy ut_cursor_column,
a_col_name varchar2, a_col_schema_name varchar2,
@@ -14,8 +30,11 @@ create or replace type body ut_cursor_column as
self.column_len := a_col_max_len; --length of column
self.column_precision := a_col_precision;
self.column_scale := a_col_scale;
- self.column_name := TRIM( BOTH '''' FROM a_col_name); --name of the column
self.column_type_name := coalesce(a_col_type_name,a_col_type); --type name e.g. test_dummy_object or varchar2
+ self.column_name := case when a_col_name is null and a_collection = 1 then
+ self.column_type_name
+ else TRIM( BOTH '''' FROM a_col_name)
+ end; --name of the column, however in nested object for collection name is not defined in cursor.
self.xml_valid_name := ut_utils.get_valid_xml_name(self.column_name);
self.display_path := case when a_access_path is null then
self.column_name
@@ -25,15 +44,16 @@ create or replace type body ut_cursor_column as
self.access_path := case when a_access_path is null then
self.xml_valid_name
else
- a_access_path||'/'||self.xml_valid_name
+ a_access_path||'/'||self.xml_valid_name
end; --Access path used for XMLTABLE query
self.filter_path := '/'||self.access_path; --Filter path will differ from access path in anydata type
+ --Transformed name needs to be build on full access path to avoid ambiguity when there is 3 or more levels of nesting.
self.transformed_name := case when length(self.xml_valid_name) > 30 then
- '"'||ut_compound_data_helper.get_fixed_size_hash(self.parent_name||self.xml_valid_name)||'"'
+ '"'||ut_compound_data_helper.get_fixed_size_hash(self.access_path)||'"'
when self.parent_name is null then
'"'||self.xml_valid_name||'"'
else
- '"'||ut_compound_data_helper.get_fixed_size_hash(self.parent_name||self.xml_valid_name)||'"'
+ '"'||ut_compound_data_helper.get_fixed_size_hash(self.access_path)||'"'
end; --when is nestd we need to hash name to make sure we dont exceed 30 char
self.column_type := a_col_type; --column type e.g. user_defined , varchar2
self.column_schema := a_col_schema_name; -- schema name
diff --git a/source/expectations/data_values/ut_cursor_column.tps b/source/expectations/data_values/ut_cursor_column.tps
index c865ea64b..fcf3fe002 100644
--- a/source/expectations/data_values/ut_cursor_column.tps
+++ b/source/expectations/data_values/ut_cursor_column.tps
@@ -1,7 +1,7 @@
-create or replace type ut_cursor_column authid current_user as object (
+create or replace noneditionable type ut_cursor_column authid current_user as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_cursor_column_tab.tps b/source/expectations/data_values/ut_cursor_column_tab.tps
index b1b33f7fe..169ba3ce4 100644
--- a/source/expectations/data_values/ut_cursor_column_tab.tps
+++ b/source/expectations/data_values/ut_cursor_column_tab.tps
@@ -1,7 +1,7 @@
-create or replace type ut_cursor_column_tab as
+create or replace noneditionable type ut_cursor_column_tab as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_cursor_details.tpb b/source/expectations/data_values/ut_cursor_details.tpb
index 20313ed96..ff204e3bd 100644
--- a/source/expectations/data_values/ut_cursor_details.tpb
+++ b/source/expectations/data_values/ut_cursor_details.tpb
@@ -1,9 +1,26 @@
-create or replace type body ut_cursor_details as
+create or replace noneditionable type body ut_cursor_details as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
member function equals( a_other ut_cursor_details, a_match_options ut_matcher_options ) return boolean is
l_diffs integer;
begin
- select count(1) into l_diffs
+ select /*+ no_parallel */ count(1) into l_diffs
from table(self.cursor_columns_info) a
full outer join table(a_other.cursor_columns_info) e
on decode(a.parent_name,e.parent_name,1,0)= 1
@@ -91,7 +108,11 @@ create or replace type body ut_cursor_details as
a_cursor_number in number
) return self as result is
l_columns_count pls_integer;
+ $if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then
l_columns_desc dbms_sql.desc_tab3;
+ $else
+ l_columns_desc dbms_sql.desc_tab4;
+ $end
l_is_collection boolean;
l_hierarchy_level integer := 1;
begin
@@ -139,7 +160,7 @@ create or replace type body ut_cursor_details as
member function contains_collection return boolean is
l_collection_elements number;
begin
- select count(1) into l_collection_elements
+ select /*+ no_parallel */ count(1) into l_collection_elements
from table(cursor_columns_info) c
where c.is_collection = 1 and rownum = 1;
return l_collection_elements > 0;
@@ -149,7 +170,7 @@ create or replace type body ut_cursor_details as
l_result ut_varchar2_list;
begin
--regexp_replace(c.access_path,'^\/?([^\/]+\/){1}')
- select fl.column_value
+ select /*+ no_parallel */ fl.column_value
bulk collect into l_result
from table(a_expected_columns) fl
where not exists (
@@ -179,7 +200,7 @@ create or replace type body ut_cursor_details as
select regexp_replace( column_value, c_xpath_extract_reg, '\5' ) col_names
from table(a_match_options.exclude.items)
)
- select value(x)
+ select /*+ no_parallel */ value(x)
bulk collect into l_result.cursor_columns_info
from table(self.cursor_columns_info) x
where exists(
@@ -192,7 +213,7 @@ create or replace type body ut_cursor_details as
select regexp_replace( column_value, c_xpath_extract_reg, '\5' ) col_names
from table(a_match_options.exclude.items)
)
- select value(x)
+ select /*+ no_parallel */ value(x)
bulk collect into l_result.cursor_columns_info
from table(self.cursor_columns_info) x
where not exists(
@@ -202,7 +223,7 @@ create or replace type body ut_cursor_details as
--Rewrite column order after columns been excluded
for i in (
- select parent_name, access_path, display_path, has_nested_col,
+ select /*+ no_parallel */ parent_name, access_path, display_path, has_nested_col,
transformed_name, hierarchy_level,
rownum as new_position, xml_valid_name,
column_name, column_type, column_type_name, column_schema,
@@ -224,7 +245,7 @@ create or replace type body ut_cursor_details as
member function get_xml_children(a_parent_name varchar2 := null) return xmltype is
l_result xmltype;
begin
- select xmlagg(xmlelement(evalname t.column_name,t.column_type_name))
+ select /*+ no_parallel */ xmlagg(xmlelement(evalname t.column_name,t.column_type_name))
into l_result
from table(self.cursor_columns_info) t
where (a_parent_name is null and parent_name is null and hierarchy_level = 1 and column_name is not null)
@@ -236,7 +257,7 @@ create or replace type body ut_cursor_details as
l_root varchar2(250);
begin
if self.cursor_columns_info.count > 0 then
- select x.access_path into l_root from table(self.cursor_columns_info) x
+ select /*+ no_parallel */ x.access_path into l_root from table(self.cursor_columns_info) x
where x.hierarchy_level = 1;
else
l_root := null;
diff --git a/source/expectations/data_values/ut_cursor_details.tps b/source/expectations/data_values/ut_cursor_details.tps
index 7092e834f..c50ed16d3 100644
--- a/source/expectations/data_values/ut_cursor_details.tps
+++ b/source/expectations/data_values/ut_cursor_details.tps
@@ -1,7 +1,7 @@
-create or replace type ut_cursor_details authid current_user as object (
+create or replace noneditionable type ut_cursor_details authid current_user as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value.tpb b/source/expectations/data_values/ut_data_value.tpb
index a644fe30e..05eb3022e 100644
--- a/source/expectations/data_values/ut_data_value.tpb
+++ b/source/expectations/data_values/ut_data_value.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value as
+create or replace noneditionable type body ut_data_value as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value.tps b/source/expectations/data_values/ut_data_value.tps
index ca7e18308..6e362a56d 100644
--- a/source/expectations/data_values/ut_data_value.tps
+++ b/source/expectations/data_values/ut_data_value.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value force authid current_user as object (
+create or replace noneditionable type ut_data_value force authid current_user as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@ create or replace type ut_data_value force authid current_user as object (
limitations under the License.
*/
data_type varchar2(250 char),
+ data_type_plsql varchar2(250 char),
self_type varchar2(250 char),
not instantiable member function is_null return boolean,
not instantiable member function to_string return varchar2,
diff --git a/source/expectations/data_values/ut_data_value_anydata.tpb b/source/expectations/data_values/ut_data_value_anydata.tpb
index a81b0f5c7..9fccb7582 100644
--- a/source/expectations/data_values/ut_data_value_anydata.tpb
+++ b/source/expectations/data_values/ut_data_value_anydata.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_anydata as
+create or replace noneditionable type body ut_data_value_anydata as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -48,11 +48,11 @@ create or replace type body ut_data_value_anydata as
begin
l_status := l_value.get'||self.compound_type||'(l_data); '||
case when self.compound_type = 'collection' then
- q'[ open :l_tmp_refcursor for select value(x) as "]'||
+ q'[ open :l_tmp_refcursor for select /*+ no_parallel */ value(x) as "]'||
ut_metadata.get_object_name(ut_metadata.get_collection_element(a_data_value))||
q'[" from table(l_data) x;]'
else
- q'[ open :l_tmp_refcursor for select l_data as "]'||ut_metadata.get_object_name(self.data_type)||
+ q'[ open :l_tmp_refcursor for select /*+ no_parallel */ l_data as "]'||ut_metadata.get_object_name(self.data_type)||
q'[" from dual;]'
end ||
'end;';
diff --git a/source/expectations/data_values/ut_data_value_anydata.tps b/source/expectations/data_values/ut_data_value_anydata.tps
index 5ed0406ca..6ad692d31 100644
--- a/source/expectations/data_values/ut_data_value_anydata.tps
+++ b/source/expectations/data_values/ut_data_value_anydata.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_anydata under ut_data_value_refcursor(
+create or replace noneditionable type ut_data_value_anydata under ut_data_value_refcursor(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_blob.tpb b/source/expectations/data_values/ut_data_value_blob.tpb
index 11af6d2f7..c961ebaab 100644
--- a/source/expectations/data_values/ut_data_value_blob.tpb
+++ b/source/expectations/data_values/ut_data_value_blob.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_blob as
+create or replace noneditionable type body ut_data_value_blob as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_blob as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'blob';
+ self.data_type_plsql := 'blob';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_blob.tps b/source/expectations/data_values/ut_data_value_blob.tps
index f557ffbce..94124bfec 100644
--- a/source/expectations/data_values/ut_data_value_blob.tps
+++ b/source/expectations/data_values/ut_data_value_blob.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_blob under ut_data_value(
+create or replace noneditionable type ut_data_value_blob under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_boolean.tpb b/source/expectations/data_values/ut_data_value_boolean.tpb
index 0645d3269..dd253442c 100644
--- a/source/expectations/data_values/ut_data_value_boolean.tpb
+++ b/source/expectations/data_values/ut_data_value_boolean.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_boolean as
+create or replace noneditionable type body ut_data_value_boolean as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_boolean as
self.data_value := ut_utils.boolean_to_int(a_value);
self.self_type := $$plsql_unit;
self.data_type := 'boolean';
+ self.data_type_plsql := 'boolean';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_boolean.tps b/source/expectations/data_values/ut_data_value_boolean.tps
index a254e05c0..b6df4e0b4 100644
--- a/source/expectations/data_values/ut_data_value_boolean.tps
+++ b/source/expectations/data_values/ut_data_value_boolean.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_boolean under ut_data_value(
+create or replace noneditionable type ut_data_value_boolean under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_clob.tpb b/source/expectations/data_values/ut_data_value_clob.tpb
index dacb390b6..76a55545e 100644
--- a/source/expectations/data_values/ut_data_value_clob.tpb
+++ b/source/expectations/data_values/ut_data_value_clob.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_clob as
+create or replace noneditionable type body ut_data_value_clob as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_clob as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'clob';
+ self.data_type_plsql := 'clob';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_clob.tps b/source/expectations/data_values/ut_data_value_clob.tps
index 1ff5d5623..686757e85 100644
--- a/source/expectations/data_values/ut_data_value_clob.tps
+++ b/source/expectations/data_values/ut_data_value_clob.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_clob under ut_data_value(
+create or replace noneditionable type ut_data_value_clob under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_date.tpb b/source/expectations/data_values/ut_data_value_date.tpb
index dd87156bc..2a1232a1c 100644
--- a/source/expectations/data_values/ut_data_value_date.tpb
+++ b/source/expectations/data_values/ut_data_value_date.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_date as
+create or replace noneditionable type body ut_data_value_date as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_date as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'date';
+ self.data_type_plsql := 'date';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_date.tps b/source/expectations/data_values/ut_data_value_date.tps
index 4435a3512..0bb9a17cf 100644
--- a/source/expectations/data_values/ut_data_value_date.tps
+++ b/source/expectations/data_values/ut_data_value_date.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_date under ut_data_value(
+create or replace noneditionable type ut_data_value_date under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_dsinterval.tpb b/source/expectations/data_values/ut_data_value_dsinterval.tpb
index ff9d5fe1e..adad0ef6d 100644
--- a/source/expectations/data_values/ut_data_value_dsinterval.tpb
+++ b/source/expectations/data_values/ut_data_value_dsinterval.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_dsinterval as
+create or replace noneditionable type body ut_data_value_dsinterval as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_dsinterval as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'interval day to second';
+ self.data_type_plsql := 'dsinterval_unconstrained';
return;
end;
@@ -31,7 +32,7 @@ create or replace type body ut_data_value_dsinterval as
overriding member function to_string return varchar2 is
begin
- return ut_utils.to_string(self.data_value);
+ return ut_utils.interval_to_text(self.data_value);
end;
overriding member function compare_implementation(a_other ut_data_value) return integer is
diff --git a/source/expectations/data_values/ut_data_value_dsinterval.tps b/source/expectations/data_values/ut_data_value_dsinterval.tps
index 114619bf2..3d15e2e09 100644
--- a/source/expectations/data_values/ut_data_value_dsinterval.tps
+++ b/source/expectations/data_values/ut_data_value_dsinterval.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_dsinterval under ut_data_value(
+create or replace noneditionable type ut_data_value_dsinterval under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@ create or replace type ut_data_value_dsinterval under ut_data_value(
See the License for the specific language governing permissions and
limitations under the License.
*/
- data_value dsinterval_unconstrained,
+ data_value interval day(9) to second(9),
constructor function ut_data_value_dsinterval(self in out nocopy ut_data_value_dsinterval, a_value dsinterval_unconstrained) return self as result,
overriding member function is_null return boolean,
overriding member function to_string return varchar2,
diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb
index 83916c30e..e1b5aa995 100644
--- a/source/expectations/data_values/ut_data_value_json.tpb
+++ b/source/expectations/data_values/ut_data_value_json.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_json as
+create or replace noneditionable type body ut_data_value_json as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -32,6 +32,13 @@ create or replace type body ut_data_value_json as
return;
end;
+ constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json) return self as result is
+ l_value json_element_t := ut_compound_data_helper.get_json_object(a_value);
+ begin
+ init(l_value);
+ return;
+ end;
+
overriding member function is_null return boolean is
begin
return (ut_utils.int_to_boolean(self.is_data_null));
@@ -140,7 +147,7 @@ create or replace type body ut_data_value_json as
l_result :=
case
when ut_compound_data_helper.insert_json_diffs(
- l_diff_id, self.json_tree.json_tree_info, l_other.json_tree.json_tree_info
+ l_diff_id, l_other.json_tree.json_tree_info, self.json_tree.json_tree_info
) > 0 then 1
else 0
end;
diff --git a/source/expectations/data_values/ut_data_value_json.tps b/source/expectations/data_values/ut_data_value_json.tps
index bfaccb2e2..c89088564 100644
--- a/source/expectations/data_values/ut_data_value_json.tps
+++ b/source/expectations/data_values/ut_data_value_json.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_json under ut_compound_data_value(
+create or replace noneditionable type ut_data_value_json under ut_compound_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ create or replace type ut_data_value_json under ut_compound_data_value(
json_tree ut_json_tree_details,
member procedure init (self in out nocopy ut_data_value_json, a_value json_element_t),
constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result,
+ constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json) return self as result,
overriding member function is_null return boolean,
overriding member function is_empty return boolean,
overriding member function to_string return varchar2,
diff --git a/source/expectations/data_values/ut_data_value_number.tpb b/source/expectations/data_values/ut_data_value_number.tpb
index b8a28a124..3ea08bbd4 100644
--- a/source/expectations/data_values/ut_data_value_number.tpb
+++ b/source/expectations/data_values/ut_data_value_number.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_number as
+create or replace noneditionable type body ut_data_value_number as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_number as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'number';
+ self.data_type_plsql := 'number';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_number.tps b/source/expectations/data_values/ut_data_value_number.tps
index 872ab405e..e7031de72 100644
--- a/source/expectations/data_values/ut_data_value_number.tps
+++ b/source/expectations/data_values/ut_data_value_number.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_number under ut_data_value(
+create or replace noneditionable type ut_data_value_number under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_refcursor.tpb b/source/expectations/data_values/ut_data_value_refcursor.tpb
index 801b35802..b7f79ac52 100644
--- a/source/expectations/data_values/ut_data_value_refcursor.tpb
+++ b/source/expectations/data_values/ut_data_value_refcursor.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_refcursor as
+create or replace noneditionable type body ut_data_value_refcursor as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -81,6 +81,7 @@ create or replace type body ut_data_value_refcursor as
self.self_type := $$plsql_unit;
self.data_id := sys_guid();
self.data_type := 'refcursor';
+ self.data_type_plsql := 'sys_refcursor';
self.compound_type := 'refcursor';
self.extract_path := '/*';
ut_compound_data_helper.cleanup_diff;
@@ -178,7 +179,7 @@ create or replace type body ut_data_value_refcursor as
l_missing_cols(l_missing_cols.last) := coalesce(a_column_diffs(i).expected_name, a_column_diffs(i).actual_name);
end if;
end loop;
- select value(i) bulk collect into l_result
+ select /*+ no_parallel */ value(i) bulk collect into l_result
from table(a_cursor_details) i
where i.access_path not in (
select c.column_value
diff --git a/source/expectations/data_values/ut_data_value_refcursor.tps b/source/expectations/data_values/ut_data_value_refcursor.tps
index 4de253b8f..90a305d32 100644
--- a/source/expectations/data_values/ut_data_value_refcursor.tps
+++ b/source/expectations/data_values/ut_data_value_refcursor.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_refcursor under ut_compound_data_value(
+create or replace noneditionable type ut_data_value_refcursor under ut_compound_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_timestamp.tpb b/source/expectations/data_values/ut_data_value_timestamp.tpb
index adb18e360..c6aabf7a3 100644
--- a/source/expectations/data_values/ut_data_value_timestamp.tpb
+++ b/source/expectations/data_values/ut_data_value_timestamp.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_timestamp as
+create or replace noneditionable type body ut_data_value_timestamp as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_timestamp as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'timestamp';
+ self.data_type_plsql := 'timestamp_unconstrained';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_timestamp.tps b/source/expectations/data_values/ut_data_value_timestamp.tps
index ebd00762c..938b0d63c 100644
--- a/source/expectations/data_values/ut_data_value_timestamp.tps
+++ b/source/expectations/data_values/ut_data_value_timestamp.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_timestamp under ut_data_value(
+create or replace noneditionable type ut_data_value_timestamp under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_timestamp_ltz.tpb b/source/expectations/data_values/ut_data_value_timestamp_ltz.tpb
index 0f41e16b1..025314f4b 100644
--- a/source/expectations/data_values/ut_data_value_timestamp_ltz.tpb
+++ b/source/expectations/data_values/ut_data_value_timestamp_ltz.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_timestamp_ltz as
+create or replace noneditionable type body ut_data_value_timestamp_ltz as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_timestamp_ltz as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'timestamp with local time zone';
+ self.data_type_plsql := 'timestamp_ltz_unconstrained';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_timestamp_ltz.tps b/source/expectations/data_values/ut_data_value_timestamp_ltz.tps
index 3608ebd37..e7c61a878 100644
--- a/source/expectations/data_values/ut_data_value_timestamp_ltz.tps
+++ b/source/expectations/data_values/ut_data_value_timestamp_ltz.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_timestamp_ltz under ut_data_value(
+create or replace noneditionable type ut_data_value_timestamp_ltz under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_timestamp_tz.tpb b/source/expectations/data_values/ut_data_value_timestamp_tz.tpb
index 710a313a7..6af5fea4d 100644
--- a/source/expectations/data_values/ut_data_value_timestamp_tz.tpb
+++ b/source/expectations/data_values/ut_data_value_timestamp_tz.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_timestamp_tz as
+create or replace noneditionable type body ut_data_value_timestamp_tz as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_timestamp_tz as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'timestamp with time zone';
+ self.data_type_plsql := 'timestamp_tz_unconstrained';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_timestamp_tz.tps b/source/expectations/data_values/ut_data_value_timestamp_tz.tps
index ca0bfc5cb..ad190d197 100644
--- a/source/expectations/data_values/ut_data_value_timestamp_tz.tps
+++ b/source/expectations/data_values/ut_data_value_timestamp_tz.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_timestamp_tz under ut_data_value(
+create or replace noneditionable type ut_data_value_timestamp_tz under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_varchar2.tpb b/source/expectations/data_values/ut_data_value_varchar2.tpb
index da4276d51..7a26d7571 100644
--- a/source/expectations/data_values/ut_data_value_varchar2.tpb
+++ b/source/expectations/data_values/ut_data_value_varchar2.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_varchar2 as
+create or replace noneditionable type body ut_data_value_varchar2 as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_varchar2 as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'varchar2';
+ self.data_type_plsql := 'varchar2(32767)';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_varchar2.tps b/source/expectations/data_values/ut_data_value_varchar2.tps
index 27bb720ce..56effd460 100644
--- a/source/expectations/data_values/ut_data_value_varchar2.tps
+++ b/source/expectations/data_values/ut_data_value_varchar2.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_varchar2 under ut_data_value(
+create or replace noneditionable type ut_data_value_varchar2 under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_xmltype.tpb b/source/expectations/data_values/ut_data_value_xmltype.tpb
index 3b10aadce..f5ffcbc9a 100644
--- a/source/expectations/data_values/ut_data_value_xmltype.tpb
+++ b/source/expectations/data_values/ut_data_value_xmltype.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_xmltype as
+create or replace noneditionable type body ut_data_value_xmltype as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_xmltype as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'xmltype';
+ self.data_type_plsql := 'xmltype';
return;
end;
diff --git a/source/expectations/data_values/ut_data_value_xmltype.tps b/source/expectations/data_values/ut_data_value_xmltype.tps
index 476097de3..2d0257918 100644
--- a/source/expectations/data_values/ut_data_value_xmltype.tps
+++ b/source/expectations/data_values/ut_data_value_xmltype.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_xmltype under ut_data_value(
+create or replace noneditionable type ut_data_value_xmltype under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_data_value_yminterval.tpb b/source/expectations/data_values/ut_data_value_yminterval.tpb
index 6d6a05b4a..8d12d3544 100644
--- a/source/expectations/data_values/ut_data_value_yminterval.tpb
+++ b/source/expectations/data_values/ut_data_value_yminterval.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_data_value_yminterval as
+create or replace noneditionable type body ut_data_value_yminterval as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ create or replace type body ut_data_value_yminterval as
self.data_value := a_value;
self.self_type := $$plsql_unit;
self.data_type := 'interval year to month';
+ self.data_type_plsql := 'yminterval_unconstrained';
return;
end;
@@ -31,7 +32,7 @@ create or replace type body ut_data_value_yminterval as
overriding member function to_string return varchar2 is
begin
- return ut_utils.to_string(self.data_value);
+ return ut_utils.interval_to_text(self.data_value);
end;
overriding member function compare_implementation(a_other ut_data_value) return integer is
diff --git a/source/expectations/data_values/ut_data_value_yminterval.tps b/source/expectations/data_values/ut_data_value_yminterval.tps
index d20dfcad7..8c3ad8079 100644
--- a/source/expectations/data_values/ut_data_value_yminterval.tps
+++ b/source/expectations/data_values/ut_data_value_yminterval.tps
@@ -1,7 +1,7 @@
-create or replace type ut_data_value_yminterval under ut_data_value(
+create or replace noneditionable type ut_data_value_yminterval under ut_data_value(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@ create or replace type ut_data_value_yminterval under ut_data_value(
See the License for the specific language governing permissions and
limitations under the License.
*/
- data_value yminterval_unconstrained,
+ data_value interval year(9) to month,
constructor function ut_data_value_yminterval(self in out nocopy ut_data_value_yminterval, a_value yminterval_unconstrained) return self as result,
overriding member function is_null return boolean,
overriding member function to_string return varchar2,
diff --git a/source/expectations/data_values/ut_json_data_diff_tmp.sql b/source/expectations/data_values/ut_json_data_diff_tmp.sql
index bfaaa1b72..4eda07d7c 100644
--- a/source/expectations/data_values/ut_json_data_diff_tmp.sql
+++ b/source/expectations/data_values/ut_json_data_diff_tmp.sql
@@ -1,7 +1,7 @@
create global temporary table ut_json_data_diff_tmp(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/source/expectations/data_values/ut_json_leaf.tpb b/source/expectations/data_values/ut_json_leaf.tpb
index 83f1009ef..d6c862286 100644
--- a/source/expectations/data_values/ut_json_leaf.tpb
+++ b/source/expectations/data_values/ut_json_leaf.tpb
@@ -1,5 +1,21 @@
-create or replace type body ut_json_leaf as
-
+create or replace noneditionable type body ut_json_leaf as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
member procedure init( self in out nocopy ut_json_leaf,
a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2,
a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2,
diff --git a/source/expectations/data_values/ut_json_leaf.tps b/source/expectations/data_values/ut_json_leaf.tps
index c38c3b238..ac478e34e 100644
--- a/source/expectations/data_values/ut_json_leaf.tps
+++ b/source/expectations/data_values/ut_json_leaf.tps
@@ -1,7 +1,7 @@
-create or replace type ut_json_leaf authid current_user as object (
+create or replace noneditionable type ut_json_leaf authid current_user as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_json_leaf_tab.tps b/source/expectations/data_values/ut_json_leaf_tab.tps
index 0c7a40b9f..c00f2a3a1 100644
--- a/source/expectations/data_values/ut_json_leaf_tab.tps
+++ b/source/expectations/data_values/ut_json_leaf_tab.tps
@@ -1,7 +1,7 @@
-create or replace type ut_json_leaf_tab as
+create or replace noneditionable type ut_json_leaf_tab as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb
index a333ea71c..0d457b2c5 100644
--- a/source/expectations/data_values/ut_json_tree_details.tpb
+++ b/source/expectations/data_values/ut_json_tree_details.tpb
@@ -1,4 +1,20 @@
-create or replace type body ut_json_tree_details as
+create or replace noneditionable type body ut_json_tree_details as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
member function get_json_type(a_json_piece json_element_t) return varchar2 is
begin
diff --git a/source/expectations/data_values/ut_json_tree_details.tps b/source/expectations/data_values/ut_json_tree_details.tps
index 0684ecafe..4f7e891be 100644
--- a/source/expectations/data_values/ut_json_tree_details.tps
+++ b/source/expectations/data_values/ut_json_tree_details.tps
@@ -1,7 +1,7 @@
-create or replace type ut_json_tree_details force as object (
+create or replace noneditionable type ut_json_tree_details force as object (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_key_anyval_pair.tps b/source/expectations/data_values/ut_key_anyval_pair.tps
index 4903282e9..6ed63ae7e 100644
--- a/source/expectations/data_values/ut_key_anyval_pair.tps
+++ b/source/expectations/data_values/ut_key_anyval_pair.tps
@@ -1,7 +1,7 @@
-create or replace type ut_key_anyval_pair force as object(
+create or replace noneditionable type ut_key_anyval_pair force as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_key_anyval_pairs.tps b/source/expectations/data_values/ut_key_anyval_pairs.tps
index 94bd5df54..a75305f79 100644
--- a/source/expectations/data_values/ut_key_anyval_pairs.tps
+++ b/source/expectations/data_values/ut_key_anyval_pairs.tps
@@ -1,7 +1,7 @@
-create or replace type ut_key_anyval_pairs as
+create or replace noneditionable type ut_key_anyval_pairs as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_key_anyvalues.tpb b/source/expectations/data_values/ut_key_anyvalues.tpb
index 93bf1eb53..4e6dd2c0f 100644
--- a/source/expectations/data_values/ut_key_anyvalues.tpb
+++ b/source/expectations/data_values/ut_key_anyvalues.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_key_anyvalues as
+create or replace noneditionable type body ut_key_anyvalues as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/data_values/ut_key_anyvalues.tps b/source/expectations/data_values/ut_key_anyvalues.tps
index 6dd32a76d..21a9a5c72 100644
--- a/source/expectations/data_values/ut_key_anyvalues.tps
+++ b/source/expectations/data_values/ut_key_anyvalues.tps
@@ -1,7 +1,7 @@
-create or replace type ut_key_anyvalues under ut_event_item (
+create or replace noneditionable type ut_key_anyvalues under ut_event_item (
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/json_objects_specs.sql b/source/expectations/json_objects_specs.sql
index c0d42e8fb..c0fc15ed6 100644
--- a/source/expectations/json_objects_specs.sql
+++ b/source/expectations/json_objects_specs.sql
@@ -1,10 +1,14 @@
BEGIN
-
- $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then
- dbms_output.put_line('Object exists , dont install');
- $else
+ null;
+ $if dbms_db_version.version < 21 $then
+ dbms_output.put_line('Installing json structures specs for native json.');
+ execute immediate q'[create or replace noneditionable TYPE JSON FORCE AUTHID CURRENT_USER AS OBJECT(
+ dummyobjt NUMBER
+) NOT FINAL NOT INSTANTIABLE;]';
+ $end
+ $if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then
dbms_output.put_line('Installing json structures specs.');
- execute immediate q'[create or replace TYPE JSON_Element_T FORCE AUTHID CURRENT_USER AS OBJECT(
+ execute immediate q'[create or replace noneditionable TYPE JSON_Element_T FORCE AUTHID CURRENT_USER AS OBJECT(
dummyobjt NUMBER,
STATIC FUNCTION parse(jsn VARCHAR2) RETURN JSON_Element_T,
STATIC FUNCTION parse(jsn CLOB) RETURN JSON_Element_T,
@@ -30,9 +34,9 @@ BEGIN
MEMBER FUNCTION get_Size(self IN JSON_ELEMENT_T) RETURN NUMBER
) NOT FINAL NOT INSTANTIABLE;]';
- execute immediate q'[create or replace TYPE JSON_KEY_LIST FORCE AS VARRAY(32767) OF VARCHAR2(4000);]';
+ execute immediate q'[create or replace noneditionable TYPE JSON_KEY_LIST FORCE AS VARRAY(32767) OF VARCHAR2(4000);]';
- execute immediate q'[create or replace TYPE JSON_Array_T FORCE AUTHID CURRENT_USER UNDER JSON_Element_T(
+ execute immediate q'[create or replace noneditionable TYPE JSON_Array_T FORCE AUTHID CURRENT_USER UNDER JSON_Element_T(
CONSTRUCTOR FUNCTION JSON_Array_T RETURN SELF AS RESULT,
MEMBER FUNCTION get(pos NUMBER) RETURN JSON_Element_T,
MEMBER FUNCTION get_String(pos NUMBER) RETURN VARCHAR2,
@@ -47,7 +51,7 @@ BEGIN
MEMBER FUNCTION get_Type(pos NUMBER) RETURN VARCHAR2
) FINAL;]';
- execute immediate q'[create or replace TYPE JSON_Object_T AUTHID CURRENT_USER UNDER JSON_Element_T(
+ execute immediate q'[create or replace noneditionable TYPE JSON_Object_T AUTHID CURRENT_USER UNDER JSON_Element_T(
CONSTRUCTOR FUNCTION JSON_Object_T RETURN SELF AS RESULT,
MEMBER FUNCTION get(key VARCHAR2) RETURN JSON_Element_T,
MEMBER FUNCTION get_Object(key VARCHAR2) RETURN JSON_OBJECT_T,
diff --git a/source/expectations/matchers/ut_be_between.tpb b/source/expectations/matchers/ut_be_between.tpb
index 7368ad5a7..83dfc4c25 100644
--- a/source/expectations/matchers/ut_be_between.tpb
+++ b/source/expectations/matchers/ut_be_between.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_between is
+create or replace noneditionable type body ut_be_between is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_between.tps b/source/expectations/matchers/ut_be_between.tps
index 7e254719e..7f1403847 100644
--- a/source/expectations/matchers/ut_be_between.tps
+++ b/source/expectations/matchers/ut_be_between.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_between under ut_matcher(
+create or replace noneditionable type ut_be_between under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_empty.tpb b/source/expectations/matchers/ut_be_empty.tpb
index 08523fb02..af233aa8f 100644
--- a/source/expectations/matchers/ut_be_empty.tpb
+++ b/source/expectations/matchers/ut_be_empty.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_empty as
+create or replace noneditionable type body ut_be_empty as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_empty.tps b/source/expectations/matchers/ut_be_empty.tps
index 76af402fe..a5672ba78 100644
--- a/source/expectations/matchers/ut_be_empty.tps
+++ b/source/expectations/matchers/ut_be_empty.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_empty under ut_matcher(
+create or replace noneditionable type ut_be_empty under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_false.tpb b/source/expectations/matchers/ut_be_false.tpb
index 671f43cbe..a02ac58cb 100644
--- a/source/expectations/matchers/ut_be_false.tpb
+++ b/source/expectations/matchers/ut_be_false.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_false as
+create or replace noneditionable type body ut_be_false as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_false.tps b/source/expectations/matchers/ut_be_false.tps
index 3d4e8dda9..d793844c1 100644
--- a/source/expectations/matchers/ut_be_false.tps
+++ b/source/expectations/matchers/ut_be_false.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_false under ut_matcher(
+create or replace noneditionable type ut_be_false under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_greater_or_equal.tpb b/source/expectations/matchers/ut_be_greater_or_equal.tpb
index 6877eb32c..6e564a90c 100644
--- a/source/expectations/matchers/ut_be_greater_or_equal.tpb
+++ b/source/expectations/matchers/ut_be_greater_or_equal.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_greater_or_equal AS
+create or replace noneditionable type body ut_be_greater_or_equal AS
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_greater_or_equal.tps b/source/expectations/matchers/ut_be_greater_or_equal.tps
index facd33b3d..d3e651958 100644
--- a/source/expectations/matchers/ut_be_greater_or_equal.tps
+++ b/source/expectations/matchers/ut_be_greater_or_equal.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_greater_or_equal under ut_comparison_matcher(
+create or replace noneditionable type ut_be_greater_or_equal under ut_comparison_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_greater_than.tpb b/source/expectations/matchers/ut_be_greater_than.tpb
index da46347af..08628ae3a 100644
--- a/source/expectations/matchers/ut_be_greater_than.tpb
+++ b/source/expectations/matchers/ut_be_greater_than.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_greater_than AS
+create or replace noneditionable type body ut_be_greater_than AS
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_greater_than.tps b/source/expectations/matchers/ut_be_greater_than.tps
index fa19a9bd4..745b19f08 100644
--- a/source/expectations/matchers/ut_be_greater_than.tps
+++ b/source/expectations/matchers/ut_be_greater_than.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_greater_than under ut_comparison_matcher(
+create or replace noneditionable type ut_be_greater_than under ut_comparison_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_less_or_equal.tpb b/source/expectations/matchers/ut_be_less_or_equal.tpb
index 98c4238eb..2df06a078 100644
--- a/source/expectations/matchers/ut_be_less_or_equal.tpb
+++ b/source/expectations/matchers/ut_be_less_or_equal.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_less_or_equal AS
+create or replace noneditionable type body ut_be_less_or_equal AS
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_less_or_equal.tps b/source/expectations/matchers/ut_be_less_or_equal.tps
index 225c99b72..f0e297ad1 100644
--- a/source/expectations/matchers/ut_be_less_or_equal.tps
+++ b/source/expectations/matchers/ut_be_less_or_equal.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_less_or_equal under ut_comparison_matcher(
+create or replace noneditionable type ut_be_less_or_equal under ut_comparison_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_less_than.tpb b/source/expectations/matchers/ut_be_less_than.tpb
index e1c5c84b1..c0b059231 100644
--- a/source/expectations/matchers/ut_be_less_than.tpb
+++ b/source/expectations/matchers/ut_be_less_than.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_less_than as
+create or replace noneditionable type body ut_be_less_than as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_less_than.tps b/source/expectations/matchers/ut_be_less_than.tps
index 8482f0327..594df9b71 100644
--- a/source/expectations/matchers/ut_be_less_than.tps
+++ b/source/expectations/matchers/ut_be_less_than.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_less_than under ut_comparison_matcher(
+create or replace noneditionable type ut_be_less_than under ut_comparison_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_like.tpb b/source/expectations/matchers/ut_be_like.tpb
index dbcd1c798..b94c3fa91 100644
--- a/source/expectations/matchers/ut_be_like.tpb
+++ b/source/expectations/matchers/ut_be_like.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_like as
+create or replace noneditionable type body ut_be_like as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_like.tps b/source/expectations/matchers/ut_be_like.tps
index 0c1609a98..ca9ad45de 100644
--- a/source/expectations/matchers/ut_be_like.tps
+++ b/source/expectations/matchers/ut_be_like.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_like under ut_matcher(
+create or replace noneditionable type ut_be_like under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_not_null.tpb b/source/expectations/matchers/ut_be_not_null.tpb
index 94ee0ce0a..d291d2e35 100644
--- a/source/expectations/matchers/ut_be_not_null.tpb
+++ b/source/expectations/matchers/ut_be_not_null.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_not_null as
+create or replace noneditionable type body ut_be_not_null as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_not_null.tps b/source/expectations/matchers/ut_be_not_null.tps
index 34cc2bfd0..61f6c871d 100644
--- a/source/expectations/matchers/ut_be_not_null.tps
+++ b/source/expectations/matchers/ut_be_not_null.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_not_null under ut_matcher(
+create or replace noneditionable type ut_be_not_null under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_null.tpb b/source/expectations/matchers/ut_be_null.tpb
index 3194f092f..89fbaf563 100644
--- a/source/expectations/matchers/ut_be_null.tpb
+++ b/source/expectations/matchers/ut_be_null.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_null as
+create or replace noneditionable type body ut_be_null as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_null.tps b/source/expectations/matchers/ut_be_null.tps
index 486e11f28..063c7db29 100644
--- a/source/expectations/matchers/ut_be_null.tps
+++ b/source/expectations/matchers/ut_be_null.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_null under ut_matcher(
+create or replace noneditionable type ut_be_null under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_true.tpb b/source/expectations/matchers/ut_be_true.tpb
index 565ab590b..59368f342 100644
--- a/source/expectations/matchers/ut_be_true.tpb
+++ b/source/expectations/matchers/ut_be_true.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_be_true as
+create or replace noneditionable type body ut_be_true as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_true.tps b/source/expectations/matchers/ut_be_true.tps
index b994913f5..19d397371 100644
--- a/source/expectations/matchers/ut_be_true.tps
+++ b/source/expectations/matchers/ut_be_true.tps
@@ -1,7 +1,7 @@
-create or replace type ut_be_true under ut_matcher(
+create or replace noneditionable type ut_be_true under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_be_within.tpb b/source/expectations/matchers/ut_be_within.tpb
new file mode 100644
index 000000000..f8f880ace
--- /dev/null
+++ b/source/expectations/matchers/ut_be_within.tpb
@@ -0,0 +1,137 @@
+create or replace noneditionable type body ut_be_within as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected number) return self as result is
+ begin
+ self.init(ut_data_value_number(a_distance_from_expected), $$plsql_unit);
+ return;
+ end;
+
+ constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected dsinterval_unconstrained) return self as result is
+ begin
+ self.init(ut_data_value_dsinterval(a_distance_from_expected), $$plsql_unit);
+ return;
+ end;
+
+ constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected yminterval_unconstrained) return self as result is
+ begin
+ self.init(ut_data_value_yminterval(a_distance_from_expected), $$plsql_unit);
+ return;
+ end;
+
+ member procedure of_(self in ut_be_within, a_expected date) is
+ l_result ut_be_within := self;
+ begin
+ l_result.expected := ut_data_value_date(a_expected);
+ if l_result.is_negated_flag = 1 then
+ l_result.expectation.not_to(l_result);
+ else
+ l_result.expectation.to_(l_result);
+ end if;
+ end;
+
+ member function of_(self in ut_be_within, a_expected date) return ut_be_within is
+ l_result ut_be_within := self;
+ begin
+ l_result.expected := ut_data_value_date(a_expected);
+ return l_result;
+ end;
+
+ member procedure of_(self in ut_be_within, a_expected timestamp_unconstrained) is
+ l_result ut_be_within := self;
+ begin
+ l_result.expected := ut_data_value_timestamp(a_expected);
+ if l_result.is_negated_flag = 1 then
+ l_result.expectation.not_to(l_result);
+ else
+ l_result.expectation.to_(l_result);
+ end if;
+ end;
+
+ member function of_(self in ut_be_within, a_expected timestamp_unconstrained) return ut_be_within is
+ l_result ut_be_within := self;
+ begin
+ l_result.expected := ut_data_value_timestamp(a_expected);
+ return l_result;
+ end;
+
+ member procedure of_(self in ut_be_within, a_expected timestamp_tz_unconstrained) is
+ l_result ut_be_within := self;
+ begin
+ l_result.expected := ut_data_value_timestamp_tz(a_expected);
+ if l_result.is_negated_flag = 1 then
+ l_result.expectation.not_to(l_result);
+ else
+ l_result.expectation.to_(l_result);
+ end if;
+ end;
+
+ member function of_(self in ut_be_within, a_expected timestamp_tz_unconstrained) return ut_be_within is
+ l_result ut_be_within := self;
+ begin
+ l_result.expected := ut_data_value_timestamp_tz(a_expected);
+ return l_result;
+ end;
+
+ member procedure of_(self in ut_be_within, a_expected timestamp_ltz_unconstrained) is
+ l_result ut_be_within := self;
+ begin
+ l_result.expected := ut_data_value_timestamp_ltz(a_expected);
+ if l_result.is_negated_flag = 1 then
+ l_result.expectation.not_to(l_result);
+ else
+ l_result.expectation.to_(l_result);
+ end if;
+ end;
+
+ member function of_(self in ut_be_within, a_expected timestamp_ltz_unconstrained) return ut_be_within is
+ l_result ut_be_within := self;
+ begin
+ l_result.expected := ut_data_value_timestamp_ltz(a_expected);
+ return l_result;
+ end;
+
+ overriding member function run_matcher(self in out nocopy ut_be_within, a_actual ut_data_value) return boolean is
+ l_result boolean;
+ begin
+ if self.expected.data_type = a_actual.data_type
+ and (
+ self.expected is of (ut_data_value_date, ut_data_value_timestamp, ut_data_value_timestamp_tz, ut_data_value_timestamp_ltz)
+ and self.distance_from_expected is of (ut_data_value_yminterval, ut_data_value_dsinterval)
+ or self.expected is of (ut_data_value_number) and self.distance_from_expected is of (ut_data_value_number)
+ )
+ then
+ l_result := ut_be_within_helper.values_within_abs_distance( a_actual, self.expected, self.distance_from_expected) ;
+ else
+ l_result := (self as ut_matcher).run_matcher(a_actual);
+ end if;
+ return l_result;
+ end;
+
+ overriding member function failure_message(a_actual ut_data_value) return varchar2 is
+ begin
+ return (self as ut_matcher).failure_message(a_actual) || ' '||self.distance_from_expected.to_string ||' of '|| expected.to_string_report();
+ end;
+
+ overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2 is
+ begin
+ return (self as ut_matcher).failure_message_when_negated(a_actual) || ' '||self.distance_from_expected.to_string ||' of '|| expected.to_string_report();
+ end;
+
+end;
+/
diff --git a/source/expectations/matchers/ut_be_within.tps b/source/expectations/matchers/ut_be_within.tps
new file mode 100644
index 000000000..276c529df
--- /dev/null
+++ b/source/expectations/matchers/ut_be_within.tps
@@ -0,0 +1,36 @@
+create or replace noneditionable type ut_be_within force under ut_be_within_pct(
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+ constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected number) return self as result,
+ constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected dsinterval_unconstrained) return self as result,
+ constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected yminterval_unconstrained) return self as result,
+ member procedure of_(self in ut_be_within, a_expected date),
+ member function of_(self in ut_be_within, a_expected date) return ut_be_within,
+ member procedure of_(self in ut_be_within, a_expected timestamp_unconstrained),
+ member function of_(self in ut_be_within, a_expected timestamp_unconstrained) return ut_be_within,
+ member procedure of_(self in ut_be_within, a_expected timestamp_tz_unconstrained ),
+ member function of_(self in ut_be_within, a_expected timestamp_tz_unconstrained) return ut_be_within,
+ member procedure of_(self in ut_be_within, a_expected timestamp_ltz_unconstrained),
+ member function of_(self in ut_be_within, a_expected timestamp_ltz_unconstrained) return ut_be_within,
+ overriding member function run_matcher(self in out nocopy ut_be_within, a_actual ut_data_value) return boolean,
+ overriding member function failure_message(a_actual ut_data_value) return varchar2,
+ overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2
+)
+not final
+/
diff --git a/source/expectations/matchers/ut_be_within_helper.pkb b/source/expectations/matchers/ut_be_within_helper.pkb
new file mode 100644
index 000000000..635c1b111
--- /dev/null
+++ b/source/expectations/matchers/ut_be_within_helper.pkb
@@ -0,0 +1,55 @@
+create or replace noneditionable package body ut_be_within_helper as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ function values_within_abs_distance(
+ a_actual ut_data_value, a_expected ut_data_value, a_distance ut_data_value
+ ) return boolean is
+ l_result integer;
+ l_YM_conversion varchar2(50) := case when a_distance is of (ut_data_value_yminterval) then ' year to month ' end;
+ l_formula varchar2(4000);
+ l_code varchar2(4000);
+ begin
+ l_formula :=
+ case
+ when a_actual is of (ut_data_value_date)
+ then '( cast(greatest(l_actual, l_expected) as timestamp) - cast(least(l_actual, l_expected) as timestamp) ) '||l_YM_conversion||' <= l_distance'
+ else '( greatest(l_actual, l_expected) - least(l_actual, l_expected) ) '||l_YM_conversion||' <= l_distance'
+ end;
+ l_code :=
+ q'[
+ declare
+ l_actual ]'||dbms_assert.simple_sql_name(a_actual.data_type_plsql)|| q'[ := treat(:a_actual as ]'||dbms_assert.simple_sql_name(a_actual.self_type)||q'[).data_value;
+ l_expected ]'||dbms_assert.simple_sql_name(a_expected.data_type_plsql)||q'[ := treat(:a_expected as ]'||dbms_assert.simple_sql_name(a_expected.self_type)||q'[).data_value;
+ l_distance ]'||dbms_assert.simple_sql_name(a_distance.data_type_plsql)||q'[ := treat(:a_distance as ]'||dbms_assert.simple_sql_name(a_distance.self_type)||q'[).data_value;
+ begin
+ :result :=
+ case
+ when
+ ]'||l_formula||q'[
+ then 1
+ else 0
+ end;
+ end;
+ ]';
+ execute immediate l_code
+ using a_actual, a_expected, a_distance, out l_result;
+ return l_result > 0;
+ end;
+
+end;
+/
diff --git a/source/expectations/matchers/ut_be_within_helper.pks b/source/expectations/matchers/ut_be_within_helper.pks
new file mode 100644
index 000000000..5a59eea9e
--- /dev/null
+++ b/source/expectations/matchers/ut_be_within_helper.pks
@@ -0,0 +1,24 @@
+create or replace noneditionable package ut_be_within_helper authid definer as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ function values_within_abs_distance(
+ a_actual ut_data_value, a_expected ut_data_value, a_distance ut_data_value
+ ) return boolean;
+
+end;
+/
diff --git a/source/expectations/matchers/ut_be_within_pct.tpb b/source/expectations/matchers/ut_be_within_pct.tpb
new file mode 100644
index 000000000..82ce8e84e
--- /dev/null
+++ b/source/expectations/matchers/ut_be_within_pct.tpb
@@ -0,0 +1,84 @@
+create or replace noneditionable type body ut_be_within_pct as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ constructor function ut_be_within_pct(self in out nocopy ut_be_within_pct, a_pct_of_expected number) return self as result is
+ begin
+ self.init(ut_data_value_number(a_pct_of_expected), $$plsql_unit);
+ return;
+ end;
+
+ member procedure init(self in out nocopy ut_be_within_pct, a_distance_from_expected ut_data_value, self_type varchar2) is
+ begin
+ self.distance_from_expected := a_distance_from_expected;
+ self.self_type := self_type;
+ end;
+
+ member procedure of_(self in ut_be_within_pct, a_expected number) is
+ l_result ut_be_within_pct := self;
+ begin
+ l_result.expected := ut_data_value_number(a_expected);
+ if l_result.is_negated_flag = 1 then
+ l_result.expectation.not_to(l_result);
+ else
+ l_result.expectation.to_(l_result);
+ end if;
+ end;
+
+ member function of_(self in ut_be_within_pct, a_expected number) return ut_be_within_pct is
+ l_result ut_be_within_pct := self;
+ begin
+ l_result.expected := ut_data_value_number(a_expected);
+ return l_result;
+ end;
+
+ overriding member function run_matcher(self in out nocopy ut_be_within_pct, a_actual ut_data_value) return boolean is
+ l_result boolean;
+ begin
+ if self.expected.data_type = a_actual.data_type then
+ if self.expected is of (ut_data_value_number) then
+ l_result :=
+ abs(treat(self.distance_from_expected as ut_data_value_number).data_value) * treat(self.expected as ut_data_value_number).data_value
+ >= abs( ( treat(self.expected as ut_data_value_number).data_value - treat(a_actual as ut_data_value_number).data_value ) * 100 );
+ end if;
+ else
+ l_result := (self as ut_matcher).run_matcher(a_actual);
+ end if;
+ return l_result;
+ end;
+
+ overriding member function failure_message(a_actual ut_data_value) return varchar2 is
+ begin
+ return rtrim( (self as ut_matcher).failure_message(a_actual), 'pct' ) || self.distance_from_expected.to_string ||' % of '|| expected.to_string_report();
+ end;
+
+ overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2 is
+ begin
+ return rtrim( (self as ut_matcher).failure_message_when_negated(a_actual), 'pct' ) || self.distance_from_expected.to_string ||' % of '|| expected.to_string_report();
+ end;
+
+ overriding member function error_message(a_actual ut_data_value) return varchar2 is
+ l_result varchar2(32767);
+ begin
+ if ut_utils.int_to_boolean(self.is_errored) then
+ l_result := 'Matcher '''||self.name()||''' cannot be used to compare Actual ('||a_actual.data_type||') with Expected ('||expected.data_type||') using distance ('||self.distance_from_expected.data_type||').';
+ end if;
+ return l_result;
+ end;
+
+end;
+/
diff --git a/source/expectations/matchers/ut_be_within_pct.tps b/source/expectations/matchers/ut_be_within_pct.tps
new file mode 100644
index 000000000..810eaa5b3
--- /dev/null
+++ b/source/expectations/matchers/ut_be_within_pct.tps
@@ -0,0 +1,34 @@
+create or replace noneditionable type ut_be_within_pct force under ut_comparison_matcher(
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ /**
+ * Holds information about mather options
+ */
+ distance_from_expected ut_data_value,
+
+ constructor function ut_be_within_pct(self in out nocopy ut_be_within_pct, a_pct_of_expected number) return self as result,
+ member procedure init(self in out nocopy ut_be_within_pct, a_distance_from_expected ut_data_value, self_type varchar2),
+ member procedure of_(self in ut_be_within_pct, a_expected number),
+ member function of_(self in ut_be_within_pct, a_expected number) return ut_be_within_pct,
+ overriding member function run_matcher(self in out nocopy ut_be_within_pct, a_actual ut_data_value) return boolean,
+ overriding member function failure_message(a_actual ut_data_value) return varchar2,
+ overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2,
+ overriding member function error_message(a_actual ut_data_value) return varchar2
+)
+not final
+/
diff --git a/source/expectations/matchers/ut_comparison_matcher.tpb b/source/expectations/matchers/ut_comparison_matcher.tpb
index 50f8d6050..d6c47855d 100644
--- a/source/expectations/matchers/ut_comparison_matcher.tpb
+++ b/source/expectations/matchers/ut_comparison_matcher.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_comparison_matcher as
+create or replace noneditionable type body ut_comparison_matcher as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_comparison_matcher.tps b/source/expectations/matchers/ut_comparison_matcher.tps
index 76520a296..b1ed08a84 100644
--- a/source/expectations/matchers/ut_comparison_matcher.tps
+++ b/source/expectations/matchers/ut_comparison_matcher.tps
@@ -1,7 +1,7 @@
-create or replace type ut_comparison_matcher under ut_matcher(
+create or replace noneditionable type ut_comparison_matcher under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_contain.tpb b/source/expectations/matchers/ut_contain.tpb
index c0c4f481a..bcc3806b7 100644
--- a/source/expectations/matchers/ut_contain.tpb
+++ b/source/expectations/matchers/ut_contain.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_contain as
+create or replace noneditionable type body ut_contain as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@ create or replace type body ut_contain as
overriding member function run_matcher_negated(self in out nocopy ut_contain, a_actual ut_data_value) return boolean is
begin
+ self.negated();
return run_matcher(a_actual);
end;
diff --git a/source/expectations/matchers/ut_contain.tps b/source/expectations/matchers/ut_contain.tps
index 518352573..bd67113c9 100644
--- a/source/expectations/matchers/ut_contain.tps
+++ b/source/expectations/matchers/ut_contain.tps
@@ -1,7 +1,7 @@
-create or replace type ut_contain under ut_equal(
+create or replace noneditionable type ut_contain under ut_equal(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_equal.tpb b/source/expectations/matchers/ut_equal.tpb
index afa2148f3..f999995d1 100644
--- a/source/expectations/matchers/ut_equal.tpb
+++ b/source/expectations/matchers/ut_equal.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_equal as
+create or replace noneditionable type body ut_equal as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -158,6 +158,12 @@ create or replace type body ut_equal as
return;
end;
+ constructor function ut_equal(self in out nocopy ut_equal, a_expected json, a_nulls_are_equal boolean := null) return self as result is
+ begin
+ init(ut_data_value_json(a_expected), a_nulls_are_equal);
+ return;
+ end;
+
member function include(a_items varchar2) return ut_equal is
l_result ut_equal := self;
begin
@@ -172,6 +178,18 @@ create or replace type body ut_equal as
return l_result;
end;
+ member procedure include(self in ut_equal, a_items varchar2) is
+ begin
+ include( ut_varchar2_list( a_items ) );
+ end;
+
+ member procedure include(self in ut_equal, a_items ut_varchar2_list) is
+ l_result ut_equal := self;
+ begin
+ l_result.options.include.add_items(a_items);
+ l_result.expectation.to_(l_result );
+ end;
+
member function exclude(a_items varchar2) return ut_equal is
l_result ut_equal := self;
begin
@@ -186,6 +204,18 @@ create or replace type body ut_equal as
return l_result;
end;
+ member procedure exclude(self in ut_equal, a_items varchar2) is
+ begin
+ exclude( ut_varchar2_list( a_items ) );
+ end;
+
+ member procedure exclude(self in ut_equal, a_items ut_varchar2_list) is
+ l_result ut_equal := self;
+ begin
+ l_result.options.exclude.add_items(a_items);
+ l_result.expectation.to_(l_result );
+ end;
+
member function unordered return ut_equal is
l_result ut_equal := self;
begin
@@ -193,6 +223,13 @@ create or replace type body ut_equal as
return l_result;
end;
+ member procedure unordered(self in ut_equal) is
+ l_result ut_equal := self;
+ begin
+ l_result.options.unordered();
+ l_result.expectation.to_(l_result );
+ end;
+
member function join_by(a_columns varchar2) return ut_equal is
l_result ut_equal := self;
begin
@@ -209,18 +246,43 @@ create or replace type body ut_equal as
return l_result;
end;
- member function uc return ut_equal is
+ member procedure join_by(self in ut_equal, a_columns varchar2) is
begin
- return unordered_columns;
+ join_by( ut_varchar2_list( a_columns ) );
end;
+ member procedure join_by(self in ut_equal, a_columns ut_varchar2_list) is
+ l_result ut_equal := self;
+ begin
+ l_result.options.unordered();
+ l_result.options.join_by.add_items(a_columns);
+ l_result.expectation.to_(l_result );
+ end;
+
member function unordered_columns return ut_equal is
l_result ut_equal := self;
begin
l_result.options.unordered_columns();
return l_result;
end;
+
+ member procedure unordered_columns(self in ut_equal) is
+ l_result ut_equal := self;
+ begin
+ l_result.options.unordered_columns();
+ l_result.expectation.to_(l_result );
+ end;
+
+ member function uc return ut_equal is
+ begin
+ return unordered_columns;
+ end;
+ member procedure uc(self in ut_equal) is
+ begin
+ unordered_columns;
+ end;
+
overriding member function run_matcher(self in out nocopy ut_equal, a_actual ut_data_value) return boolean is
l_result boolean;
begin
diff --git a/source/expectations/matchers/ut_equal.tps b/source/expectations/matchers/ut_equal.tps
index 1f42324d7..eeb715a58 100644
--- a/source/expectations/matchers/ut_equal.tps
+++ b/source/expectations/matchers/ut_equal.tps
@@ -1,7 +1,7 @@
-create or replace type ut_equal force under ut_comparison_matcher(
+create or replace noneditionable type ut_equal force under ut_comparison_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -42,18 +42,28 @@ create or replace type ut_equal force under ut_comparison_matcher(
constructor function ut_equal(self in out nocopy ut_equal, a_expected yminterval_unconstrained, a_nulls_are_equal boolean := null) return self as result,
constructor function ut_equal(self in out nocopy ut_equal, a_expected dsinterval_unconstrained, a_nulls_are_equal boolean := null) return self as result,
constructor function ut_equal(self in out nocopy ut_equal, a_expected json_element_t, a_nulls_are_equal boolean := null) return self as result,
+ constructor function ut_equal(self in out nocopy ut_equal, a_expected json, a_nulls_are_equal boolean := null) return self as result,
member function include(a_items varchar2) return ut_equal,
- member function include(a_items ut_varchar2_list) return ut_equal,
+ member function include(a_items ut_varchar2_list) return ut_equal,
+ member procedure include(self in ut_equal, a_items varchar2),
+ member procedure include(self in ut_equal, a_items ut_varchar2_list),
member function exclude(a_items varchar2) return ut_equal,
member function exclude(a_items ut_varchar2_list) return ut_equal,
+ member procedure exclude(self in ut_equal, a_items varchar2),
+ member procedure exclude(self in ut_equal, a_items ut_varchar2_list),
member function unordered return ut_equal,
+ member procedure unordered(self in ut_equal),
member function join_by(a_columns varchar2) return ut_equal,
member function join_by(a_columns ut_varchar2_list) return ut_equal,
+ member procedure join_by(self in ut_equal, a_columns varchar2),
+ member procedure join_by(self in ut_equal, a_columns ut_varchar2_list),
overriding member function run_matcher(self in out nocopy ut_equal, a_actual ut_data_value) return boolean,
overriding member function failure_message(a_actual ut_data_value) return varchar2,
overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2,
member function unordered_columns return ut_equal,
- member function uc return ut_equal
+ member procedure unordered_columns(self in ut_equal),
+ member function uc return ut_equal,
+ member procedure uc(self in ut_equal)
)
not final
/
diff --git a/source/expectations/matchers/ut_have_count.tpb b/source/expectations/matchers/ut_have_count.tpb
index b0f4e06d4..f6d3d2682 100644
--- a/source/expectations/matchers/ut_have_count.tpb
+++ b/source/expectations/matchers/ut_have_count.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_have_count as
+create or replace noneditionable type body ut_have_count as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_have_count.tps b/source/expectations/matchers/ut_have_count.tps
index fb0fc76fc..85cdf4393 100644
--- a/source/expectations/matchers/ut_have_count.tps
+++ b/source/expectations/matchers/ut_have_count.tps
@@ -1,7 +1,7 @@
-create or replace type ut_have_count under ut_matcher(
+create or replace noneditionable type ut_have_count under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_match.tpb b/source/expectations/matchers/ut_match.tpb
index 57fabbaec..1836298e7 100644
--- a/source/expectations/matchers/ut_match.tpb
+++ b/source/expectations/matchers/ut_match.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_match as
+create or replace noneditionable type body ut_match as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_match.tps b/source/expectations/matchers/ut_match.tps
index 8d6b0f73a..3f840545f 100644
--- a/source/expectations/matchers/ut_match.tps
+++ b/source/expectations/matchers/ut_match.tps
@@ -1,7 +1,7 @@
-create or replace type ut_match under ut_matcher(
+create or replace noneditionable type ut_match under ut_matcher(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_matcher.tpb b/source/expectations/matchers/ut_matcher.tpb
index 11ff1b6b6..828e1ac51 100644
--- a/source/expectations/matchers/ut_matcher.tpb
+++ b/source/expectations/matchers/ut_matcher.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_matcher as
+create or replace noneditionable type body ut_matcher as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -20,11 +20,6 @@ create or replace type body ut_matcher as
begin
ut_utils.debug_log('Failure - ut_matcher.run_matcher'||'(a_actual '||a_actual.data_type||')');
self.is_errored := ut_utils.boolean_to_int(true);
--- self.error_message := 'The matcher '''||name()||''' cannot be used';
--- if self.expected is not null then
--- self.error_message := self.error_message ||' for comparison of data type ('||self.expected.data_type||')';
--- end if;
--- self.error_message := self.error_message ||' with data type ('||a_actual.data_type||').';
return null;
end;
diff --git a/source/expectations/matchers/ut_matcher.tps b/source/expectations/matchers/ut_matcher.tps
index 34e1742df..ee7f1b5dd 100644
--- a/source/expectations/matchers/ut_matcher.tps
+++ b/source/expectations/matchers/ut_matcher.tps
@@ -1,7 +1,7 @@
-create or replace type ut_matcher authid current_user as object(
+create or replace noneditionable type ut_matcher under ut_matcher_base(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -15,10 +15,9 @@ create or replace type ut_matcher authid current_user as object(
See the License for the specific language governing permissions and
limitations under the License.
*/
- self_type varchar2(250),
is_errored integer,
is_negated_flag number(1,0),
-
+ expectation ut_expectation_base,
/*
function: run_matcher
diff --git a/source/reporters/ut_coveralls_reporter.tps b/source/expectations/matchers/ut_matcher_base.tps
similarity index 51%
rename from source/reporters/ut_coveralls_reporter.tps
rename to source/expectations/matchers/ut_matcher_base.tps
index 53e86338e..ba79a6aed 100644
--- a/source/reporters/ut_coveralls_reporter.tps
+++ b/source/expectations/matchers/ut_matcher_base.tps
@@ -1,7 +1,7 @@
-create or replace type ut_coveralls_reporter under ut_coverage_reporter_base(
+create or replace noneditionable type ut_matcher_base force authid current_user as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -16,16 +16,7 @@ create or replace type ut_coveralls_reporter under ut_coverage_reporter_base(
limitations under the License.
*/
- /**
- * Builds a coverage XML report that follows rules described in
- * https://docs.sonarqube.org/display/SONAR/Generic+Test+Data
- */
- constructor function ut_coveralls_reporter(
- self in out nocopy ut_coveralls_reporter
- ) return self as result,
-
- overriding member procedure after_calling_run(self in out nocopy ut_coveralls_reporter, a_run in ut_run),
-
- overriding member function get_description return varchar2
+ self_type varchar2(250)
)
+not final not instantiable
/
diff --git a/source/expectations/matchers/ut_matcher_options.tpb b/source/expectations/matchers/ut_matcher_options.tpb
index 6fec3dee6..57aca2617 100644
--- a/source/expectations/matchers/ut_matcher_options.tpb
+++ b/source/expectations/matchers/ut_matcher_options.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_matcher_options as
+create or replace noneditionable type body ut_matcher_options as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_matcher_options.tps b/source/expectations/matchers/ut_matcher_options.tps
index dbdf985f0..db076fd99 100644
--- a/source/expectations/matchers/ut_matcher_options.tps
+++ b/source/expectations/matchers/ut_matcher_options.tps
@@ -1,7 +1,7 @@
-create or replace type ut_matcher_options authid current_user as object(
+create or replace noneditionable type ut_matcher_options authid current_user as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_matcher_options_items.tpb b/source/expectations/matchers/ut_matcher_options_items.tpb
index 2c3f51d34..6e4841870 100644
--- a/source/expectations/matchers/ut_matcher_options_items.tpb
+++ b/source/expectations/matchers/ut_matcher_options_items.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_matcher_options_items is
+create or replace noneditionable type body ut_matcher_options_items is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/matchers/ut_matcher_options_items.tps b/source/expectations/matchers/ut_matcher_options_items.tps
index cc74650f3..045a7ece7 100644
--- a/source/expectations/matchers/ut_matcher_options_items.tps
+++ b/source/expectations/matchers/ut_matcher_options_items.tps
@@ -1,7 +1,7 @@
-create or replace type ut_matcher_options_items authid current_user as object(
+create or replace noneditionable type ut_matcher_options_items authid current_user as object(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/ut_expectation.tpb b/source/expectations/ut_expectation.tpb
index d8ed1f79a..5f7c9cce0 100644
--- a/source/expectations/ut_expectation.tpb
+++ b/source/expectations/ut_expectation.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_expectation as
+create or replace noneditionable type body ut_expectation as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -15,31 +15,6 @@ create or replace type body ut_expectation as
See the License for the specific language governing permissions and
limitations under the License.
*/
- member procedure to_(self in ut_expectation, a_matcher ut_matcher) is
- l_expectation_result boolean;
- l_matcher ut_matcher := a_matcher;
- l_message varchar2(32767);
- begin
- if a_matcher.is_negated() then
- self.not_to( a_matcher );
- else
- l_expectation_result := l_matcher.run_matcher( self.actual_data );
- l_expectation_result := coalesce(l_expectation_result,false);
- l_message := coalesce( l_matcher.error_message( self.actual_data ), l_matcher.failure_message( self.actual_data ) );
- ut_expectation_processor.add_expectation_result( ut_expectation_result( ut_utils.to_test_result( l_expectation_result ), self.description, l_message ) );
- end if;
- end;
-
- member procedure not_to(self in ut_expectation, a_matcher ut_matcher) is
- l_expectation_result boolean;
- l_matcher ut_matcher := a_matcher;
- l_message varchar2(32767);
- begin
- l_expectation_result := coalesce( l_matcher.run_matcher_negated( self.actual_data ), false );
-
- l_message := coalesce( l_matcher.error_message( self.actual_data ), l_matcher.failure_message_when_negated( self.actual_data ) );
- ut_expectation_processor.add_expectation_result( ut_expectation_result( ut_utils.to_test_result( l_expectation_result ), self.description, l_message ) );
- end;
member procedure to_be_null(self in ut_expectation) is
begin
@@ -201,6 +176,11 @@ create or replace type body ut_expectation as
self.to_( ut_equal(a_expected, a_nulls_are_equal) );
end;
+ member procedure to_equal(self in ut_expectation, a_expected json, a_nulls_are_equal boolean := null) is
+ begin
+ self.to_( ut_equal(a_expected, a_nulls_are_equal) );
+ end;
+
member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_nulls_are_equal boolean := null) is
begin
self.not_to( ut_equal(a_expected, a_nulls_are_equal) );
@@ -706,7 +686,7 @@ create or replace type body ut_expectation as
member procedure not_to_contain(self in ut_expectation, a_expected sys_refcursor) is
begin
- self.not_to( ut_contain(a_expected).negated() );
+ self.not_to( ut_contain(a_expected));
end;
member procedure to_contain(self in ut_expectation, a_expected anydata) is
@@ -716,8 +696,73 @@ create or replace type body ut_expectation as
member procedure not_to_contain(self in ut_expectation, a_expected anydata) is
begin
- self.not_to( ut_contain(a_expected).negated() );
+ self.not_to( ut_contain(a_expected));
end;
+ member function to_be_within(a_dist number) return ut_be_within is
+ l_result ut_be_within;
+ begin
+ l_result := ut_be_within(a_dist);
+ l_result.expectation := self;
+ return l_result;
+ end;
+
+ member function to_be_within(a_dist dsinterval_unconstrained) return ut_be_within is
+ l_result ut_be_within;
+ begin
+ l_result := ut_be_within(a_dist);
+ l_result.expectation := self;
+ return l_result;
+ end;
+
+ member function to_be_within(a_dist yminterval_unconstrained) return ut_be_within is
+ l_result ut_be_within;
+ begin
+ l_result := ut_be_within(a_dist);
+ l_result.expectation := self;
+ return l_result;
+ end;
+
+ member function to_be_within_pct(a_dist number) return ut_be_within_pct is
+ l_result ut_be_within_pct;
+ begin
+ l_result := ut_be_within_pct(a_dist);
+ l_result.expectation := self;
+ return l_result;
+ end;
+
+ member function not_to_be_within(a_dist number) return ut_be_within is
+ l_result ut_be_within;
+ begin
+ l_result := treat( ut_be_within(a_dist).negated() as ut_be_within);
+ l_result.expectation := self;
+ return l_result;
+ end;
+
+ member function not_to_be_within(a_dist dsinterval_unconstrained) return ut_be_within is
+ l_result ut_be_within;
+ begin
+ l_result := treat( ut_be_within(a_dist).negated() as ut_be_within);
+ l_result.expectation := self;
+ return l_result;
+ end;
+
+ member function not_to_be_within(a_dist yminterval_unconstrained) return ut_be_within is
+ l_result ut_be_within;
+ begin
+ l_result := treat( ut_be_within(a_dist).negated() as ut_be_within);
+ l_result.expectation := self;
+ return l_result;
+ end;
+
+ member function not_to_be_within_pct(a_dist number) return ut_be_within_pct is
+ l_result ut_be_within_pct;
+ begin
+ l_result := treat( ut_be_within_pct(a_dist).negated() as ut_be_within_pct);
+ l_result.expectation := self;
+ return l_result;
+ end;
+
end;
/
+
diff --git a/source/expectations/ut_expectation.tps b/source/expectations/ut_expectation.tps
index 37fefd085..a423c39a4 100644
--- a/source/expectations/ut_expectation.tps
+++ b/source/expectations/ut_expectation.tps
@@ -1,7 +1,7 @@
-create or replace type ut_expectation authid current_user as object(
+create or replace noneditionable type ut_expectation force under ut_expectation_base(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -15,13 +15,7 @@ create or replace type ut_expectation authid current_user as object(
See the License for the specific language governing permissions and
limitations under the License.
*/
- actual_data ut_data_value,
- description varchar2(4000 char),
-
- --base matcher executors
- member procedure to_(self in ut_expectation, a_matcher ut_matcher),
- member procedure not_to(self in ut_expectation, a_matcher ut_matcher),
-
+
--shortcuts
member procedure to_be_null(self in ut_expectation),
member procedure to_be_not_null(self in ut_expectation),
@@ -55,6 +49,7 @@ create or replace type ut_expectation authid current_user as object(
member procedure to_equal(self in ut_expectation, a_expected yminterval_unconstrained, a_nulls_are_equal boolean := null),
member procedure to_equal(self in ut_expectation, a_expected dsinterval_unconstrained, a_nulls_are_equal boolean := null),
member procedure to_equal(self in ut_expectation, a_expected json_element_t, a_nulls_are_equal boolean := null),
+ member procedure to_equal(self in ut_expectation, a_expected json, a_nulls_are_equal boolean := null),
member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_nulls_are_equal boolean := null),
member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_exclude varchar2, a_nulls_are_equal boolean := null),
@@ -168,8 +163,16 @@ create or replace type ut_expectation authid current_user as object(
member procedure to_contain(self in ut_expectation, a_expected sys_refcursor),
member procedure not_to_contain(self in ut_expectation, a_expected sys_refcursor),
member procedure to_contain(self in ut_expectation, a_expected anydata),
- member procedure not_to_contain(self in ut_expectation, a_expected anydata)
+ member procedure not_to_contain(self in ut_expectation, a_expected anydata),
+ member function to_be_within(a_dist number) return ut_be_within,
+ member function to_be_within(a_dist dsinterval_unconstrained) return ut_be_within,
+ member function to_be_within(a_dist yminterval_unconstrained) return ut_be_within,
+ member function to_be_within_pct(a_dist number) return ut_be_within_pct,
+ member function not_to_be_within(a_dist number) return ut_be_within,
+ member function not_to_be_within(a_dist dsinterval_unconstrained) return ut_be_within,
+ member function not_to_be_within(a_dist yminterval_unconstrained) return ut_be_within,
+ member function not_to_be_within_pct(a_dist number) return ut_be_within_pct
)
not final
/
diff --git a/source/expectations/ut_expectation_base.tpb b/source/expectations/ut_expectation_base.tpb
new file mode 100644
index 000000000..5ed630c63
--- /dev/null
+++ b/source/expectations/ut_expectation_base.tpb
@@ -0,0 +1,45 @@
+create or replace noneditionable type body ut_expectation_base as
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ member procedure to_(self in ut_expectation_base, a_matcher ut_matcher_base) is
+ l_expectation_result boolean;
+ l_matcher ut_matcher := treat(a_matcher as ut_matcher);
+ l_message varchar2(32767);
+ begin
+ if l_matcher.is_negated() then
+ self.not_to( a_matcher );
+ else
+ l_expectation_result := l_matcher.run_matcher( self.actual_data );
+ l_expectation_result := coalesce(l_expectation_result,false);
+ l_message := coalesce( l_matcher.error_message( self.actual_data ), l_matcher.failure_message( self.actual_data ) );
+ ut_expectation_processor.add_expectation_result( ut_expectation_result( ut_utils.to_test_result( l_expectation_result ), self.description, l_message ) );
+ end if;
+ end;
+
+ member procedure not_to(self in ut_expectation_base, a_matcher ut_matcher_base) is
+ l_expectation_result boolean;
+ l_matcher ut_matcher := treat(a_matcher as ut_matcher);
+ l_message varchar2(32767);
+ begin
+ l_expectation_result := coalesce( l_matcher.run_matcher_negated( self.actual_data ), false );
+
+ l_message := coalesce( l_matcher.error_message( self.actual_data ), l_matcher.failure_message_when_negated( self.actual_data ) );
+ ut_expectation_processor.add_expectation_result( ut_expectation_result( ut_utils.to_test_result( l_expectation_result ), self.description, l_message ) );
+ end;
+end;
+/
diff --git a/source/expectations/ut_expectation_base.tps b/source/expectations/ut_expectation_base.tps
new file mode 100644
index 000000000..86f274495
--- /dev/null
+++ b/source/expectations/ut_expectation_base.tps
@@ -0,0 +1,26 @@
+create or replace noneditionable type ut_expectation_base authid current_user as object(
+ /*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ actual_data ut_data_value,
+ description varchar2(4000 char),
+
+ --base matcher executors
+ member procedure to_(self in ut_expectation_base, a_matcher ut_matcher_base),
+ member procedure not_to(self in ut_expectation_base, a_matcher ut_matcher_base)
+) not final not instantiable
+/
diff --git a/source/expectations/ut_expectation_compound.tpb b/source/expectations/ut_expectation_compound.tpb
index 5e7268bd9..d76010f80 100644
--- a/source/expectations/ut_expectation_compound.tpb
+++ b/source/expectations/ut_expectation_compound.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_expectation_compound as
+create or replace noneditionable type body ut_expectation_compound as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -34,162 +34,68 @@ create or replace type body ut_expectation_compound as
end;
- member function to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
+ member function to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_equal is
+ l_result ut_equal;
begin
- l_result.matcher := ut_equal(a_expected, a_nulls_are_equal);
+ l_result := ut_equal(a_expected, a_nulls_are_equal);
+ l_result.expectation := self;
return l_result;
end;
- member function not_to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
+ member function not_to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_equal is
+ l_result ut_matcher;
begin
- l_result.matcher := ut_equal(a_expected, a_nulls_are_equal).negated();
- return l_result;
- end;
-
- member function to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
- begin
- l_result.matcher := ut_equal(a_expected, a_nulls_are_equal);
- return l_result;
- end;
-
- member function not_to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
- begin
- l_result.matcher := ut_equal(a_expected, a_nulls_are_equal).negated();
- return l_result;
- end;
-
- member function to_contain(a_expected sys_refcursor) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
- begin
- l_result.matcher := ut_contain(a_expected);
- return l_result;
- end;
-
- member function not_to_contain(a_expected sys_refcursor) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
- begin
- l_result.matcher := ut_contain(a_expected).negated();
- return l_result;
- end;
-
- member function to_contain(a_expected anydata) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
- begin
- l_result.matcher := ut_contain(a_expected);
- return l_result;
+ l_result := ut_equal(a_expected, a_nulls_are_equal).negated();
+ l_result.expectation := self;
+ return treat(l_result as ut_equal);
end;
- member function not_to_contain(a_expected anydata) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
+ member function to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_equal is
+ l_result ut_equal;
begin
- l_result.matcher := ut_contain(a_expected).negated();
+ l_result := ut_equal(a_expected, a_nulls_are_equal);
+ l_result.expectation := self;
return l_result;
end;
- member function include(a_items varchar2) return ut_expectation_compound is
+ member function not_to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_equal is
+ l_result ut_matcher;
begin
- return include( ut_varchar2_list( a_items ) );
+ l_result := ut_equal(a_expected, a_nulls_are_equal).negated();
+ l_result.expectation := self;
+ return treat(l_result as ut_equal);
end;
- member function include(a_items ut_varchar2_list) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
+ member function to_contain(a_expected sys_refcursor) return ut_contain is
+ l_result ut_contain;
begin
- l_result.matcher := treat(l_result.matcher as ut_equal).include(a_items);
+ l_result := ut_contain(a_expected);
+ l_result.expectation := self;
return l_result;
end;
- member procedure include(self in ut_expectation_compound, a_items varchar2) is
- begin
- include( ut_varchar2_list( a_items ) );
- end;
-
- member procedure include(self in ut_expectation_compound, a_items ut_varchar2_list) is
+ member function not_to_contain(a_expected sys_refcursor) return ut_contain is
+ l_result ut_matcher;
begin
- self.to_( treat(matcher as ut_equal).include(a_items) );
+ l_result := ut_contain(a_expected).negated();
+ l_result.expectation := self;
+ return treat(l_result as ut_contain);
end;
-
- member function exclude(a_items varchar2) return ut_expectation_compound is
- begin
- return exclude( ut_varchar2_list( a_items ) );
- end;
-
- member function exclude(a_items ut_varchar2_list) return ut_expectation_compound is
- l_result ut_expectation_compound := self;
+
+ member function to_contain(a_expected anydata) return ut_contain is
+ l_result ut_contain;
begin
- l_result.matcher := treat(l_result.matcher as ut_equal).exclude(a_items);
+ l_result := ut_contain(a_expected);
+ l_result.expectation := self;
return l_result;
end;
- member procedure exclude(self in ut_expectation_compound, a_items varchar2) is
- begin
- exclude( ut_varchar2_list( a_items ) );
- end;
-
- member procedure exclude(self in ut_expectation_compound, a_items ut_varchar2_list) is
- begin
- self.to_( treat(matcher as ut_equal).exclude(a_items) );
- end;
-
- member function unordered return ut_expectation_compound is
- l_result ut_expectation_compound := self;
- begin
- l_result.matcher := treat(l_result.matcher as ut_equal).unordered();
- return l_result;
- end;
-
- member procedure unordered(self in ut_expectation_compound) is
- begin
- self.to_( treat(matcher as ut_equal).unordered() );
- end;
-
- member function join_by(a_columns varchar2) return ut_expectation_compound is
- begin
- return join_by( ut_varchar2_list( a_columns ) );
- end;
-
- member function join_by(a_columns ut_varchar2_list) return ut_expectation_compound is
- l_result ut_expectation_compound;
- begin
- l_result := self;
- l_result.matcher := treat(l_result.matcher as ut_equal).join_by(a_columns);
- return l_result;
- end;
-
- member procedure join_by(self in ut_expectation_compound, a_columns varchar2) is
- begin
- join_by( ut_varchar2_list( a_columns ) );
- end;
-
- member procedure join_by(self in ut_expectation_compound, a_columns ut_varchar2_list) is
- begin
- self.to_( treat(matcher as ut_equal).join_by(a_columns) );
- end;
-
- member function unordered_columns return ut_expectation_compound is
- l_result ut_expectation_compound;
- begin
- l_result := self;
- l_result.matcher := treat(l_result.matcher as ut_equal).unordered_columns;
- return l_result;
- end;
-
- member procedure unordered_columns(self in ut_expectation_compound) is
- begin
- self.to_( treat(matcher as ut_equal).unordered_columns );
- end;
-
- member function uc return ut_expectation_compound is
- begin
- return unordered_columns;
- end;
-
- member procedure uc(self in ut_expectation_compound) is
+ member function not_to_contain(a_expected anydata) return ut_contain is
+ l_result ut_matcher;
begin
- unordered_columns;
+ l_result := ut_contain(a_expected).negated();
+ l_result.expectation := self;
+ return treat(l_result as ut_contain);
end;
end;
diff --git a/source/expectations/ut_expectation_compound.tps b/source/expectations/ut_expectation_compound.tps
index fc413e3fd..54de48dd4 100644
--- a/source/expectations/ut_expectation_compound.tps
+++ b/source/expectations/ut_expectation_compound.tps
@@ -1,7 +1,7 @@
-create or replace type ut_expectation_compound force under ut_expectation(
+create or replace noneditionable type ut_expectation_compound under ut_expectation(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -22,33 +22,15 @@ create or replace type ut_expectation_compound force under ut_expectation(
member procedure to_have_count(self in ut_expectation_compound, a_expected integer),
member procedure not_to_have_count(self in ut_expectation_compound, a_expected integer),
- member function to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_expectation_compound,
- member function not_to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_expectation_compound,
- member function to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_expectation_compound,
- member function not_to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_expectation_compound,
- member function to_contain(a_expected sys_refcursor) return ut_expectation_compound,
- member function not_to_contain(a_expected sys_refcursor) return ut_expectation_compound,
- member function to_contain(a_expected anydata) return ut_expectation_compound,
- member function not_to_contain(a_expected anydata) return ut_expectation_compound,
-
- member function include(a_items varchar2) return ut_expectation_compound,
- member function include(a_items ut_varchar2_list) return ut_expectation_compound,
- member procedure include(self in ut_expectation_compound, a_items varchar2),
- member procedure include(self in ut_expectation_compound, a_items ut_varchar2_list),
- member function exclude(a_items varchar2) return ut_expectation_compound,
- member function exclude(a_items ut_varchar2_list) return ut_expectation_compound,
- member procedure exclude(self in ut_expectation_compound, a_items varchar2),
- member procedure exclude(self in ut_expectation_compound, a_items ut_varchar2_list),
- member function unordered return ut_expectation_compound,
- member procedure unordered(self in ut_expectation_compound),
- member function join_by(a_columns varchar2) return ut_expectation_compound,
- member function join_by(a_columns ut_varchar2_list) return ut_expectation_compound,
- member procedure join_by(self in ut_expectation_compound, a_columns varchar2),
- member procedure join_by(self in ut_expectation_compound, a_columns ut_varchar2_list),
-
- member function unordered_columns return ut_expectation_compound,
- member procedure unordered_columns(self in ut_expectation_compound),
- member function uc return ut_expectation_compound,
- member procedure uc(self in ut_expectation_compound)
+ member function to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_equal,
+ member function not_to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_equal,
+ member function to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_equal,
+ member function not_to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_equal,
+ member function to_contain(a_expected sys_refcursor) return ut_contain,
+ member function not_to_contain(a_expected sys_refcursor) return ut_contain,
+ member function to_contain(a_expected anydata) return ut_contain,
+ member function not_to_contain(a_expected anydata) return ut_contain
)
/
+
+
diff --git a/source/expectations/ut_expectation_json.tpb b/source/expectations/ut_expectation_json.tpb
index 49c0c2e1f..68d4085ef 100644
--- a/source/expectations/ut_expectation_json.tpb
+++ b/source/expectations/ut_expectation_json.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_expectation_json as
+create or replace noneditionable type body ut_expectation_json as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/expectations/ut_expectation_json.tps b/source/expectations/ut_expectation_json.tps
index dd3aa5b25..c75154bc8 100644
--- a/source/expectations/ut_expectation_json.tps
+++ b/source/expectations/ut_expectation_json.tps
@@ -1,7 +1,7 @@
-create or replace type ut_expectation_json under ut_expectation(
+create or replace noneditionable type ut_expectation_json under ut_expectation(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -24,4 +24,4 @@ create or replace type ut_expectation_json under ut_expectation(
member procedure to_have_count(self in ut_expectation_json, a_expected integer),
member procedure not_to_have_count(self in ut_expectation_json, a_expected integer)
)
-/
+/
\ No newline at end of file
diff --git a/source/install.sql b/source/install.sql
index 3249e3e49..9af0a4f66 100644
--- a/source/install.sql
+++ b/source/install.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -50,6 +50,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex
@@install_component.sql 'core/types/ut_key_value_pairs.tps'
@@install_component.sql 'core/types/ut_reporter_info.tps'
@@install_component.sql 'core/types/ut_reporters_info.tps'
+@@install_component.sql 'core/types/ut_stack.tps'
+@@install_component.sql 'core/types/ut_stack.tpb'
@@install_component.sql 'core/ut_utils.pks'
@@install_component.sql 'core/ut_metadata.pks'
@@install_component.sql 'core/ut_savepoint_seq.sql'
@@ -89,7 +91,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex
@@install_component.sql 'core/types/ut_run.tps'
@@install_component.sql 'core/types/ut_reporter_base.tps'
@@install_component.sql 'core/types/ut_reporters.tps'
-
+@@install_component.sql 'core/types/ut_path_item.tps'
+@@install_component.sql 'core/types/ut_path_items.tps'
@@install_component.sql 'expectations/json_objects_specs.sql'
@@install_component.sql 'expectations/matchers/ut_matcher_options_items.tps'
@@ -119,6 +122,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex
@@install_component.sql 'core/output_buffers/ut_output_table_buffer.tpb'
@@install_component.sql 'core/output_buffers/ut_output_clob_table_buffer.tps'
@@install_component.sql 'core/output_buffers/ut_output_clob_table_buffer.tpb'
+@@install_component.sql 'core/output_buffers/ut_output_bulk_buffer.tps'
+@@install_component.sql 'core/output_buffers/ut_output_bulk_buffer.tpb'
@@install_component.sql 'core/types/ut_output_reporter_base.tps'
@@ -150,6 +155,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex
@@install_component.sql 'core/ut_suite_cache_seq.sql'
@@install_component.sql 'core/ut_suite_cache.sql'
+@@install_component.sql 'core/ut_suite_tag_filter.pks'
+@@install_component.sql 'core/ut_suite_tag_filter.pkb'
@@install_component.sql 'core/ut_suite_cache_manager.pks'
@@install_component.sql 'core/ut_suite_cache_manager.pkb'
@@install_component.sql 'core/ut_suite_builder.pks'
@@ -165,9 +172,6 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex
prompt Installing PLSQL profiler objects into &&ut3_owner schema
@@core/coverage/proftab.sql
-prompt Installing PLSQL profiler objects into &&ut3_owner schema
-@@core/coverage/proftab.sql
-
prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema
@@core/coverage/dbms_plssqlcode.sql
@@ -210,6 +214,7 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema
@@install_component.sql 'core/types/ut_executable_test.tpb'
@@install_component.sql 'core/types/ut_console_reporter_base.tps'
@@install_component.sql 'core/types/ut_console_reporter_base.tpb'
+@@install_component.sql 'core/types/ut_path_item.tpb'
--expectations and matchers
@@install_component.sql 'expectations/data_values/ut_compound_data_tmp.sql'
@@ -238,8 +243,14 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema
@@install_component.sql 'expectations/data_values/ut_data_value_xmltype.tps'
@@install_component.sql 'expectations/data_values/ut_compound_data_helper.pks'
@@install_component.sql 'expectations/data_values/ut_data_value_json.tps'
+@@install_component.sql 'expectations/matchers/ut_matcher_base.tps'
+@@install_component.sql 'expectations/ut_expectation_base.tps'
@@install_component.sql 'expectations/matchers/ut_matcher.tps'
@@install_component.sql 'expectations/matchers/ut_comparison_matcher.tps'
+@@install_component.sql 'expectations/matchers/ut_be_within_pct.tps'
+@@install_component.sql 'expectations/matchers/ut_be_within.tps'
+@@install_component.sql 'expectations/matchers/ut_be_within_helper.pks'
+@@install_component.sql 'expectations/ut_expectation.tps'
@@install_component.sql 'expectations/matchers/ut_be_false.tps'
@@install_component.sql 'expectations/matchers/ut_be_greater_or_equal.tps'
@@install_component.sql 'expectations/matchers/ut_be_greater_than.tps'
@@ -255,7 +266,6 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema
@@install_component.sql 'expectations/matchers/ut_be_between.tps'
@@install_component.sql 'expectations/matchers/ut_be_empty.tps'
@@install_component.sql 'expectations/matchers/ut_match.tps'
-@@install_component.sql 'expectations/ut_expectation.tps'
@@install_component.sql 'expectations/data_values/ut_json_leaf.tpb'
@@install_component.sql 'expectations/data_values/ut_json_tree_details.tpb'
@@install_component.sql 'expectations/data_values/ut_cursor_column.tpb'
@@ -295,11 +305,15 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema
@@install_component.sql 'expectations/matchers/ut_be_null.tpb'
@@install_component.sql 'expectations/matchers/ut_be_true.tpb'
@@install_component.sql 'expectations/matchers/ut_equal.tpb'
+@@install_component.sql 'expectations/matchers/ut_be_within_pct.tpb'
+@@install_component.sql 'expectations/matchers/ut_be_within.tpb'
+@@install_component.sql 'expectations/matchers/ut_be_within_helper.pkb'
@@install_component.sql 'expectations/matchers/ut_contain.tpb'
@@install_component.sql 'expectations/matchers/ut_have_count.tpb'
@@install_component.sql 'expectations/matchers/ut_be_between.tpb'
@@install_component.sql 'expectations/matchers/ut_be_empty.tpb'
@@install_component.sql 'expectations/matchers/ut_match.tpb'
+@@install_component.sql 'expectations/ut_expectation_base.tpb'
@@install_component.sql 'expectations/ut_expectation.tpb'
@@install_component.sql 'expectations/ut_expectation_compound.tpb'
@@install_component.sql 'expectations/ut_expectation_json.tpb'
@@ -332,6 +346,8 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema
@@install_component.sql 'reporters/ut_xunit_reporter.tpb'
@@install_component.sql 'reporters/ut_sonar_test_reporter.tps'
@@install_component.sql 'reporters/ut_sonar_test_reporter.tpb'
+@@install_component.sql 'reporters/ut_tap_reporter.tps'
+@@install_component.sql 'reporters/ut_tap_reporter.tpb'
@@install_component.sql 'reporters/ut_coverage_html_reporter.tps'
@@install_component.sql 'reporters/ut_coverage_report_html_helper.pks'
@@ -339,8 +355,6 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema
@@install_component.sql 'reporters/ut_coverage_html_reporter.tpb'
@@install_component.sql 'reporters/ut_coverage_sonar_reporter.tps'
@@install_component.sql 'reporters/ut_coverage_sonar_reporter.tpb'
-@@install_component.sql 'reporters/ut_coveralls_reporter.tps'
-@@install_component.sql 'reporters/ut_coveralls_reporter.tpb'
@@install_component.sql 'reporters/ut_coverage_cobertura_reporter.tps'
@@install_component.sql 'reporters/ut_coverage_cobertura_reporter.tpb'
@@install_component.sql 'reporters/ut_realtime_reporter.tps'
@@ -357,6 +371,8 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema
@@install_component.sql 'api/be_not_null.syn'
@@install_component.sql 'api/be_null.syn'
@@install_component.sql 'api/be_true.syn'
+@@install_component.sql 'api/be_within_pct.syn'
+@@install_component.sql 'api/be_within.syn'
@@install_component.sql 'api/equal.syn'
@@install_component.sql 'api/have_count.syn'
@@install_component.sql 'api/match.syn'
diff --git a/source/install_above_12_1.sql b/source/install_above_12_1.sql
index 69e47ce9a..5a1943358 100644
--- a/source/install_above_12_1.sql
+++ b/source/install_above_12_1.sql
@@ -1,3 +1,20 @@
+/*
+ utPLSQL - Version 3
+ Copyright 2016 - 2026 utPLSQL Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"):
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
def FILE_NAME = '&&1'
column SCRIPT_NAME new_value SCRIPT_NAME noprint
diff --git a/source/install_component.sql b/source/install_component.sql
index 857c1771c..1189df496 100644
--- a/source/install_component.sql
+++ b/source/install_component.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/install_ddl_trigger.sql b/source/install_ddl_trigger.sql
index 843ee0c07..b23b4bc63 100644
--- a/source/install_ddl_trigger.sql
+++ b/source/install_ddl_trigger.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/install_headless.sql b/source/install_headless.sql
index e8cf00705..be1e5becf 100644
--- a/source/install_headless.sql
+++ b/source/install_headless.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/install_headless_with_trigger.sql b/source/install_headless_with_trigger.sql
index 16d81f3c6..2c05403c5 100644
--- a/source/install_headless_with_trigger.sql
+++ b/source/install_headless_with_trigger.sql
@@ -1,6 +1,6 @@
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/reporters/ut_ansiconsole_helper.pkb b/source/reporters/ut_ansiconsole_helper.pkb
index b015e50be..e8ee34531 100644
--- a/source/reporters/ut_ansiconsole_helper.pkb
+++ b/source/reporters/ut_ansiconsole_helper.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_ansiconsole_helper as
+create or replace noneditionable package body ut_ansiconsole_helper as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/reporters/ut_ansiconsole_helper.pks b/source/reporters/ut_ansiconsole_helper.pks
index 7c83ca73b..e882de293 100644
--- a/source/reporters/ut_ansiconsole_helper.pks
+++ b/source/reporters/ut_ansiconsole_helper.pks
@@ -1,7 +1,7 @@
-create or replace package ut_ansiconsole_helper as
+create or replace noneditionable package ut_ansiconsole_helper as
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/reporters/ut_coverage_cobertura_reporter.tpb b/source/reporters/ut_coverage_cobertura_reporter.tpb
index d8aae7f7a..45c1935b1 100644
--- a/source/reporters/ut_coverage_cobertura_reporter.tpb
+++ b/source/reporters/ut_coverage_cobertura_reporter.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_coverage_cobertura_reporter is
+create or replace noneditionable type body ut_coverage_cobertura_reporter is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ create or replace type body ut_coverage_cobertura_reporter is
self in out nocopy ut_coverage_cobertura_reporter
) return self as result is
begin
- self.init($$plsql_unit);
+ self.init($$plsql_unit,ut_output_bulk_buffer());
return;
end;
@@ -29,11 +29,19 @@ create or replace type body ut_coverage_cobertura_reporter is
l_report_lines ut_varchar2_list;
l_coverage_data ut_coverage.t_coverage;
- function get_lines_xml(a_unit_coverage ut_coverage.t_unit_coverage) return clob is
+ function get_line_rate(a_lines_covered in integer, a_lines_hit in integer) return varchar2 is
+ begin
+ return to_char(round((case a_lines_covered when 0 then 0 else a_lines_covered/a_lines_hit end), 17), rpad('FM0.0',20,'9') , 'NLS_NUMERIC_CHARACTERS=''. ''');
+ end;
+
+ procedure get_lines_xml(a_unit_coverage ut_coverage.t_unit_coverage,
+ a_lines_result in out nocopy clob, a_lines_hits out number, a_lines_total out number) is
l_file_part varchar2(32767);
l_result clob;
l_line_no binary_integer;
l_pct integer;
+ l_lines_hits integer := 0;
+ l_lines_total integer := 0;
begin
dbms_lob.createtemporary(l_result, true);
l_line_no := a_unit_coverage.lines.first;
@@ -41,11 +49,14 @@ create or replace type body ut_coverage_cobertura_reporter is
for i in 1 .. a_unit_coverage.total_lines loop
ut_utils.append_to_clob(l_result, ''||chr(10));
end loop;
+ l_lines_hits:=0;
+ l_lines_total:= a_unit_coverage.total_lines;
else
while l_line_no is not null loop
if a_unit_coverage.lines(l_line_no).executions = 0 then
l_file_part := ''||chr(10);
else
+ l_lines_hits:= l_lines_hits+1;
l_file_part := ''
);
@@ -113,9 +134,12 @@ create or replace type body ut_coverage_cobertura_reporter is
while l_unit is not null loop
l_obj_name := a_coverage_data.objects(l_unit).name;
+ dbms_lob.createtemporary(l_lines_xml, true);
+ get_lines_xml(a_coverage_data.objects(l_unit),l_lines_xml,l_line_hits,l_line_total);
+
ut_utils.append_to_list(
l_result,
- ''
+ ''
);
ut_utils.append_to_list(
@@ -126,13 +150,13 @@ create or replace type body ut_coverage_cobertura_reporter is
ut_utils.append_to_list(
l_result,
''
+ ||dbms_xmlgen.convert(l_unit)||'" line-rate="'||get_line_rate(l_line_hits,l_line_total)||'" branch-rate="0.0" complexity="0.0">'
);
ut_utils.append_to_list(l_result, '');
-
- ut_utils.append_to_list( l_result, get_lines_xml(a_coverage_data.objects(l_unit)) );
-
+ ut_utils.append_to_list( l_result,l_lines_xml);
+ dbms_lob.freetemporary(l_lines_xml);
+
ut_utils.append_to_list(l_result, c_lines_footer);
ut_utils.append_to_list(l_result, c_class_footer);
ut_utils.append_to_list(l_result, c_classes_footer);
diff --git a/source/reporters/ut_coverage_cobertura_reporter.tps b/source/reporters/ut_coverage_cobertura_reporter.tps
index ec5cef2cb..799840a55 100644
--- a/source/reporters/ut_coverage_cobertura_reporter.tps
+++ b/source/reporters/ut_coverage_cobertura_reporter.tps
@@ -1,7 +1,7 @@
-create or replace type ut_coverage_cobertura_reporter under ut_coverage_reporter_base(
+create or replace noneditionable type ut_coverage_cobertura_reporter under ut_coverage_reporter_base(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/reporters/ut_coverage_html_reporter.tpb b/source/reporters/ut_coverage_html_reporter.tpb
index 9e51b5cc2..90419371a 100644
--- a/source/reporters/ut_coverage_html_reporter.tpb
+++ b/source/reporters/ut_coverage_html_reporter.tpb
@@ -1,7 +1,7 @@
-create or replace type body ut_coverage_html_reporter is
+create or replace noneditionable type body ut_coverage_html_reporter is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ create or replace type body ut_coverage_html_reporter is
a_html_report_assets_path varchar2 := null
) return self as result is
begin
- self.init($$plsql_unit);
+ self.init($$plsql_unit,ut_output_bulk_buffer());
self.project_name := a_project_name;
assets_path := nvl(a_html_report_assets_path, ut_coverage_report_html_helper.get_default_html_assets_path());
return;
diff --git a/source/reporters/ut_coverage_html_reporter.tps b/source/reporters/ut_coverage_html_reporter.tps
index f6bdcc249..23cf45c0b 100644
--- a/source/reporters/ut_coverage_html_reporter.tps
+++ b/source/reporters/ut_coverage_html_reporter.tps
@@ -1,7 +1,7 @@
-create or replace type ut_coverage_html_reporter under ut_coverage_reporter_base(
+create or replace noneditionable type ut_coverage_html_reporter under ut_coverage_reporter_base(
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
diff --git a/source/reporters/ut_coverage_report_html_helper.pkb b/source/reporters/ut_coverage_report_html_helper.pkb
index f4b633c1f..99c36ab73 100644
--- a/source/reporters/ut_coverage_report_html_helper.pkb
+++ b/source/reporters/ut_coverage_report_html_helper.pkb
@@ -1,7 +1,7 @@
-create or replace package body ut_coverage_report_html_helper is
+create or replace noneditionable package body ut_coverage_report_html_helper is
/*
utPLSQL - Version 3
- Copyright 2016 - 2019 utPLSQL Project
+ Copyright 2016 - 2026 utPLSQL Project
Licensed under the Apache License, Version 2.0 (the "License"):
you may not use this file except in compliance with the License.
@@ -90,7 +90,7 @@ create or replace package body ut_coverage_report_html_helper is
function object_id(a_object_full_name varchar2) return varchar2 is
begin
- return rawtohex(dbms_crypto.hash(src => utl_raw.cast_to_raw(a_object_full_name), typ => dbms_crypto.hash_md5));
+ return rawtohex(dbms_crypto.hash(src => utl_raw.cast_to_raw(a_object_full_name), typ => dbms_crypto.HASH_SH256));
end;
function link_to_source_file(a_object_full_name varchar2) return varchar2 is
@@ -134,9 +134,11 @@ create or replace package body ut_coverage_report_html_helper is
l_file_part varchar2(32767);
l_result ut_varchar2_rows := ut_varchar2_rows();
l_coverage_pct number(5, 2);
- l_coverage_block_pct number(5, 2);
l_hits varchar2(30);
l_blocks varchar2(30);
+ l_line_text varchar2(32767);
+ e_buffer_too_small exception;
+ pragma exception_init ( e_buffer_too_small, -19011 );
begin
l_coverage_pct := coverage_pct(a_coverage_unit.covered_lines, a_coverage_unit.uncovered_lines);
@@ -148,10 +150,16 @@ create or replace package body ut_coverage_report_html_helper is
ut_utils.append_to_list(l_result, l_file_part);
for line_no in 1 .. a_source_code.count loop
+ begin
+ l_line_text := dbms_xmlgen.convert(a_source_code(line_no));
+ exception
+ when e_buffer_too_small then
+ l_line_text := dbms_xmlgen.convert(to_clob(a_source_code(line_no)));
+ end;
if not a_coverage_unit.lines.exists(line_no) then
l_file_part := '