{"id":6841,"date":"2016-04-23T14:48:09","date_gmt":"2016-04-23T12:48:09","guid":{"rendered":"https:\/\/www.salvis.com\/blog\/?p=6841"},"modified":"2023-11-07T22:39:34","modified_gmt":"2023-11-07T21:39:34","slug":"monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube","status":"publish","type":"post","link":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/","title":{"rendered":"Monitoring PL\/SQL Code Evolution"},"content":{"rendered":"\n<p>Last week I presented the PL\/SQL Cop tool suite to a customer in Germany. While preparing the demo\u00a0I had taken my first deeper look at the <a href=\"https:\/\/github.com\/Trivadis\/plsql-cop-sonar\">PL\/SQL Cop SonarQube plugin<\/a>, written by Peter Rohner, a fellow Trivadian. I\u00a0was impressed\u00a0by how well the additional PL\/SQL Cop metrics integrate into SonarQube and how easy it is to monitor the code evolution.<\/p>\n\n\n\n<p>Before I show the code evolution I will go through the metric definitions based on a fairly simple example. If you are not interested in the math, then feel free to skip reading the metric sections.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Password_Check, Version 0.1<\/h2>\n\n\n\n<p>As a starting point, I use the following simplified password verification\u00a0procedure, which ensures that every password contains a digit. I know this procedure is not a candidate for &#8220;good PL\/SQL code&#8221;, but it is based on a real live example. The goal of this piece of code is to explain some metrics, before starting to improve the code.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Password_Check, v0.1<\/span><span role=\"button\" tabindex=\"0\" data-code=\"CREATE OR REPLACE PROCEDURE PASSWORD_CHECK (in_password IN VARCHAR2) IS -- NOSONAR\n   co_digitarray CONSTANT STRING(10)     := '0123456789';\n   co_one        CONSTANT SIMPLE_INTEGER := 1;\n   co_errno      CONSTANT SIMPLE_INTEGER := -20501;\n   co_errmsg     CONSTANT STRING(100)    := 'Password must contain a digit.';\n   l_isdigit     BOOLEAN;\n   l_len_pw      PLS_INTEGER;\n   l_len_array   PLS_INTEGER;\nBEGIN\n   -- initialize variables\n   l_isdigit := FALSE;\n   l_len_pw := LENGTH(in_password);\n   l_len_array := LENGTH(co_digitarray);\n   <<check_digit&gt;&gt;\n   FOR i IN co_one .. l_len_array\n   LOOP\n      <<check_pw_char&gt;&gt;\n      FOR j IN co_one .. l_len_pw\n      LOOP\n         IF SUBSTR(in_password, j, co_one) = SUBSTR(co_digitarray, i, co_one) THEN\n            l_isdigit := TRUE;\n            GOTO check_other_things;\n         END IF;\n      END LOOP check_pw_char;\n   END LOOP check_digit;\n   <<check_other_things&gt;&gt;\n   NULL;\n   \n   IF NOT l_isdigit THEN\n      raise_application_error(co_errno, co_errmsg);\n   END IF;\nEND password_check;\n\/\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> PROCEDURE PASSWORD_CHECK (in_password <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #569CD6\">IS<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #6A9955\">-- NOSONAR<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   co_digitarray <\/span><span style=\"color: #569CD6\">CONSTANT<\/span><span style=\"color: #D4D4D4\"> STRING(<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\">)     := <\/span><span style=\"color: #CE9178\">&#39;0123456789&#39;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   co_one        <\/span><span style=\"color: #569CD6\">CONSTANT<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">SIMPLE_INTEGER<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   co_errno      <\/span><span style=\"color: #569CD6\">CONSTANT<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">SIMPLE_INTEGER<\/span><span style=\"color: #D4D4D4\"> := -<\/span><span style=\"color: #B5CEA8\">20501<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   co_errmsg     <\/span><span style=\"color: #569CD6\">CONSTANT<\/span><span style=\"color: #D4D4D4\"> STRING(<\/span><span style=\"color: #B5CEA8\">100<\/span><span style=\"color: #D4D4D4\">)    := <\/span><span style=\"color: #CE9178\">&#39;Password must contain a digit.&#39;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_isdigit<\/span><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #569CD6\">BOOLEAN<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_len_pw<\/span><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">PLS_INTEGER<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_len_array<\/span><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">PLS_INTEGER<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #6A9955\">-- initialize variables<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_isdigit<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #569CD6\">FALSE<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_len_pw<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #DCDCAA\">LENGTH<\/span><span style=\"color: #D4D4D4\">(in_password);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_len_array<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #DCDCAA\">LENGTH<\/span><span style=\"color: #D4D4D4\">(co_digitarray);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   &lt;&lt;check_digit&gt;&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">FOR<\/span><span style=\"color: #D4D4D4\"> i <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> co_one .. <\/span><span style=\"color: #9CDCFE\">l_len_array<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">LOOP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      &lt;&lt;check_pw_char&gt;&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">FOR<\/span><span style=\"color: #D4D4D4\"> j <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> co_one .. <\/span><span style=\"color: #9CDCFE\">l_len_pw<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">LOOP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">SUBSTR<\/span><span style=\"color: #D4D4D4\">(in_password, j, co_one) = <\/span><span style=\"color: #DCDCAA\">SUBSTR<\/span><span style=\"color: #D4D4D4\">(co_digitarray, i, co_one) <\/span><span style=\"color: #569CD6\">THEN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">l_isdigit<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #569CD6\">TRUE<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #C586C0\">GOTO<\/span><span style=\"color: #D4D4D4\"> check_other_things;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">END LOOP<\/span><span style=\"color: #D4D4D4\"> check_pw_char;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">END LOOP<\/span><span style=\"color: #D4D4D4\"> check_digit;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   &lt;&lt;check_other_things&gt;&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">NULL<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NOT<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_isdigit<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">THEN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #DCDCAA\">raise_application_error<\/span><span style=\"color: #D4D4D4\">(co_errno, co_errmsg);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\"> password_check;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>After running this code through PL\/SQL Cop I get the following metrics. I&nbsp;show here just the SonarQube output, but the results are the same for the <a href=\"https:\/\/github.com\/Trivadis\/plsql-cop-cli\">command line utility<\/a> and the <a href=\"https:\/\/github.com\/Trivadis\/plsql-cop-sqldev\">SQL Developer extension<\/a> of SQL Cop.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.1.png\"><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" width=\"1018\" height=\"987\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.1.png\" alt=\"password_check_v0.1\" class=\"wp-image-6957\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.1.png 1018w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.1-300x291.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.1-768x745.png 768w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1018px) 100vw, 1018px\" \/><\/a><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Simple Metrics<\/h4>\n\n\n\n<p>Here are the definitions of the&nbsp;simple metrics shown above.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em><strong>Bytes<\/strong><\/em> &#8211; the number of bytes (1039)<\/li>\n\n\n\n<li><em><strong>Lines<\/strong><\/em>\u00a0&#8211; the number of physical lines &#8211;\u00a0lines\u00a0separated by OS-specific line separator (33)<\/li>\n\n\n\n<li><em><strong>Comment Lines<\/strong><\/em> &#8211; the number of comment lines &#8211; see line 10 (1)<\/li>\n\n\n\n<li><em><strong>Blank Lines<\/strong><\/em> &#8211; the number of empty lines &#8211; see line 28 (1)<\/li>\n\n\n\n<li><em><strong>Lines Of Code <\/strong><\/em>&nbsp;&#8211; Lines minus comment lines minus blank lines (31)<\/li>\n\n\n\n<li><em><strong>Commands<\/strong><\/em> &#8211; the number of commands from a SQL*Plus point of view &#8211; see CREATE OR REPLACE PROCEDURE (1)<\/li>\n\n\n\n<li><em><strong>Functions<\/strong><\/em>&nbsp;&#8211; the number of program units &#8211; the password_check procedure (1)<\/li>\n\n\n\n<li><em><strong>Statements<\/strong><\/em>&nbsp;&#8211; the number of PL\/SQL statements &#8211; 4 assignments, 2 FOR loops, 2 IF statements, 1 GOTO statement, 1 NULL statement, 1 procedure call (11)<\/li>\n\n\n\n<li><em><strong>Files<\/strong><\/em> &#8211; the number of files processed (1)<\/li>\n\n\n\n<li><em><strong>Directories<\/strong><\/em> &#8211; the number of directories processed (1)<\/li>\n\n\n\n<li><em><strong>Issues<\/strong><\/em> &#8211; the number of Trivadis PL\/SQL &amp; SQL Coding Guideline&nbsp;violations &#8211; Guideline 39: Never use GOTO statements in your code (1)<\/li>\n<\/ul>\n\n\n\n<p>Simple metrics such as <em><strong>Lines of Code<\/strong><\/em> are an&nbsp;easy way to categorise the&nbsp;programs&nbsp;in a project.&nbsp;But the program with the most lines of code does&nbsp;not necessarily have to be the most complex one. Other metrics are better suited to identify the&nbsp;complex parts of a&nbsp;project. But it is important to have a good idea how such metrics are calculated, because no single metric is perfect. I typically identify programs to have a closer look at by a combination of metrics such as lines of code, statements, cyclomatic complexity&nbsp;and the number of severe&nbsp;issues.<\/p>\n\n\n\n<p>See SonarQube <a href=\"http:\/\/docs.sonarqube.org\/display\/SONAR\/Metric+Definitions\">documentation<\/a> for the further&nbsp;metric definitions. Please note, that&nbsp;PL\/SQL Cop does not calculate all metrics and some metrics are calculated a bit differently, e.g. <em><strong>Comment Lines<\/strong><\/em>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"http:\/\/docs.sonarqube.org\/display\/SONAR\/Metric+Definitions#MetricDefinitions-Maintainability\">SQALE Rating<\/a><\/h4>\n\n\n\n<p>SonarQube rates a project using the <em><strong>SQALE Rating <\/strong><\/em>which is is based on the Technical Dept Ratio&nbsp;and&nbsp;calculated as follows:<\/p>\n\n\n<span>$$\\text'tdr' = 100\u22c5{\\text'Technical Debt'}\/{\\text'Development Cost'}$$<\/span>\n\n\n\n<p>where<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span>$\\text'tdr'$<\/span> is defined as the technical dept ratio (1.6%)<\/li>\n\n\n\n<li><span>$\\text'Technical Debt'$<\/span>&nbsp;is defined as the estimated time to fix the issues, PL\/SQL Cop defines the time to fix per issue type (0.25 hours)<\/li>\n\n\n\n<li><span>$\\text'Development Cost'$<\/span> is defined as the estimated time to develop the source code from scratch, the SonarQube default configuration is 30 minutes per <em><strong>Line of Code<\/strong><\/em>, you may amend the value&nbsp;on the Technical Dept page (15.5 hours)<\/li>\n<\/ul>\n\n\n\n<p>The ranges for the SQALE Rating values <em><strong>A<\/strong><\/em>&nbsp;(very good) to <em><strong>E<\/strong><\/em>&nbsp;(very bad)&nbsp;are based&nbsp;on&nbsp;the <a href=\"http:\/\/www.sqale.org\/wp-content\/uploads\/2010\/08\/SQALE-Method-EN-V1-0.pdf\">SQALE Method Definition Document<\/a>. SonarQube uses the default rating scheme &#8220;0.1,0.2,0.5,1&#8221; which may be amended on the Technical Debt page. The rating scheme defines the rating thresholds for A, B, C and D. Higher values lead to an E rating. Here is another way to represent the default rating scheme:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A: <span>$\\text'tdr'$<\/span> &lt;= 10%<\/li>\n\n\n\n<li>B: <span>$\\text'tdr'$<\/span> &gt; 10% and <span>$\\text'tdr'$<\/span> &lt;= 20%<\/li>\n\n\n\n<li>C: <span>$\\text'tdr'$<\/span> &gt; 20% and <span>$\\text'tdr'$<\/span> &lt;= 50%<\/li>\n\n\n\n<li>D: <span>$\\text'tdr'$<\/span> &gt; 50% and <span>$\\text'tdr'$<\/span> &lt;= 100%<\/li>\n\n\n\n<li>E: <span>$\\text'tdr'$<\/span> &gt; 100%<\/li>\n<\/ul>\n\n\n\n<p>Based on the default SQALE Rating scheme, a project rated as &#8220;E&#8221; should be rewritten from scratch, since it would take more time to fix all issues.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"http:\/\/www.mccabe.com\/pdf\/mccabe-nist235r.pdf\">McCabe&#8217;s Cyclomatic Complexity<\/a><\/h4>\n\n\n\n<p>Thomas J. McCabe introduced 1976 the&nbsp;metric <em><strong>Cyclomatic Complexity<\/strong><\/em>&nbsp;which counts the number of paths in the source&nbsp;code. SonarQube uses this metric to represent the complexity of a program.&nbsp;PL\/SQL Cop calculates the cyclomatic complexity as follows:<\/p>\n\n\n<span>$$M=E-N+2P$$<\/span>\n\n\n\n<p>where<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span>$M$<\/span> is defined as the cyclomatic complexity (6)<\/li>\n\n\n\n<li><span>$E$<\/span> is defined as the number of edges (15)<\/li>\n\n\n\n<li><span>$N$<\/span> is defined as the number of nodes (11)<\/li>\n\n\n\n<li><span>$P$<\/span> is defined as the number of connected components\/programs (1)<\/li>\n<\/ul>\n\n\n\n<p>The higher the cyclomatic complexity,&nbsp;the more difficult it is to maintain the code.<\/p>\n\n\n\n<p>Please note that PL\/SQL Cop V1.0.16 adds an additional edge&nbsp;for ELSE branches in IF\/CASE statements, for PL\/SQL blocks and for GOTO statements. I consider this a bug. However, Toad Code Analysis (Xpert) calculates the Cyclomatic Complexity the very same way.<\/p>\n\n\n\n<p>PL\/SQL Cop calculates the <em><strong>Cyclomatic Complexity<\/strong><\/em> per program unit and provides the aggregated&nbsp;<em><strong>Max. Cyclomatic Complexity<\/strong><\/em>&nbsp;on file level.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"http:\/\/www.amazon.com\/Elements-Software-Science-Operating-programming\/dp\/0444002057\">Halstead Volume<\/a><\/h4>\n\n\n\n<p>Maurice H. Halstead introduced 1977 the metric <em><strong>Halstead Volume<\/strong><\/em> which defines the complexity based on the vocabulary and the total number of words\/elements used within a program. In his work Halstead showed&nbsp;also how to express the complexity of&nbsp;academic abstracts using his metrics.&nbsp;PL\/SQL Cop calculates the Halstead volume as follows:<\/p>\n\n\n<span>$$V=N\u22c5log_2n$$<\/span>\n\n\n\n<p>where<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span>$V$<\/span> is defined as the Halstead Volume. (489.7)<\/li>\n\n\n\n<li><span>$N$<\/span> is defined as the program length. <span>$N=N_1+N_2$<\/span> (94)<\/li>\n\n\n\n<li><span>$n$<\/span> is defined as the program vocabulary. <span>$n=n_1+n_2$<\/span> (37)<\/li>\n\n\n\n<li><span>$N_1$<\/span> is defined as the total number of operators (42)<\/li>\n\n\n\n<li><span>$N_2$<\/span> is defined as the total number of operands (52)<\/li>\n\n\n\n<li><span>$n_1$<\/span> is defined as the number of distinct operators (11)<\/li>\n\n\n\n<li><span>$n_2$<\/span> is defined as the number of distinct operands (26)<\/li>\n<\/ul>\n\n\n\n<p>using<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the following <em><strong>operators<\/strong><\/em>:&nbsp;if, then, elsif, case, when, else, loop, for-loop, forall-loop, while-loop, exit, exit-when, goto, return, close, fetch, open, open-for, open-for-using, pragma, exception, procedure-call, assignment, function-call, sub-block, parenthesis, and, or, not, eq, ne, gt, lt, ge, le, semicolon, comma, colon, dot, like, between, minus, plus, star, slash, percent<\/li>\n\n\n\n<li>the following <em><strong>operands<\/strong><\/em>:&nbsp;identifier, string, number<\/li>\n<\/ul>\n\n\n\n<p>The higher the Halstead volume,&nbsp;the more difficult it is to maintain the code.<\/p>\n\n\n\n<p>PL\/SQL Cop calculates the <em><strong>Halstead Volume<\/strong><\/em> per program unit and provides the aggregated&nbsp;<em><strong>Max. Halstead Volume<\/strong><\/em> on file level.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"http:\/\/static1.1.sqspcdn.com\/static\/f\/702523\/9457031\/1290003349713\/200108-Welker.pdf?token=7%2B6uapX6Nng92A2i5ggANrgiH34%3D\">Maintainability Index<\/a><\/h4>\n\n\n\n<p>Paul Oman and Jack Hagemeister introduced 1991 the metric <em><strong>Maintainability Index<\/strong><\/em> which weighs comments and combines it with <em><strong>Halstead Volume<\/strong><\/em> and <em><strong>Cyclomatic Complexity<\/strong><\/em>. PL\/SQL Cop calculates the maintainability index as follows:<\/p>\n\n\n<span>$$\\text'MI'=\\text'MI'woc+\\text'MI'cw$$<\/span>\n\n\n\n<p>where<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span>$\\text'MI'$<\/span> is defined as the Maintainability Index (102.2)<\/li>\n\n\n\n<li><span>$\\text'MI'woc$<\/span> is defined as the <span>$\\text'MI'$<\/span> without comments. <span>$\\text'MI'woc=171\u22125.2\u22c5log_eaveV\u22120.23\u22c5aveM\u221216.2\u22c5log_eaveLOC$<\/span> (86.617)<\/li>\n\n\n\n<li><span style=\"line-height: 1.5;\"><span>$\\text'MI'cw$<\/span> is defined as the <span>$\\text'MI'$<\/span> comment weight. <span>$\\text'MI'cw=50\u22c5sin(\u221a{{2.4\u22c5aveC}\/{aveLOC}})$<\/span> (15.549)<\/span><\/li>\n\n\n\n<li><span style=\"line-height: 1.5;\"><span>$aveV$<\/span> is defined as the average Halstead volume. <span>$aveV={\u2211unitLOC\u22c5V}\/{fileLOC}$<\/span> (489.7)<\/span><\/li>\n\n\n\n<li><span style=\"line-height: 1.5;\"><span>$aveM$<\/span> is defined as the average cyclomatic complexity. <span>$aveM={\u2211unitLOC\u22c5M}\/{fileLOC}$<\/span> (6)<\/span><\/li>\n\n\n\n<li><span style=\"line-height: 1.5;\"><span>$aveLOC$<\/span> is defined as the average lines of code including&nbsp;comments. <span>$aveLOC={\u2211unitLOC}\/{units}$<\/span> (24)<\/span><\/li>\n\n\n\n<li><span style=\"line-height: 1.5;\"><span>$aveC$<\/span> is defined as the average lines of comment. <span>$aveC={\u2211unitC}\/{units}$<\/span> (1)<\/span><\/li>\n\n\n\n<li><span>$unitLOC$<\/span> is defined as the number of lines in a PL\/SQL unit, without declare section (24)<\/li>\n\n\n\n<li><span>$fileLOC$<\/span> is defined as the number of lines in\u00a0the source file (33)<\/li>\n\n\n\n<li><span>$units$<\/span> is defined as the number of&nbsp;PL\/SQL units in a file (1)<\/li>\n\n\n\n<li><span>$unitC$<\/span> is defined as the number of comment lines in a PL\/SQL unit (1)<\/li>\n<\/ul>\n\n\n\n<p>The lower the maintainability index,&nbsp;the more difficult it is to maintain the code.<\/p>\n\n\n\n<p>PL\/SQL Cop calculates the <em><strong>Maintainability Index<\/strong><\/em> per program unit and provides the aggregated&nbsp;<em><strong>Min. Maintainability Index<\/strong><\/em> on file level.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Password_Check, Version 0.2 &#8211; Better<\/h2>\n\n\n\n<p>To get rid of the GOTO I&#8217;ve rewritten the procedure to use regular expressions to look for digits within the password. The code looks now as follows:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Password_Check, v0.2<\/span><span role=\"button\" tabindex=\"0\" data-code=\"CREATE OR REPLACE PROCEDURE PASSWORD_CHECK (in_password IN VARCHAR2) IS\nBEGIN\n   IF NOT REGEXP_LIKE(in_password, '\\d') THEN\n      raise_application_error(-20501, 'Password must contain a digit.');\n   END IF;\nEND;\n\/\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> PROCEDURE PASSWORD_CHECK (in_password <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NOT<\/span><span style=\"color: #D4D4D4\"> REGEXP_LIKE(in_password, <\/span><span style=\"color: #CE9178\">&#39;\\d&#39;<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #569CD6\">THEN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #DCDCAA\">raise_application_error<\/span><span style=\"color: #D4D4D4\">(-<\/span><span style=\"color: #B5CEA8\">20501<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;Password must contain a digit.&#39;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>After loading the new version into SonarQube, the dashboard looks as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1018\" height=\"1022\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2.png\" alt=\"password_check_v0.2\" class=\"wp-image-6960\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2.png 1018w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2-150x150.png 150w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2-300x300.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2-768x771.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2-330x330.png 330w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1018px) 100vw, 1018px\" \/><\/a><\/figure>\n\n\n\n<p>Almost all metrics look better now. But instead of 1 major issue, I have now 5 minor ones. This leads to a higher <em><strong>Technical Dept Ratio<\/strong><\/em> and a bad trend in this area. So let&#8217;s see what these minor issues are.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/issues_password_check_v0.2.png\"><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" width=\"1018\" height=\"760\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/issues_password_check_v0.2.png\" alt=\"issues_password_check_v0.2\" class=\"wp-image-6961\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/issues_password_check_v0.2.png 1018w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/issues_password_check_v0.2-300x224.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/issues_password_check_v0.2-768x573.png 768w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1018px) 100vw, 1018px\" \/><\/a><\/figure>\n\n\n\n<p>I consider all\u00a0guideline violations as not worth fixing and marked them as &#8220;won&#8217;t fix&#8221;. After reloading the unchanged password_check.sql the SonarQube dashboard looks as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2e.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1018\" height=\"1020\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2e.png\" alt=\"password_check_v0.2e\" class=\"wp-image-6970\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2e.png 1018w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2e-150x150.png 150w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2e-300x300.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2e-768x770.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.2e-330x330.png 330w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1018px) 100vw, 1018px\" \/><\/a><\/figure>\n\n\n\n<p>The differences\/improvement to the previous version is shown in parenthesis.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Password_Check, Version 0.3 &#8211; Even Better?<\/h2>\n\n\n\n<p>The version 0.2 code looks really good. No technical debt, no issues. A complexity of 2 and just 7 lines of code.&nbsp;But is it possible to improve this code further? Technically yes, especially since we know how the <em><strong>Maintainability Index<\/strong><\/em> is calculated. We could simply reduce the <em><strong>Lines of Code<\/strong><\/em> as follows:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Password_Check, v0.3<\/span><span role=\"button\" tabindex=\"0\" data-code=\"CREATE OR REPLACE PROCEDURE PASSWORD_CHECK(in_password IN VARCHAR2)IS BEGIN IF NOT REGEXP_LIKE(in_password,'\\d')THEN raise_application_error(-20501,'Password must contain a digit.');END IF;END;\n\/\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> PROCEDURE PASSWORD_CHECK(in_password <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">)<\/span><span style=\"color: #569CD6\">IS<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">BEGIN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NOT<\/span><span style=\"color: #D4D4D4\"> REGEXP_LIKE(in_password,<\/span><span style=\"color: #CE9178\">&#39;\\d&#39;<\/span><span style=\"color: #D4D4D4\">)<\/span><span style=\"color: #569CD6\">THEN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">raise_application_error<\/span><span style=\"color: #D4D4D4\">(-<\/span><span style=\"color: #B5CEA8\">20501<\/span><span style=\"color: #D4D4D4\">,<\/span><span style=\"color: #CE9178\">&#39;Password must contain a digit.&#39;<\/span><span style=\"color: #D4D4D4\">);<\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>And after loading the new version into SonarQube the dashboard looks as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1018\" height=\"1020\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.3.png\" alt=\"password_check_v0.3\" class=\"wp-image-6972\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.3.png 1018w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.3-150x150.png 150w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.3-300x300.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.3-768x770.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/password_check_v0.3-330x330.png 330w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1018px) 100vw, 1018px\" \/><\/a><\/figure>\n\n\n\n<p>Reducing the number of lines from 7 to 2 leads to a better Maintainability Index, but the number of statements, the Cyclomatic Complexity and the Halstead Volume are still the same. The change from version 0.2 to 0.3 reduces the readability of the code and has a negative value.\u00a0That clearly shows, that the <em><strong>Maintainability Index<\/strong><\/em> has its flaws (see also\u00a0<a href=\"https:\/\/avandeursen.com\/2014\/08\/29\/think-twice-before-using-the-maintainability-index\/\">https:\/\/avandeursen.com\/2014\/08\/29\/think-twice-before-using-the-maintainability-index\/<\/a>). There are various ways to discourage such kind of changes in a project. Using a formatter\/beautifier with agreed settings is my favourite.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Code Evolution<\/h2>\n\n\n\n<p>SonarQube\u00a0shows the metrics of the latest two versions in the dashboard. Use the Time Machine page to show metrics of more than two versions of the project.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png\"><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" width=\"1018\" height=\"838\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png\" alt=\"timemachine2\" class=\"wp-image-6976\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png 1018w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2-300x247.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2-768x632.png 768w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1018px) 100vw, 1018px\" \/><\/a><\/figure>\n\n\n\n<p>Or use the Compare page to compare metrics between&nbsp;versions or projects.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/compare.png\"><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" width=\"976\" height=\"600\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/compare.png\" alt=\"compare\" class=\"wp-image-6977\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/compare.png 976w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/compare-300x184.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/compare-768x472.png 768w\" sizes=\"auto, (max-width:767px) 480px, (max-width:976px) 100vw, 976px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Every metric has its flaws, for example<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em><strong>Lines of Code<\/strong> <\/em>does not account for the code complexity<\/li>\n\n\n\n<li><em><strong>Cyclomatic Complexity<\/strong><\/em> does not account for the length of a program and the complexity of a statement<\/li>\n\n\n\n<li><em><strong>Halstead Volume<\/strong><\/em> does not account for the number of paths in the program<\/li>\n\n\n\n<li><em><strong>The maintainability index<\/strong><\/em> cannot distinguish between useful and useless comments and does not account for code formatting<\/li>\n<\/ul>\n\n\n\n<p>But these metrics are still useful to\u00a0identify complex programs, to\u00a0measure code evolution (improvements, degradations) and to\u00a0help you write better PL\/SQL, if you do not trust in metrics blindly.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last week I presented the PL\/SQL Cop tool suite to a customer in Germany. While preparing the demo\u00a0I had taken my first deeper look at the PL\/SQL Cop SonarQube plugin, written by Peter Rohner, a fellow Trivadian. I\u00a0was impressed\u00a0by how well the additional PL\/SQL Cop metrics integrate into SonarQube and how easy<span class=\"excerpt-hellip\"> [\u2026]<\/span><\/p>\n","protected":false},"author":1,"featured_media":6976,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[13,92,91],"class_list":["post-6841","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oracle","tag-plsql","tag-plsql-cop","tag-sonarqube"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Monitoring PL\/SQL Code Evolution - Philipp Salvisberg&#039;s Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Monitoring PL\/SQL Code Evolution - Philipp Salvisberg&#039;s Blog\" \/>\n<meta property=\"og:description\" content=\"Last week I presented the PL\/SQL Cop tool suite to a customer in Germany. While preparing the demo\u00a0I had taken my first deeper look at the PL\/SQL Cop SonarQube plugin, written by Peter Rohner, a fellow Trivadian. I\u00a0was impressed\u00a0by how well the additional PL\/SQL Cop metrics integrate into SonarQube and how easy [\u2026]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/\" \/>\n<meta property=\"og:site_name\" content=\"Philipp Salvisberg&#039;s Blog\" \/>\n<meta property=\"article:published_time\" content=\"2016-04-23T12:48:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-11-07T21:39:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1018\" \/>\n\t<meta property=\"og:image:height\" content=\"838\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Philipp Salvisberg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@phsalvisberg\" \/>\n<meta name=\"twitter:site\" content=\"@phsalvisberg\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Philipp Salvisberg\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/\"},\"author\":{\"name\":\"Philipp Salvisberg\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"headline\":\"Monitoring PL\\\/SQL Code Evolution\",\"datePublished\":\"2016-04-23T12:48:09+00:00\",\"dateModified\":\"2023-11-07T21:39:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/\"},\"wordCount\":1883,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"image\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2016\\\/04\\\/timemachine2.png\",\"keywords\":[\"PL\\\/SQL\",\"PL\\\/SQL Cop\",\"SonarQube\"],\"articleSection\":[\"Oracle\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/\",\"name\":\"Monitoring PL\\\/SQL Code Evolution - Philipp Salvisberg&#039;s Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2016\\\/04\\\/timemachine2.png\",\"datePublished\":\"2016-04-23T12:48:09+00:00\",\"dateModified\":\"2023-11-07T21:39:34+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2016\\\/04\\\/timemachine2.png\",\"contentUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2016\\\/04\\\/timemachine2.png\",\"width\":1018,\"height\":838},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2016\\\/04\\\/23\\\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Monitoring PL\\\/SQL Code Evolution\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/\",\"name\":\"Philipp Salvisberg&#039;s Blog\",\"description\":\"Database-centric development\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\",\"name\":\"Philipp Salvisberg\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"contentUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"width\":400,\"height\":400,\"caption\":\"Philipp Salvisberg\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\"},\"sameAs\":[\"http:\\\/\\\/www.salvis.com\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Monitoring PL\/SQL Code Evolution - Philipp Salvisberg&#039;s Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/","og_locale":"en_US","og_type":"article","og_title":"Monitoring PL\/SQL Code Evolution - Philipp Salvisberg&#039;s Blog","og_description":"Last week I presented the PL\/SQL Cop tool suite to a customer in Germany. While preparing the demo\u00a0I had taken my first deeper look at the PL\/SQL Cop SonarQube plugin, written by Peter Rohner, a fellow Trivadian. I\u00a0was impressed\u00a0by how well the additional PL\/SQL Cop metrics integrate into SonarQube and how easy [\u2026]","og_url":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/","og_site_name":"Philipp Salvisberg&#039;s Blog","article_published_time":"2016-04-23T12:48:09+00:00","article_modified_time":"2023-11-07T21:39:34+00:00","og_image":[{"width":1018,"height":838,"url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png","type":"image\/png"}],"author":"Philipp Salvisberg","twitter_card":"summary_large_image","twitter_creator":"@phsalvisberg","twitter_site":"@phsalvisberg","twitter_misc":{"Written by":"Philipp Salvisberg","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/#article","isPartOf":{"@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/"},"author":{"name":"Philipp Salvisberg","@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"headline":"Monitoring PL\/SQL Code Evolution","datePublished":"2016-04-23T12:48:09+00:00","dateModified":"2023-11-07T21:39:34+00:00","mainEntityOfPage":{"@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/"},"wordCount":1883,"commentCount":0,"publisher":{"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"image":{"@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/#primaryimage"},"thumbnailUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png","keywords":["PL\/SQL","PL\/SQL Cop","SonarQube"],"articleSection":["Oracle"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/","url":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/","name":"Monitoring PL\/SQL Code Evolution - Philipp Salvisberg&#039;s Blog","isPartOf":{"@id":"https:\/\/www.salvis.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/#primaryimage"},"image":{"@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/#primaryimage"},"thumbnailUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png","datePublished":"2016-04-23T12:48:09+00:00","dateModified":"2023-11-07T21:39:34+00:00","breadcrumb":{"@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/#primaryimage","url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png","contentUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2016\/04\/timemachine2.png","width":1018,"height":838},{"@type":"BreadcrumbList","@id":"https:\/\/www.salvis.com\/blog\/2016\/04\/23\/monitoring-plsql-code-evolution-with-plsql-cop-for-sonarqube\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.salvis.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Monitoring PL\/SQL Code Evolution"}]},{"@type":"WebSite","@id":"https:\/\/www.salvis.com\/blog\/#website","url":"https:\/\/www.salvis.com\/blog\/","name":"Philipp Salvisberg&#039;s Blog","description":"Database-centric development","publisher":{"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.salvis.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515","name":"Philipp Salvisberg","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","contentUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","width":400,"height":400,"caption":"Philipp Salvisberg"},"logo":{"@id":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg"},"sameAs":["http:\/\/www.salvis.com\/"]}]}},"_links":{"self":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/6841","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/comments?post=6841"}],"version-history":[{"count":140,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/6841\/revisions"}],"predecessor-version":[{"id":12592,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/6841\/revisions\/12592"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/media\/6976"}],"wp:attachment":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/media?parent=6841"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/categories?post=6841"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/tags?post=6841"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}