17
Extending Toad for Oracle’s Code Xpert with the SonarSource dashboard Olivier Gaudin – SonarSource SA Toad for Oracle’s Code Xpert utility (part of the Toad Professional Edition) is one of the best PL/SQL code analyzers available on the market, embedding the know-how of leading PL/SQL experts such as Steven Feuerstein. However, although there is the option to store data in the Code Xpert repository for point-in-time reporting, it is still primarily a static reporting tool and therefore cannot report dynamically on code quality trends as part of an overall quality strategy in a company, without manually querying the repository tables. SonarSource is a company whose main objective is democratization of source code quality management. To reach this objective, the development of Sonar , an open source platform to manage source code quality, started two years ago. Sonar enables the collection, analysis and reporting of quality metrics on source code. Its functionality is articulated around 7 strengths: 1. a flexible and powerful data collection engine 2. a synthetic project dashboard, showing every axis of quality analysis (coding rules, comments density, potential bugs’ detection, code duplication, unit tests, standards metrics and code complexity) 3. a centralized configuration using quality profiles with associated alerts 4. a TimeMachine to follow trends and compare versions 5. several tools to chase defects 6. a consolidated dashboard showing all projects at a glance 7. extensibility through a powerful API and a plugins forge Originally built for Java, Sonar has been extended to include the PL/SQL language through a commercial plugin . In this article, I am going to explain how Sonar leverages Toad’s Code Xpert unlocking its full potential in order to provide a full code quality management product. Running code analysis with Toad’s Code Xpert covers several axes of code quality management However, this is not sufficient if

Extending Toads Code Xpert With the Sonar Source Dashboard

Embed Size (px)

Citation preview

Page 1: Extending Toads Code Xpert With the Sonar Source Dashboard

Extending Toad for Oracle’s Code Xpert with the SonarSource dashboard

Olivier Gaudin – SonarSource SA

Toad for Oracle’s Code Xpert utility (part of the Toad Professional Edition) is one of the best PL/SQL code analyzers available on the market, embedding the know-how of leading PL/SQL experts such as Steven Feuerstein. However, although there is the option to store data in the Code Xpert repository for point-in-time reporting, it is still primarily a static reporting tool and therefore cannot report dynamically on code quality trends as part of an overall quality strategy in a company, without manually querying the repository tables.

SonarSource is a company whose main objective is democratization of source code quality management. To reach this objective, the development of Sonar, an open source platform to manage source code quality, started two years ago.Sonar enables the collection, analysis and reporting of quality metrics on source code. Its functionality is articulated around 7 strengths:

1. a flexible and powerful data collection engine2. a synthetic project dashboard, showing every axis of quality analysis (coding rules,

comments density, potential bugs’ detection, code duplication, unit tests, standards metrics and code complexity)

3. a centralized configuration using quality profiles with associated alerts4. a TimeMachine to follow trends and compare versions5. several tools to chase defects6. a consolidated dashboard showing all projects at a glance7. extensibility through a powerful API and a plugins forge

Originally built for Java, Sonar has been extended to include the PL/SQL language through a commercial plugin. In this article, I am going to explain how Sonar leverages Toad’s Code Xpert unlocking its full potential in order to provide a full code quality management product.

Running code analysis with Toad’s Code Xpert covers several axes of code quality management However, this is not sufficient if you are required to have a global approach. What do I mean by that? If you want to manage your projects portfolio globally, follow the evolution throughout time, get alerts and then maybe delegate some remediation work, you are left with a lot of manual work to consolidate data, compare projects or versions... That is where Sonar comes into play with the following features:

1. Quality Profile managementA quality profile is the set of rules that you choose to apply when you run code analysis on projects. In an enterprise environment, you would certainly expect that several quality profiles are defined and get associated depending on the nature of the project.

Page 2: Extending Toads Code Xpert With the Sonar Source Dashboard

Every rule defined in a quality profile is managed in Sonar. Priority, activation and configuration are handled centrally in the tool.

In Sonar, the set of rules is coupled to thresholds on metrics in order to trigger alerts. Therefore it becomes possible to mission Sonar to proactively notify you if a project reaches more than 30% of rules violation for example.

2. Central reportingSonar centralizes reporting at two levels.

Firstly at the project level where it enables its user to get a comprehensive dashboard (that extends to any directory) enabling to view all metrics at a glance to evaluate axes that need to be worked on. On top of the metrics covered by Code Xpert, Sonar also reports on duplicated code and comments.

Page 3: Extending Toads Code Xpert With the Sonar Source Dashboard

