|
| 1 | +create or replace type body ut_compound_data_value as |
| 2 | + /* |
| 3 | + utPLSQL - Version 3 |
| 4 | + Copyright 2016 - 2017 utPLSQL Project |
| 5 | + |
| 6 | + Licensed under the Apache License, Version 2.0 (the "License"): |
| 7 | + you may not use this file except in compliance with the License. |
| 8 | + You may obtain a copy of the License at |
| 9 | + |
| 10 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | + |
| 12 | + Unless required by applicable law or agreed to in writing, software |
| 13 | + distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | + See the License for the specific language governing permissions and |
| 16 | + limitations under the License. |
| 17 | + */ |
| 18 | + |
| 19 | + overriding member function get_object_info return varchar2 is |
| 20 | + begin |
| 21 | + return self.data_type||' [ count = '||self.elements_count||' ]'; |
| 22 | + end; |
| 23 | + |
| 24 | + overriding member function is_null return boolean is |
| 25 | + begin |
| 26 | + return ut_utils.int_to_boolean(self.is_data_null); |
| 27 | + end; |
| 28 | + |
| 29 | + overriding member function is_diffable return boolean is |
| 30 | + begin |
| 31 | + return true; |
| 32 | + end; |
| 33 | + |
| 34 | + member function is_empty return boolean is |
| 35 | + begin |
| 36 | + return self.elements_count = 0; |
| 37 | + end; |
| 38 | + |
| 39 | + overriding member function is_multi_line return boolean is |
| 40 | + begin |
| 41 | + return not self.is_null() and not self.is_empty(); |
| 42 | + end; |
| 43 | + |
| 44 | + overriding member function compare_implementation(a_other ut_data_value) return integer is |
| 45 | + begin |
| 46 | + return compare_implementation( a_other, null, null); |
| 47 | + end; |
| 48 | + |
| 49 | + overriding member function to_string return varchar2 is |
| 50 | + l_results ut_utils.t_clob_tab; |
| 51 | + c_max_rows constant integer := 20; |
| 52 | + l_result clob; |
| 53 | + l_result_string varchar2(32767); |
| 54 | + begin |
| 55 | + if not self.is_null() then |
| 56 | + dbms_lob.createtemporary(l_result, true); |
| 57 | + ut_utils.append_to_clob(l_result,'Data:'||chr(10)); |
| 58 | + --return first c_max_rows rows |
| 59 | + execute immediate ' |
| 60 | + select xmlserialize( content ucd.item_data no indent) |
| 61 | + from '|| ut_utils.ut_owner ||'.ut_compound_data_tmp ucd |
| 62 | + where ucd.data_id = :data_id |
| 63 | + and ucd.item_no <= :max_rows' |
| 64 | + bulk collect into l_results using self.data_id, c_max_rows; |
| 65 | + |
| 66 | + ut_utils.append_to_clob(l_result,l_results); |
| 67 | + |
| 68 | + l_result_string := ut_utils.to_string(l_result,null); |
| 69 | + dbms_lob.freetemporary(l_result); |
| 70 | + end if; |
| 71 | + return l_result_string; |
| 72 | + end; |
| 73 | + |
| 74 | + overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return varchar2 is |
| 75 | + l_result clob; |
| 76 | + l_result_string varchar2(32767); |
| 77 | + begin |
| 78 | + l_result := get_data_diff(a_other, a_exclude_xpath, a_include_xpath); |
| 79 | + l_result_string := ut_utils.to_string(l_result,null); |
| 80 | + dbms_lob.freetemporary(l_result); |
| 81 | + return l_result_string; |
| 82 | + end; |
| 83 | + |
| 84 | + member function get_data_diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return clob is |
| 85 | + c_max_rows constant integer := 20; |
| 86 | + l_result clob; |
| 87 | + l_results ut_utils.t_clob_tab := ut_utils.t_clob_tab(); |
| 88 | + l_message varchar2(32767); |
| 89 | + l_ut_owner varchar2(250) := ut_utils.ut_owner; |
| 90 | + l_diff_row_count integer; |
| 91 | + l_actual ut_compound_data_value; |
| 92 | + l_diff_id raw(16); |
| 93 | + l_row_diffs ut_compound_data_helper.tt_row_diffs; |
| 94 | + begin |
| 95 | + if not a_other is of (ut_compound_data_value) then |
| 96 | + raise value_error; |
| 97 | + end if; |
| 98 | + l_actual := treat(a_other as ut_compound_data_value); |
| 99 | + |
| 100 | + dbms_lob.createtemporary(l_result,true); |
| 101 | + |
| 102 | + --diff rows and row elements |
| 103 | + l_diff_id := dbms_crypto.hash(self.data_id||l_actual.data_id,2); |
| 104 | + -- First tell how many rows are different |
| 105 | + execute immediate 'select count(*) from ' || l_ut_owner || '.ut_compound_data_diff_tmp where diff_id = :diff_id' into l_diff_row_count using l_diff_id; |
| 106 | + |
| 107 | + if l_diff_row_count > 0 then |
| 108 | + l_row_diffs := ut_compound_data_helper.get_rows_diff( |
| 109 | + self.data_id, l_actual.data_id, l_diff_id, c_max_rows, a_exclude_xpath, a_include_xpath |
| 110 | + ); |
| 111 | + l_message := chr(10) |
| 112 | + ||'Rows: [ ' || l_diff_row_count ||' differences' |
| 113 | + || case when l_diff_row_count > c_max_rows and l_row_diffs.count > 0 then ', showing first '||c_max_rows end |
| 114 | + ||' ]' || chr(10) |
| 115 | + || case when l_row_diffs.count = 0 |
| 116 | + then ' All rows are different as the columns are not matching.' end; |
| 117 | + ut_utils.append_to_clob( l_result, l_message ); |
| 118 | + for i in 1 .. l_row_diffs.count loop |
| 119 | + l_results.extend; |
| 120 | + l_results(l_results.last) := ' Row No. '||l_row_diffs(i).rn||' - '||rpad(l_row_diffs(i).diff_type,10)||l_row_diffs(i).diffed_row; |
| 121 | + end loop; |
| 122 | + ut_utils.append_to_clob(l_result,l_results); |
| 123 | + end if; |
| 124 | + return l_result; |
| 125 | + end; |
| 126 | + |
| 127 | + member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2) return integer is |
| 128 | + l_other ut_compound_data_value; |
| 129 | + l_ut_owner varchar2(250) := ut_utils.ut_owner; |
| 130 | + l_column_filter varchar2(32767); |
| 131 | + l_diff_id raw(16); |
| 132 | + l_result integer; |
| 133 | + --the XML stylesheet is applied on XML representation of data to exclude column names from comparison |
| 134 | + --column names and data-types are compared separately |
| 135 | + l_xml_data_fmt constant xmltype := xmltype( |
| 136 | + q'[<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
| 137 | + <xsl:strip-space elements="*" /> |
| 138 | + <xsl:template match="/child::*"> |
| 139 | + <xsl:for-each select="child::node()"> |
| 140 | + <xsl:choose> |
| 141 | + <xsl:when test="*[*]"><xsl:copy-of select="node()"/></xsl:when> |
| 142 | + <xsl:when test="position()=last()"><xsl:value-of select="normalize-space(.)"/><xsl:text>
</xsl:text></xsl:when> |
| 143 | + <xsl:otherwise><xsl:value-of select="normalize-space(.)"/>,</xsl:otherwise> |
| 144 | + </xsl:choose> |
| 145 | + </xsl:for-each> |
| 146 | + </xsl:template> |
| 147 | + </xsl:stylesheet>]'); |
| 148 | + begin |
| 149 | + if not a_other is of (ut_compound_data_value) then |
| 150 | + raise value_error; |
| 151 | + end if; |
| 152 | + |
| 153 | + l_other := treat(a_other as ut_compound_data_value); |
| 154 | + |
| 155 | + l_diff_id := dbms_crypto.hash(self.data_id||l_other.data_id,2); |
| 156 | + l_column_filter := ut_compound_data_helper.get_columns_filter(a_exclude_xpath, a_include_xpath); |
| 157 | + -- Find differences |
| 158 | + execute immediate 'insert into ' || l_ut_owner || '.ut_compound_data_diff_tmp ( diff_id, item_no ) |
| 159 | + select :diff_id, nvl(exp.item_no, act.item_no) |
| 160 | + from (select '||l_column_filter||', ucd.item_no |
| 161 | + from ' || l_ut_owner || '.ut_compound_data_tmp ucd where ucd.data_id = :self_guid) exp |
| 162 | + full outer join |
| 163 | + (select '||l_column_filter||', ucd.item_no |
| 164 | + from ' || l_ut_owner || '.ut_compound_data_tmp ucd where ucd.data_id = :l_other_guid) act |
| 165 | + on exp.item_no = act.item_no '|| |
| 166 | + 'where nvl( dbms_lob.compare(' || |
| 167 | + /*the xmltransform removes column names and leaves column data to be compared only*/ |
| 168 | + ' xmltransform(exp.item_data, :l_xml_data_fmt).getclobval()' || |
| 169 | + ', xmltransform(act.item_data, :l_xml_data_fmt).getclobval())' || |
| 170 | + ',1' || |
| 171 | + ') != 0' |
| 172 | + using in l_diff_id, a_exclude_xpath, a_include_xpath, self.data_id, |
| 173 | + a_exclude_xpath, a_include_xpath, l_other.data_id, l_xml_data_fmt, l_xml_data_fmt; |
| 174 | + |
| 175 | + --result is OK only if both are same |
| 176 | + if sql%rowcount = 0 and self.elements_count = l_other.elements_count then |
| 177 | + l_result := 0; |
| 178 | + else |
| 179 | + l_result := 1; |
| 180 | + end if; |
| 181 | + return l_result; |
| 182 | + end; |
| 183 | + |
| 184 | +end; |
| 185 | +/ |
0 commit comments