Secondly, at the projects portfolio level to be able to be able to get the big picture projects portfolio. The configurable dashboard enables to compare projects and quickly understand where risk lies. This dashboard is cross language (Java and PL/SQL in this case).

3. Metric evolution

At every analysis, measures are recorded into the Sonar database and add to historical data. Sonar has a TimeMachine that enables to go back in time, compare versions and monitor evolution.

Page 4: Extending Toads Code Xpert With the Sonar Source Dashboard

4. Chasing tools

Sonar embarks a collection of chasing tools to track down the defects. Amongst them: files cloud, hotspots, drill downs

Page 5: Extending Toads Code Xpert With the Sonar Source Dashboard

5. The resource viewer

The resource viewer enables to display the source code “tainted” with a specific type of defect. All defects of the selected type are highlighted within the code.

Moreover, the plugin embarks a PL/SQL code extractor that enables the analysis of any Oracle Forms application.

The Sonar PL/SQL plugin is now at version 1.5 and currently covers 4 quality axes out of 6 possible. Here is the list of planned short-term evolutions:

Integration of SQLScan Integration of File complexity (McCabe) Integration of Unit tests

Here are a few links to help you find out more about Sonar and the PL/SQL plugin:

The treemap The files cloud

The hotspots The drill downs

Page 6: Extending Toads Code Xpert With the Sonar Source Dashboard

Sonar web site: http://sonar.codehaus.org Sonar in action: http://nemo.sonar.codehaus.org Sample PL/SQL project :

http://nemo.sonarsource.org/project/index/nl.oracledeveloper:utplsql To download a limited version of the plugin :

http://www.sonarsource.com/plugins

About the author

Olivier Gaudin is co-founder and director of SonarSource S.A.; an IT company created in November 2008 and based in Plan-les-Ouates, Switzerland.

He has 12 years experience in IT, mainly in the banking industry and experience heading Development and Application support departments.

He has a strong background in SQL and has been involved in CMMI and ITIL implementations and have an acute awareness of quality and reliability of processes.

PL/SQL Source Code Control inside the data-base – After Compile trigger for automatic ar-chivingSource code control for PL/SQL is often discussed in the organizations that I encounter. The database it-self will always hold the latest version of the PL/SQL objects we are using or developing. The sources can of course also be held in files and those files can be managed in Source Code Control systems like CVS, Subversion, ClearCase, Visual Source Safe etc. that do version control.

Many tools for PL/SQL development like TOAD and PL/SQL Developer or even JDeveloper, do not readily integrate with such tools for PL/SQL code. Besides, most of these tools are used by developers directly on the PL/SQL source as it is held in the database. Developers do not write the code to file all the time and they certainly do not check in their code after each change they make. They compile any changes, sending the latest version of their code to be held in the database – overwriting the previous state of their PL/SQL object – and that is that. Perhaps after a number of compilations they may save to a file and at the end of the day or some large task they may check in to the Source Code Control system.

The potential issues with this way of working – and believe me, I have run into them on many occasions – is that developers often lose stages of their code that later on they want to retrieve.

For example: you start working in the morning on a source that you retrieve from the filesystem or source code control environment. You make changes, compiling them as you go along. At some point, you have a pretty good intermediate result. Youthen continue with some intricate changes and you make some additional modifications and all of a sud-den, you have messed up your code. And you would really like to return to that intermediate result. But: where is it? No longer in the database – overwritten by the next compilation. Not on the file system, as you forgot to save. And certainly not in the Source Code Control repository, as you did not check in.

Page 7: Extending Toads Code Xpert With the Sonar Source Dashboard

Then there is the everpresent risk of two developers working on the same package at about the same time. Compilation by the second developer will immediately override and therefore discard any changes made by the first.

In this article I would like to outline a very simple mechanism for using the database itself as archive for all these intermediate versions. The essential pieces for this ‘working archive’ or operational repository: a table, a package and a trigger. The machinery is put into motion by a Oracle 9i and higher DDL Event trigger, fired on the CREATE (or compile) event.The table PLSQL_ARCHIVE will hold earlier versions of the PL/SQL objects in the schema you are working on. For each previous version, it willThe table is set up to hold a number of values for each Version of PL/SQL Objects: the PL/SQL source, the timestamp for the compilation/creation, the status, size and errors and when provided by the devel-oper through annotations in the PL/SQL source also the Author, the Version, a Branch, the Priority and possibly additional Comments. We define the table like this:

create table plsql_archive( object_name varchar2(128), object_type varchar2(19), object_owner varchar2(32), creation_time date, status varchar2(7), source clob, errors clob, author varchar2(128), version_label varchar2(32), seq number(10), branch varchar2(32), source_size number(10), pri-ority varchar2(32), comments varchar2(4000), label varchar2(4000) )/

Whenever we compile – create or replace – a PL/SQL object, the CREATE DDL event is fired and we can intercept that event with our own trigger. At that point we have various pieces of information that we can use to insert a new record into the PLSQL_ARCHIVE table.

CREATE OR REPLACE TRIGGER plsql_archiver_trgAFTER CREATEON SCHEMABEGIN if ora_dict_obj_type in ( 'PACKAGE', 'PACKAGE BODY', 'TRIGGER','FUNCTION', 'PROCEDURE') then plsql_archiver.archive_version ( p_object_name => ora_dict_obj_name , p_object_type => ora_dict_obj_type , p_object_owner => ora_dict_obj_owner , p_creation_time => systimestamp ); end if;END plsql_archiver_trg;/

We need a Nested Table Type defined in the server:

create type string_table as table of varchar2(4000)/

The package plsql_archiver:

CREATE OR REPLACE package plsql_archiverasprocedure archive_version( p_object_name in varchar2, p_object_type in varchar2, p_object_owner in varchar2, p_creation_time in date);--procedure revert_to_previous_version -- undo last change; can be called repeatedly( p_object_name in varchar2, p_object_type in varchar2, p_object_owner in varchar2, p_purge_latest in varchar2 default 'Y' );--procedure revert_to_version( p_object_name in varchar2, p_object_type in varchar2, p_ob-ject_owner in varchar2, p_version_label in varchar2, p_branch in varchar2, p_seq in number, p_purge_later in varchar2 default 'N' );-- wildcards allowedprocedure purge( p_object_name in varchar2, p_object_type in varchar2, p_object_owner in varchar2 , p_status in varchar2 default null, p_priority in varchar2 default null, p_from_datetime in date default null, p_to_datetime in date default null, p_branch in varchar2 default null, p_seq_from in number default null, p_seq_to in number default null);--/* Not yet im-plemented:-- wildcards allowed-- this procedure will link all specified object ver-sions to the label specified through p_labelprocedure create_stripe( p_label in varchar2, p_object_name in varchar2 , p_object_type in varchar2, p_object_owner in varchar2 , p_from_datetime in date default null, p_to_date-time in date default null, p_branch in varchar2 default null, p_seq in number default null);*/function version_graph( p_object_name in varchar2, p_object_type in varchar2, p_object_owner in varchar2 , p_show_version_label in varchar2 default 'Y' -- show the version label , p_show_seq in varchar2 default 'N' -- show the seq value for each version (appended to the version label if that is requested too), p_show_datetime in varchar2 default 'N' -- display the timestamp of the creation of each version, p_show_author in varchar2 default 'N' -- display the author of each version, p_show_labels in varchar2 default 'N' -- display the labels or stripes a version is associated with, p_show_status in varchar2 default 'N' -- display the status (VALID or INVALID) of the version,

Page 8: Extending Toads Code Xpert With the Sonar Source Dashboard

p_show_comments in varchar2 default 'N' -- display the Comments for each ver-sion) return string_table;end plsql_archiver;/

We will bother with the actual implementation of this package later on. Let’s first see how we can make use of what we have created thusfar:

Get an overview of all compilations in our schema, including timestamp and resulting status Get the errors for a particular "version" of a PL/SQL object Get the source at a particular moment in time, for example to compare with the current version Revert to a specifc stage in the development of a PL/SQL object Create a Stripe or Label, associating specific versions of all or at least many PL/SQL program

units with what could be a release, a patch or whatever the stripe is to signify Gather some analytical data on the PL/SQL development process: frequency of compilations,

evolution of number of errors and size of the source When the developer uses annotations to provide comments, priority, version label and branch we

can even retrieve version graphs

Some of the information stored in the PLSQL_ARCHIVE table is inherently available at AFTER CRE-ATE trigger time, such as the source, the errors, the object name, size and type and the compilation time. Others will have to be provided as annotations or meta-data in the source code, specified by the devel-oper him or herself.

In the implementation of the PLSQL_ARCHIVER presented below, we make use of the following anno-tations, largely following the style used in JavaDoc and also adopted in PLDoc – an open source project for generation of technical documentation for PL/SQL code:

@author – for the author or developer of the (current version of the) PL/SQL unit @version – the version label for the current instance of the PL/SQL object @label – the label or stripe that this current instance of the PL/SQL object is associated with;

examples of Labels are Releases, Patches, Versions of (reusable) Components etc. @branch – an indication of the particular branch or subproject the PL/SQL unit is currently

being reworked for; when not explicitly set, the MAIN branch is assumed. Examples of other branches are ADD_SECURITY (a subproject), 1.0PTCH2 (Patch 2 on Release 1.0) and 10gR2_PORT (the collection instances that make use of new Oracle 10gR2 PL/SQL features.

@priority – an indication of the extent of changes in this version compared to the previous ver-sion; typicaly values are Pico, Minor, Normal, Major

@comments – any additional description of this version of the object, for example the changes with respect to the previous version

The developer would include these keywords or annotations somewhere in a comment section in the PL/SQL object. For example:

create or replace procedure test_archiveris l_t number; /**** * Annotations for the PLSQL_ARCHIVER framework * * @author Lucas Jellema * @version 1.2 * @com-ments Just a simple trial procedure to build some version history for * @priority Normal *****/ begin for i in 1..100 loop null; i=i+1; end loop;end;/

Some simple examples:To list the recent history of PL/SQL compilation:

select object_name, object_type, object_owner, source_size, cre-ation_time , errorsfrom plsql_archiveorderby creation_time desc/

To find the most recent compilation errors:

select *from ( select errors from plsql_archive order by creation_time desc )where rownum =1 /

To undo the last "check in" or last compilation of an object:

Page 9: Extending Toads Code Xpert With the Sonar Source Dashboard

begin plsql_archiver.revert_to_previous_version -- undo last change; can be called repeatedly ( p_object_name => 'TEST_ARCHIVER' , p_object_type => 'PROCEDURE' , p_object_owner => USER , p_purge => 'Y' -- delete the most recently compiled code ) ;end;/

To view the entire version tree for our TEST_ARCHIVER object:

set long 4000set linesize 300set pagesize 1000select *from table ( plsql_archiv-er.version_graph ( 'TEST_ARCHIVER' , 'PROCEDURE' , USER , 'Y' -- version_label , 'Y' -- seq , 'Y' -- creation datetime , 'Y' -- author , 'Y' -- la-bels , 'Y' -- status , 'Y' -- comments ) ) version_history/

It is probably wise to start with somewhat less information. Let’s say just the Version Label, the Se-quence, Status and the Creation Date Time. The result for our TEST_ARCHIVER object looks some-thing like:

select *from table ( plsql_archiver.version_graph ( 'TEST_ARCHIVER' , 'PROCEDURE' , USER , 'Y' -- version_label , 'Y' -- seq , 'Y' -- creation datetime , 'N' -- author , 'N' -- labels , 'Y' -- status , 'N' -- comments ) ) version_history/COLUMN_VALUE----------------------------------------------------------------------------------------------------Version Graph for PROCEDURE TEST_ARCHIVER (schema AGS)MAIN Patch 3 Release 2.0 ADD_SECUTIRY New_BRANCH1.1 ()09-OCT 21:28:33 |1.1 ()09-OCT 21:28:43 |1.2 (1)09-OCT 21:39:03 |1.2 (2)09-OCT 21:40:21 |1.2 (3)09-OCT 21:40:22 |1.2 (4)09-OCT 21:40:23 |1.2 (5)09-OCT 22:11:57 1.2PTCH203_1.1 (1) | 09-OCT 22:11:59 | 1.2PTCH203_1.1 (2) 09-OCT 22:12:11 | 1.2PTCH203_1.2 (1) 09-OCT 22:12:20 | 1.2PTCH203_1.3 (1) 09-OCT 22:12:21 | 1.2PTCH203_1.3 (2) 09-OCT 22:14:14 1.0SECU_1.0 (1) | 09-OCT 22:14:24 | 1.0SECU_1.1 (1) 09-OCT 22:14:34 | 1.0SECU_1.2 (1) 09-OCT 22:14:36 | 1.0SECU_1.2 (2)1.3 (1) 10-OCT 10:34:1211-OCT 06:14:12 | 1.3NEW_BRANCH1.0 (1) | 11-OCT 06:14:16 | 1.3NEW_BRANCH1.0 (2)1.4 (1) 11-OCT 06:14:5611-OCT 06:33:27 | |1.5 (1)11-OCT 06:33:33 |1.6 (1) *12-OCT 16:45:27 |

Note: the asterisk (*) indicates that the object version is INVALID or its PL/SQL code cannot be com-piled!

Next Steps

Things I can envision for next steps with this revolutionary technology framework:

Include a Compare facility that can report the differences between two versions. If I feel really incredibly brave, I will do Merge as well. Actually, I think there must be Java based Compare and perhaps Merge components for Text; we could probably upload them as Java Stored Proce-dures and wrap them within PL/SQL wrappers

Page 10: Extending Toads Code Xpert With the Sonar Source Dashboard

Provide a GUI for this database backend or repository Provide a migration from CVS to my source code control system (or perhaps the other way

round) Ensure that a call to REVERT_TO_VERSION does not itself create a new version upon compi-

lation (as it currently probably does). Provide automatic version labeling: if I check in the next version after 1.4 the tool will call it 1.5

for me. It could even look at the priority level – major, minor, pico, normal – and decide to go from 1.4 to 1.4.1, 1.5, 2.0 or 1.4.0.1.

Add support for other database objects such as Tables and Views

Resources

Download the sources for this article: plsql_Archiver.zip 

Implementation of the PLSQL_ARCHIVER

The Package Body for PLSQL_ARCHIVER is on the second page of this article. You can also down-load it, with all SQL DDL scripts.

The body of this package:

CREATE OR REPLACE package body plsql_archiveras-- forward declarations (implementa-tion is at the end of the package body)function get_code( p_name in varchar2) return clob;function get_errors( p_name in varchar2, p_type in varchar2) return clob;--pro-cedure archive_version( p_object_name in varchar2, p_object_type in varchar2, p_object_owner in varchar2, p_creation_time in date) is l_code clob; l_object_type varchar2(32):= p_object_type; l_archive_rec plsql_archive%rowtype; l_rowid rowid; l_errors clob; procedure debug ( p_txt in varchar2 ) is begin dbms_output.put_line(substr(p_txt,1,255)); end; -- this function tries to locate the specified keyword in the current code block; if the keyword is found, -- the string following the keyword until the first newline character (chr(10)) is returned function get_annotation ( p_keyword in varchar2 -- values include: version, author, branch, comments, priority ) return varchar2 is l_pos number(5); -- position of the keyword, including the @ character l_pos2 number(5); -- position of the first chr(10) following the keyword l_return varchar2(4000); begin debug(p_keyword); l_pos:= instr( l_code, '@'||p_keyword); debug('pos of key-word in code '||l_pos); if l_pos > 0 then l_pos2:= instr( l_code, chr(10), l_pos, 1); -- find the first instance of chr(10), starting from position l_pos in the l_code block debug( 'Position of chr(10) after keyword '||l_pos2); if l_pos2 > 0 then l_return:= ltrim(substr( l_code, l_pos + length(p_keyword)+2, l_pos2 - (l_pos + length(p_keyword)+2))); debug('return value '||l_return); end if; end if; return l_return; end get_annotation;begin l_archive_rec.object_name:= p_object_name; l_archive_rec.ob-ject_type:= p_object_type; l_archive_rec.object_owner:= p_object_owner; l_archive_rec.creation_time:= sysdate; if l_object_type = 'PACKAGE BODY' then l_object_type:= 'PACKAGE'; -- make sure that dbms_metadata does return the pack-age body DBMS_METADATA.SET_TRANSFORM_PARAM ( transform_handle => dbms_meta-data.SESSION_TRANSFORM , name => 'BODY' , value => true , object_type => 'PACKAGE' ); -- make sure that dbms_metadata does not return the package specification as well DBMS_METADATA.SET_TRANSFORM_PARAM ( transform_handle => dbms_metadata.SESSION_TRANSFORM , name => 'SPECIFICATION' , value => false , object_type => 'PACKAGE' ); elsif l_object_type = 'PACKAGE' then -- make sure that dbms_metadata does return the package body DBMS_META-DATA.SET_TRANSFORM_PARAM ( transform_handle => dbms_metadata.SESSION_TRANSFORM , name => 'BODY' , value => false , object_type => 'PACKAGE' ); -- make sure that dbms_metadata does not return the package specification as well DBMS_METADATA.SET_TRANSFORM_PARAM ( transform_handle => dbms_metadata.SESSION_TRANSFORM , name => 'SPECIFICATION' , value => true , object_type => 'PACKAGE' ); end if; be-gin l_code:= dbms_metadata.get_ddl(l_object_type, p_object_name, p_object_owner); exception when others then l_archive_rec.comments:= sqlerrm; l_code:= get_code(p_object_name); end; l_archive_rec.source_size:= length(l_code);

Page 11: Extending Toads Code Xpert With the Sonar Source Dashboard

l_errors:= get_errors( p_name => p_object_name, p_type => p_object_type); l_ar-chive_rec.source:= get_code( p_name => p_object_name); l_archive_rec.errors:= l_errors; begin select status into l_archive_rec.status from all_ob-jects where object_name = p_object_name and object_type = p_object_type ; exception when others then if l_errors is null or length(l_errors) <2 then l_archive_rec.status:= 'VALID'; else l_archive_rec.status:= 'INVALID'; end if; end; l_archive_rec.version_label:= get_annotation('version'); l_archive_rec.comments:= get_annotation('comments'); l_archive_rec.branch:= get_annotation('branch'); l_archive_rec.priority:= get_an-notation('priority'); l_archive_rec.author:= get_annotation('author'); -- find the max seq for objects on the same branch with the same version label -- this as-sumes that either no version labels are used at all or that every object has a ver-sion label -- and it is only changed once in a while when a new meaningful stage has been reached for a particular object select nvl(max(seq),0)+1 into l_ar-chive_rec.seq from plsql_archive where object_name = l_archive_rec.object_name and object_type = l_archive_rec.object_type and object_owner = l_archive_rec.object_owner and nvl(branch,'MAIN') = nvl(l_archive_rec.branch,'MAIN') and nvl(version_label, 'x.y') = nvl(l_archive_rec.version_label, 'x.y') ; insert into plsql_archive values l_archive_rec returning rowid into l_rowid;end archive_version;

procedure revert_to_previous_version -- undo last change; can be called repeatedly( p_object_name in varchar2, p_object_type in varchar2, p_object_owner in varchar2, p_purge_latest in varchar2 default 'Y' ) is l_source clob; procedure debug ( p_txt in varchar2 ) is begin dbms_output.put_line(sub-str(p_txt,1,255)); end; begin select source into l_source from ( select pae.source , row_number() over (partition by object_name, object_type, object_owner order by cre-ation_time desc) rn from plsql_archive pae where object_name = p_object_name and object_type = p_object_type and object_owner = p_object_owner ) all_versions where rn = 2 ; if p_purge_latest = 'Y' then -- delete last version from plsql_archive delete from plsql_archive where rowid = ( select latest_version.rowid from ( select pae.rowid , row_number() over (par-tition by object_name, object_type, object_owner order by creation_time desc) rn from plsql_archive pae where object_name = p_object_name and object_type = p_object_type and object_owner = p_object_owner ) latest_version where rn = 1 ) ; end if; debug(l_source); -- create plsql object based on current source execute immediate 'create or replace '||cast( l_source as varchar2);

end revert_to_previous_version;

procedure revert_to_version( p_object_name in varchar2, p_object_type in var-char2, p_object_owner in varchar2, p_version_label in varchar2, p_branch in varchar2, p_seq in number, p_purge_later in varchar2 default 'N' ) is l_source clob; l_creation_time date; procedure debug ( p_txt in varchar2 ) is begin dbms_output.put_line(substr(p_txt,1,255)); end; begin select source , creation_time into l_source , l_creation_time from plsql_archive where object_name = p_object_name and object_type = p_object_type and ob-ject_owner = p_object_owner and nvl(version_label, 'X') = nvl(p_version_label,nvl(version_label, 'X')) and nvl(branch, nvl(p_branch,'X')) = nvl(p_branch,nvl(branch,'X')) and nvl(seq, 0) = nvl(p_seq,nvl(seq, 0)) ; if p_purge_later = 'Y' then -- delete last version from plsql_archive delete from plsql_archive where object_name = p_object_name and object_type = p_object_type and object_owner = p_object_owner and nvl(branch, nvl(p_branch,'X')) = nvl(p_branch,nvl(branch,'X')) and creation_time > l_creation_time ; end if; debug(l_source); -- create plsql object based on current source execute immediate 'create or replace '||cast( l_source as varchar2); end revert_to_version;

procedure purge( p_object_name in varchar2, p_object_type in varchar2, p_object_owner in varchar2 , p_status in varchar2 default null, p_priority in varchar2 default null, p_from_datetime in date default null, p_to_datetime in date default null, p_branch in varchar2 default null, p_seq_from in number default null, p_seq_to in number default null) isbegin delete from plsql_archive where object_name like p_object_name and ob-ject_type like p_object_type and object_owner like p_object_owner and nvl(branch, nvl(p_branch,'X')) like nvl(p_branch,nvl(branch,'X')) and

Page 12: Extending Toads Code Xpert With the Sonar Source Dashboard

nvl(seq, 0) between nvl(p_seq_from,nvl(seq, 0)) and nvl(p_seq_to,nvl(seq, 0)) and creation_time between nvl(p_from_datetime,creation_time) and nvl(p_to_date-time,creation_time) and nvl(priority, nvl(p_priority,'X')) like nvl(p_priority,nvl(priority,'X')) and nvl(status, nvl(p_status,'X')) like nvl(p_status,nvl(status,'X')) ;end purge;

function version_graph( p_object_name in varchar2, p_object_type in varchar2, p_object_owner in varchar2 , p_show_version_label in varchar2 default 'Y' -- show the version label , p_show_seq in varchar2 default 'N' -- show the seq value for each version (appended to the version label if that is requested too), p_show_datetime in varchar2 default 'N' -- display the timestamp of the cre-ation of each version, p_show_author in varchar2 default 'N' -- display the author of each version, p_show_labels in varchar2 default 'N' -- display the labels or stripes a version is associated with, p_show_status in varchar2 default 'N' -- display the status (VALID or INVALID) of the version, p_show_comments in varchar2 default 'N' -- display the Comments for each version) return string_tableis l_graph string_table:= string_table('Version Graph for '||p_object_type||' '||p_object_name||' (schema '||p_object_owner||')'); l_line var-char2(32000);/*start with the MAIN branch, then the branch who entered the version history most recentlyMAIN SECU PATCH21.0 1.0PTHC2_1.01.1 1.1SECU1.0 1.1SECU1.1 1.0PTHC2_1.11.21.2(2)1.2(3) */ type branch_columns_type is table of string_table index by varchar2(32); type ver-sion_history_type is table of plsql_archive%rowtype; type branch_rec is record (branch varchar2(32), width number(4)); type branches_tbl_type is table of branch_rec index by binary_integer; l_branches_tbl branches_tbl_type; l_branch_columns branch_columns_type; l_branch varchar2(32); l_vh version_history_type; idx number(5); -- index into l_vh collection ctr number(6):=1; l_next_branch varchar2(32); l_vh_done boolean:= false; l_more_on_branch boolean:= false;

function get_vh_tbl ( p_version in plsql_archive%rowtype , p_column_width in number default 40 ) return string_table is l_return string_table:= string_ta-ble(); l_comments_length number(5):= length(p_version.comments);

function ifThen( p_test in boolean , p_value in varchar2 ) return var-char2 is begin if p_test then return p_value; else return ''; end if;

end ifThen; begin if p_show_version_label='Y' or p_show_seq='Y' or p_show_status='Y' then l_return.extend; l_return(l_return.last):= p_version.version_label||' ('||p_version.seq||')'||ifThen(p_show_status='Y' and p_version.status ='INVALID',' *'); end if; if p_show_author='Y' then l_re-turn.extend; l_return(l_return.last):= p_version.author; end if; if p_show_date-time='Y' then l_return.extend; l_return(l_return.last):= to_char(p_version.creation_time,'DD-MON HH24:MI:SS'); end if; if p_show_labels='Y' and length(nvl(p_version.label,''))> 0then l_return.extend; l_return(l_return.last):= 'Labels: '||p_version.label; end if; if p_show_com-ments='Y' and l_comments_length>0 then for i in 1..trunc(l_comments_length/40)+1 loop l_return.extend; l_return(l_return.last):= substr(p_version.comments, 1 + (i-1)*p_column_width, least(p_column_width, l_comments_length - (1 + (i-1)*p_column_width))); end loop; end if; -- p_show_labels l_return.extend; l_return(l_return.last):= ' |'; return l_return; end get_vh_tbl; procedure add ( p_string in varchar2) is begin l_graph.extend; l_graph( l_graph.last) := p_string; end add;begin -- get branches for branch in (select distinct nvl(branch, 'MAIN') branch , first_value(creation_time) over (partition by branch order by creation_time) start_branch , max(length(ver-sion_label)) over (partition by branch) longest_version_label , max(length(to_char(seq))) over (partition by branch) longest_seq from plsql_archive order by start_branch ) loop l_branches_tbl(ctr).branch:= branch.branch ; l_branches_tbl(ctr).width:= greatest( 40, branch.longest_version_label+branch.long-est_seq+6, length(branch.branch)+3) ; l_line:= l_line||rpad(l_branches_tbl(ctr).branch, l_branches_tbl(ctr).width, ' '); ctr:= ctr+1; end loop; -- branch add(l_line); select * bulk collect into l_vh from plsql_archive where object_name = p_object_name and object_type = p_object_type and object_owner = p_object_owner order by creation_time ;

idx:= l_vh.first; l_branch_columns(nvl(l_vh(idx).branch,'MAIN')):= get_vh_tbl(l_vh(idx)); loop l_line:=''; -- loop over branches and for each branch column, if there is a line to write: write it! l_more_on_branch:= false; for i in 1..l_branches_tbl.count loop-- add('outside'||l_branches_tbl(i).branch); if

Page 13: Extending Toads Code Xpert With the Sonar Source Dashboard

l_branch_columns.exists(l_branches_tbl(i).branch) and l_branch_columns(l_branches_tbl(i).branch).count > 0 then -- add('inside'||l_branches_tbl(i).branch||' count '||l_branch_columns(l_branches_tbl(i).branch).count); l_line:=l_line|| rpad(l_branch_columns(l_branches_tbl(i).branch)(l_branch_columns(l_branches_tbl(i).branch).first),l_branches_tbl(i).width,' ');

l_branch_columns(l_branches_tbl(i).branch).delete(l_branch_columns(l_branches_tbl(i).branch).first); else l_line:=l_line|| rpad(' ',l_branches_tbl(i).width,' ');

end if; if l_branch_columns.exists(l_branches_tbl(i).branch) and l_branch_columns(l_branches_tbl(i).branch).count > 0 then l_more_on_branch:= true; end if; --add('end of'); end loop; add(l_line); -- if there are more versions left to process if l_vh.exists(l_vh.next(idx)) then l_next_branch:= nvl(l_vh(l_vh.next(idx)).branch,'MAIN'); -- if there is room for another branch if not l_branch_columns.exists(l_next_branch) or l_branch_columns(l_next_branch).count = 0 or l_branch_columns( l_next_branch) is null then idx:= l_vh.next(idx); l_branch_columns(l_next_branch) := get_vh_tbl(l_vh(idx), 40); -- instead of 40 here we should indicate the width for column associated with branch l_next_branch

l_more_on_branch:= true; end if; end if; ctr:=ctr+1; -- failsafe, to not run infinitely in this loop l_vh_done := l_vh.next(idx) is null; exit when l_vh_done and not l_more_on_branch; exit when ctr>20000; end loop; -- now we have to go through a couple of rounds to have all branch_columns completely wash out their stacks return l_graph;exceptionwhen othersthen add(sqlerrm||'ctr = '||ctr);return l_graph;

end version_graph;--function get_code( p_name in varchar2) return clobis l_code clob:='';begin for src in (SELECT text FROM user_source WHERE name=p_name ORDER BY line) loop l_code:= l_code||src.text; end loop; return l_code;end get_code;--function get_errors( p_name in varchar2, p_type in varchar2) return clobis -- based on the example in chapter 15 of Advanced Oracle PL/SQL - Programming with packages last_line INTEGER := 0; l_errors clob:=''; CURSOR err_cur IS SELECT line, text FROM user_errors WHERE name = UPPER (p_name) AND type = UPPER (p_type) ORDER BY line; /* Local Modules */ procedure add ( p_text in varchar2 ) is begin l_errors:= l_errors||p_text; end add; PROCEDURE err_put_line (prefix_in IN VARCHAR2, text_in IN VARCHAR2)

IS BEGIN add (RTRIM (RPAD (prefix_in, || text_in, CHR(10))); END; -- PROCEDURE display_line (line_in IN INTEGER) IS CURSOR src_cur IS SELECT S.line, S.text FROM user_source S WHERE S.name = UPPER (p_name) AND S.type = UPPER (p_type) AND S.line = line_in; src_rec src_cur%ROWTYPE; BEGIN OPEN src_cur; FETCH src_cur INTO src_rec; IF src_cur%FOUND THEN err_put_line (TO_CHAR (line_in), src_rec.text); END IF; CLOSE src_cur; END; -- PROCEDURE display_err (line_in IN INTEGER) IS CURSOR err_cur IS SELECT line, position, text FROM user_errors WHERE name = UPPER (p_name) AND type = UPPER (p_type) AND line = line_in; err_rec err_cur%ROWTYPE; BEGIN OPEN err_cur; FETCH err_cur INTO err_rec; IF err_cur%FOUND THEN add ('ERR' || LPAD ('*', err_rec.position+5)); err_put_line ('ERR', err_rec.text); END IF; CLOSE err_cur; END; --BEGIN /* Main body of procedure. Loop through all error lines. */ FOR err_rec IN err_cur LOOP /* Show the surrounding code. */ FOR line_ind IN err_rec.line-2 .. err_rec.line+2 LOOP IF last_line < line_ind THEN display_line (line_ind); display_err (line_ind); END IF; last_line := GREATEST (last_line, line_ind); END LOOP; END LOOP; dbms_output.put_line(substr(l_errors,1,255)); return l_er-rors;END get_errors;--end plsql_archiver;/