<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>utPLSQL Archives - Philipp Salvisberg&#039;s Blog</title>
	<atom:link href="https://www.salvis.com/blog/tag/utplsql/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.salvis.com/blog/tag/utplsql/</link>
	<description>Database-centric development</description>
	<lastBuildDate>Wed, 08 Nov 2023 17:13:43 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://www.salvis.com/blog/wp-content/uploads/2014/04/favicon.png</url>
	<title>utPLSQL Archives - Philipp Salvisberg&#039;s Blog</title>
	<link>https://www.salvis.com/blog/tag/utplsql/</link>
	<width>32</width>
	<height>32</height>
</image> 
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com"/>
<atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com"/>
<atom:link rel="hub" href="https://websubhub.com/hub"/>
<atom:link rel="self" href="https://www.salvis.com/blog/tag/utplsql/feed/"/>
	<item>
		<title>IslandSQL Episode 1:  Select Statement</title>
		<link>https://www.salvis.com/blog/2023/02/04/islandsql-episode-1-select-statement/</link>
					<comments>https://www.salvis.com/blog/2023/02/04/islandsql-episode-1-select-statement/#comments</comments>
		
		<dc:creator><![CDATA[Philipp Salvisberg]]></dc:creator>
		<pubDate>Sat, 04 Feb 2023 12:00:04 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[ANTLR]]></category>
		<category><![CDATA[Code Analysis]]></category>
		<category><![CDATA[IslandSQL]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[utPLSQL]]></category>
		<category><![CDATA[VSCode]]></category>
		<guid isPermaLink="false">https://www.salvis.com/blog/?p=12006</guid>

					<description><![CDATA[<p>Introduction An island grammar focuses only on a small part of a grammar. The island represents the small, interesting part and the sea the rest. In this blog post, I explain the components of an island grammar for SQL scripts named IslandSQL. In the first iteration, we focus on the select&#160;statement. Everything<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2023/02/04/islandsql-episode-1-select-statement/">IslandSQL Episode 1:  Select Statement</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">An island grammar focuses only on a small part of a grammar. The island represents the small, interesting part and the sea the rest. In this blog post, I explain the components of an island grammar for SQL scripts named IslandSQL. In the first iteration, we focus on the <code>select</code>&nbsp;statement. Everything else is not of interest for the time being.</p>



<h2 class="wp-block-heading">Use Case</h2>



<p class="wp-block-paragraph">Let&#8217;s assume we want to write an extension for Visual Studio Code that can find text in <code>select</code> statements within SQL files of a workspace. So what is the difference to VSCode&#8217;s integrated text search? Well, the text search does not know what a <code>select</code> statement is. It finds occurrences of the search text in all kinds of places. In fact, identifying a <code>select</code> statement is not as easy as one might think.</p>



<p class="wp-block-paragraph">Let&#8217;s look at an example.</p>



<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">demo1.sql: No select statements</span><span role="button" tabindex="0" data-code="/* select * from t1; */
-- select * from t2;
remark select * from t3;
prompt select * from t4;
begin
    sys.dbms_output.put_line('irrelevant: select * from t5;');
end;
/
create or replace procedure p is
begin
   $if false $then irrelevant: select * from t6; $end
   null;
end;
/" 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: #6A9955">/* select * from t1; */</span></span>
<span class="line"><span style="color: #6A9955">-- select * from t2;</span></span>
<span class="line"><span style="color: #D4D4D4">remark </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> t3;</span></span>
<span class="line"><span style="color: #D4D4D4">prompt </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> t4;</span></span>
<span class="line"><span style="color: #569CD6">begin</span></span>
<span class="line"><span style="color: #D4D4D4">    sys.dbms_output.put_line(</span><span style="color: #CE9178">&#39;irrelevant: select * from t5;&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #569CD6">end</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #569CD6">create</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">replace</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">procedure</span><span style="color: #D4D4D4"> p </span><span style="color: #569CD6">is</span></span>
<span class="line"><span style="color: #569CD6">begin</span></span>
<span class="line"><span style="color: #D4D4D4">   $</span><span style="color: #569CD6">if</span><span style="color: #D4D4D4"> false $</span><span style="color: #569CD6">then</span><span style="color: #D4D4D4"> irrelevant: </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> t6; $</span><span style="color: #569CD6">end</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">end</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span></code></pre></div>



<p class="wp-block-paragraph">This SQL script does not contain relevant <code>select</code> statements. A <code>select</code> statement within a comment is hardly relevant. The same is true for <code>select</code> statements in remark and prompt commands. And I consider <code>select</code> statements within string literals and conditional compilation text also as irrelevant, at least in this example.</p>



<p class="wp-block-paragraph">So let&#8217;s look at another example:</p>



<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">demo2.sql: Variants of select statements</span><span role="button" tabindex="0" data-code="-- simple
select * from dept;
-- subquery_factoring_clause
with
   d as (
      select * from dept
   )
select * from d;
-- plsql_declarations
with
   function e_count (in_deptno in dept.deptno%type) return integer is
      l_count integer;
   begin
      select count(*)
        into l_count
        from emp;
      return l_count;
   end e_count;
select deptno, e_count(deptno)
  from dept
/
-- unterminated
select * from dept" 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: #6A9955">-- simple</span></span>
<span class="line"><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> dept;</span></span>
<span class="line"><span style="color: #6A9955">-- subquery_factoring_clause</span></span>
<span class="line"><span style="color: #569CD6">with</span></span>
<span class="line"><span style="color: #D4D4D4">   d </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> dept</span></span>
<span class="line"><span style="color: #D4D4D4">   )</span></span>
<span class="line"><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> d;</span></span>
<span class="line"><span style="color: #6A9955">-- plsql_declarations</span></span>
<span class="line"><span style="color: #569CD6">with</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> e_count (in_deptno </span><span style="color: #569CD6">in</span><span style="color: #D4D4D4"> dept.deptno%</span><span style="color: #569CD6">type</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">integer</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">is</span></span>
<span class="line"><span style="color: #D4D4D4">      l_count </span><span style="color: #569CD6">integer</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">begin</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(*)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">into</span><span style="color: #D4D4D4"> l_count</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> emp;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">return</span><span style="color: #D4D4D4"> l_count;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">end</span><span style="color: #D4D4D4"> e_count;</span></span>
<span class="line"><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> deptno, e_count(deptno)</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> dept</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #6A9955">-- unterminated</span></span>
<span class="line"><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> dept</span></span></code></pre></div>



<p class="wp-block-paragraph">This example script contains four <code>select</code> statements. As you can see, a <code>select</code> statement does not necessarily need to start with the <code>select</code> keyword. Furthermore a <code>select</code> statement can end on semicolon or slash or EOF (end-of-file). In fact, when using <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6__GUID-28DA0E1D-87BF-462E-BCB8-8F77921022F9">plsql_declarations</a> the statement must end on a slash (or EOF).</p>



<p class="wp-block-paragraph">Here&#8217;s a screenshot of the VSCode extension after searching for the regular expression <code>.+</code> in the demo workspace, highlighting the third search result.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2.png"><img fetchpriority="high" decoding="async" width="1752" height="1108" src="https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2.png" alt="IslandSQL VSCode extension: search result" class="wp-image-12010" srcset="https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2.png 1752w, https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2-300x190.png 300w, https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2-1024x648.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2-768x486.png 768w, https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2-1536x971.png 1536w, https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2-231x146.png 231w, https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2-50x32.png 50w, https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2-119x75.png 119w, https://www.salvis.com/blog/wp-content/uploads/2023/02/islandsql-vscode-demo2-1x1.png 1w" sizes="(max-width:767px) 480px, (max-width:1752px) 100vw, 1752px" /></a></figure>



<h2 class="wp-block-heading">Lexer Grammar</h2>



<p class="wp-block-paragraph">The responsibility of the lexer is to convert a stream of characters to a stream of tokens. We use <a href="https://www.antlr.org/">ANTLR 4</a> to generate our lexer with Java as the target language.</p>



<p class="wp-block-paragraph">Here&#8217;s the grammar definition for our lexer.</p>



<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">IslandSqlLexer.g4: based on https://github.com/IslandSQL/IslandSQL/tree/v0.1.0/</span><span role="button" tabindex="0" data-code="lexer grammar IslandSqlLexer;

options {
    superClass=IslandSqlLexerBase;
    caseInsensitive = true;
}

/*----------------------------------------------------------------------------*/
// Comments and alike to be ignored
/*----------------------------------------------------------------------------*/

ML_COMMENT: '/*' .*? '*/' -&gt; channel(HIDDEN);
SL_COMMENT: '--' .*? (EOF|SINGLE_NL) -&gt; channel(HIDDEN);
REMARK_COMMAND:
    {isBeginOfCommand()}? 'rem' ('a' ('r' 'k'?)?)?
        (WS SQLPLUS_TEXT*)? SQLPLUS_END -&gt; channel(HIDDEN)
;
PROMPT_COMMAND:
    {isBeginOfCommand()}? 'pro' ('m' ('p' 't'?)?)?
       (WS SQLPLUS_TEXT*)? SQLPLUS_END -&gt; channel(HIDDEN)
;
STRING:
    'n'?
    (
          (['] .*? ['])+
        | ('q' ['] '[' .*? ']' ['])
        | ('q' ['] '(' .*? ')' ['])
        | ('q' ['] '{' .*? '}' ['])
        | ('q' ['] '<' .*? '&gt;' ['])
        | ('q' ['] . {saveQuoteDelimiter1()}? .+? . ['] {checkQuoteDelimiter2()}?)
    ) -&gt; channel(HIDDEN)
;
CONDITIONAL_COMPILATION_DIRECTIVE: '$if' .*? '$end' -&gt; channel(HIDDEN);

/*----------------------------------------------------------------------------*/
// Islands of interest on DEFAULT_CHANNEL
/*----------------------------------------------------------------------------*/

PLSQL_DECLARATION:
    {isBeginOfStatement()}? 'with' WS
        ('function'|'procedure') SQL_TEXT*?  PLSQL_DECLARATION_END
;
SELECT:
    {isBeginOfStatement()}? ('with'|('(' WS?)* 'select') SQL_TEXT*? SQL_END
;

/*----------------------------------------------------------------------------*/
// Whitespace
/*----------------------------------------------------------------------------*/

WS: [ \t\r\n]+ -&gt; channel(HIDDEN);

/*----------------------------------------------------------------------------*/
// Any other token
/*----------------------------------------------------------------------------*/

ANY_OTHER: . -&gt; channel(HIDDEN);

/*----------------------------------------------------------------------------*/
// Fragments to name expressions and reduce code duplication
/*----------------------------------------------------------------------------*/

fragment SINGLE_NL: '\r'? '\n';
fragment CONTINUE_LINE: '-' [ \t]* SINGLE_NL;
fragment SQLPLUS_TEXT: (~[\r\n]|CONTINUE_LINE);
fragment SQL_TEXT: (ML_COMMENT|SL_COMMENT|STRING|.);
fragment SLASH_END: SINGLE_NL WS* '/' [ \t]* (EOF|SINGLE_NL);
fragment PLSQL_DECLARATION_END: ';'? [ \t]* (EOF|SLASH_END);
fragment SQL_END:
      EOF
    | (';' [ \t]* SINGLE_NL?)
    | SLASH_END
;
fragment SQLPLUS_END: EOF|SINGLE_NL;" 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: #9CDCFE">lexer</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">grammar</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">IslandSqlLexer</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">superClass</span><span style="color: #D4D4D4">=</span><span style="color: #9CDCFE">IslandSqlLexerBase</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">caseInsensitive</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"><span style="color: #6A9955">// Comments and alike to be ignored</span></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C8C8C8">ML_COMMENT</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&#39;/*&#39;</span><span style="color: #D4D4D4"> .*? </span><span style="color: #CE9178">&#39;*/&#39;</span><span style="color: #D4D4D4"> -&gt; </span><span style="color: #DCDCAA">channel</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HIDDEN</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #4FC1FF">SL_COMMENT</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&#39;--&#39;</span><span style="color: #D4D4D4"> .*? (</span><span style="color: #4FC1FF">EOF</span><span style="color: #D4D4D4">|</span><span style="color: #4FC1FF">SINGLE_NL</span><span style="color: #D4D4D4">) -&gt; </span><span style="color: #DCDCAA">channel</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HIDDEN</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #4FC1FF">REMARK_COMMAND</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #DCDCAA">isBeginOfCommand</span><span style="color: #D4D4D4">()}? </span><span style="color: #CE9178">&#39;rem&#39;</span><span style="color: #D4D4D4"> (</span><span style="color: #CE9178">&#39;a&#39;</span><span style="color: #D4D4D4"> (</span><span style="color: #CE9178">&#39;r&#39;</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&#39;k&#39;</span><span style="color: #D4D4D4">?)?)?</span></span>
<span class="line"><span style="color: #D4D4D4">        (</span><span style="color: #4FC1FF">WS</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">SQLPLUS_TEXT</span><span style="color: #D4D4D4">*)? </span><span style="color: #4FC1FF">SQLPLUS_END</span><span style="color: #D4D4D4"> -&gt; </span><span style="color: #DCDCAA">channel</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HIDDEN</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #4FC1FF">PROMPT_COMMAND</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #DCDCAA">isBeginOfCommand</span><span style="color: #D4D4D4">()}? </span><span style="color: #CE9178">&#39;pro&#39;</span><span style="color: #D4D4D4"> (</span><span style="color: #CE9178">&#39;m&#39;</span><span style="color: #D4D4D4"> (</span><span style="color: #CE9178">&#39;p&#39;</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&#39;t&#39;</span><span style="color: #D4D4D4">?)?)?</span></span>
<span class="line"><span style="color: #D4D4D4">       (</span><span style="color: #4FC1FF">WS</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">SQLPLUS_TEXT</span><span style="color: #D4D4D4">*)? </span><span style="color: #4FC1FF">SQLPLUS_END</span><span style="color: #D4D4D4"> -&gt; </span><span style="color: #DCDCAA">channel</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HIDDEN</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #4FC1FF">STRING</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&#39;n&#39;</span><span style="color: #D4D4D4">?</span></span>
<span class="line"><span style="color: #D4D4D4">    (</span></span>
<span class="line"><span style="color: #D4D4D4">          ([</span><span style="color: #CE9178">&#39;] .*? [&#39;</span><span style="color: #D4D4D4">])+</span></span>
<span class="line"><span style="color: #D4D4D4">        | (</span><span style="color: #CE9178">&#39;q&#39;</span><span style="color: #D4D4D4"> [</span><span style="color: #CE9178">&#39;] &#39;</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&#39; .*? &#39;</span><span style="color: #D4D4D4">]</span><span style="color: #CE9178">&#39; [&#39;</span><span style="color: #D4D4D4">])</span></span>
<span class="line"><span style="color: #D4D4D4">        | (</span><span style="color: #CE9178">&#39;q&#39;</span><span style="color: #D4D4D4"> [</span><span style="color: #CE9178">&#39;] &#39;</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39; .*? &#39;</span><span style="color: #D4D4D4">)</span><span style="color: #CE9178">&#39; [&#39;</span><span style="color: #D4D4D4">])</span></span>
<span class="line"><span style="color: #D4D4D4">        | (</span><span style="color: #CE9178">&#39;q&#39;</span><span style="color: #D4D4D4"> [</span><span style="color: #CE9178">&#39;] &#39;</span><span style="color: #D4D4D4">{</span><span style="color: #CE9178">&#39; .*? &#39;</span><span style="color: #D4D4D4">}</span><span style="color: #CE9178">&#39; [&#39;</span><span style="color: #D4D4D4">])</span></span>
<span class="line"><span style="color: #D4D4D4">        | (</span><span style="color: #CE9178">&#39;q&#39;</span><span style="color: #D4D4D4"> [</span><span style="color: #CE9178">&#39;] &#39;</span><span style="color: #D4D4D4">&lt;</span><span style="color: #CE9178">&#39; .*? &#39;</span><span style="color: #D4D4D4">&gt;</span><span style="color: #CE9178">&#39; [&#39;</span><span style="color: #D4D4D4">])</span></span>
<span class="line"><span style="color: #D4D4D4">        | (</span><span style="color: #CE9178">&#39;q&#39;</span><span style="color: #D4D4D4"> [</span><span style="color: #CE9178">&#39;] . {saveQuoteDelimiter1()}? .+? . [&#39;</span><span style="color: #D4D4D4">] {</span><span style="color: #DCDCAA">checkQuoteDelimiter2</span><span style="color: #D4D4D4">()}?)</span></span>
<span class="line"><span style="color: #D4D4D4">    ) -&gt; </span><span style="color: #DCDCAA">channel</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HIDDEN</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #4FC1FF">CONDITIONAL_COMPILATION_DIRECTIVE</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&#39;$if&#39;</span><span style="color: #D4D4D4"> .*? </span><span style="color: #CE9178">&#39;$end&#39;</span><span style="color: #D4D4D4"> -&gt; </span><span style="color: #DCDCAA">channel</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HIDDEN</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"><span style="color: #6A9955">// Islands of interest on DEFAULT_CHANNEL</span></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #4FC1FF">PLSQL_DECLARATION</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #DCDCAA">isBeginOfStatement</span><span style="color: #D4D4D4">()}? </span><span style="color: #CE9178">&#39;with&#39;</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">WS</span></span>
<span class="line"><span style="color: #D4D4D4">        (</span><span style="color: #CE9178">&#39;function&#39;</span><span style="color: #D4D4D4">|</span><span style="color: #CE9178">&#39;procedure&#39;</span><span style="color: #D4D4D4">) </span><span style="color: #4FC1FF">SQL_TEXT</span><span style="color: #D4D4D4">*?  </span><span style="color: #4FC1FF">PLSQL_DECLARATION_END</span></span>
<span class="line"><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #4FC1FF">SELECT</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span><span style="color: #DCDCAA">isBeginOfStatement</span><span style="color: #D4D4D4">()}? (</span><span style="color: #CE9178">&#39;with&#39;</span><span style="color: #D4D4D4">|(</span><span style="color: #CE9178">&#39;(&#39;</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">WS</span><span style="color: #D4D4D4">?)* </span><span style="color: #CE9178">&#39;select&#39;</span><span style="color: #D4D4D4">) </span><span style="color: #4FC1FF">SQL_TEXT</span><span style="color: #D4D4D4">*? </span><span style="color: #4FC1FF">SQL_END</span></span>
<span class="line"><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"><span style="color: #6A9955">// Whitespace</span></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #4FC1FF">WS</span><span style="color: #D4D4D4">: [ \</span><span style="color: #9CDCFE">t</span><span style="color: #D4D4D4">\</span><span style="color: #9CDCFE">r</span><span style="color: #D4D4D4">\</span><span style="color: #9CDCFE">n</span><span style="color: #D4D4D4">]+ -&gt; </span><span style="color: #DCDCAA">channel</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HIDDEN</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"><span style="color: #6A9955">// Any other token</span></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #4FC1FF">ANY_OTHER</span><span style="color: #D4D4D4">: . -&gt; </span><span style="color: #DCDCAA">channel</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HIDDEN</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"><span style="color: #6A9955">// Fragments to name expressions and reduce code duplication</span></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">fragment</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">SINGLE_NL</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&#39;</span><span style="color: #D7BA7D">\r</span><span style="color: #CE9178">&#39;</span><span style="color: #D4D4D4">? </span><span style="color: #CE9178">&#39;</span><span style="color: #D7BA7D">\n</span><span style="color: #CE9178">&#39;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #9CDCFE">fragment</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">CONTINUE_LINE</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&#39;-&#39;</span><span style="color: #D4D4D4"> [ \</span><span style="color: #9CDCFE">t</span><span style="color: #D4D4D4">]* </span><span style="color: #4FC1FF">SINGLE_NL</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #9CDCFE">fragment</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">SQLPLUS_TEXT</span><span style="color: #D4D4D4">: (~[\</span><span style="color: #9CDCFE">r</span><span style="color: #D4D4D4">\</span><span style="color: #9CDCFE">n</span><span style="color: #D4D4D4">]|</span><span style="color: #4FC1FF">CONTINUE_LINE</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">fragment</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">SQL_TEXT</span><span style="color: #D4D4D4">: (</span><span style="color: #4FC1FF">ML_COMMENT</span><span style="color: #D4D4D4">|</span><span style="color: #4FC1FF">SL_COMMENT</span><span style="color: #D4D4D4">|</span><span style="color: #4FC1FF">STRING</span><span style="color: #D4D4D4">|.);</span></span>
<span class="line"><span style="color: #9CDCFE">fragment</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">SLASH_END</span><span style="color: #D4D4D4">: </span><span style="color: #4FC1FF">SINGLE_NL</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">WS</span><span style="color: #D4D4D4">* </span><span style="color: #CE9178">&#39;/&#39;</span><span style="color: #D4D4D4"> [ \</span><span style="color: #9CDCFE">t</span><span style="color: #D4D4D4">]* (</span><span style="color: #4FC1FF">EOF</span><span style="color: #D4D4D4">|</span><span style="color: #4FC1FF">SINGLE_NL</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">fragment</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">PLSQL_DECLARATION_END</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&#39;;&#39;</span><span style="color: #D4D4D4">? [ \</span><span style="color: #9CDCFE">t</span><span style="color: #D4D4D4">]* (</span><span style="color: #4FC1FF">EOF</span><span style="color: #D4D4D4">|</span><span style="color: #4FC1FF">SLASH_END</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">fragment</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">SQL_END</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #4FC1FF">EOF</span></span>
<span class="line"><span style="color: #D4D4D4">    | (</span><span style="color: #CE9178">&#39;;&#39;</span><span style="color: #D4D4D4"> [ \</span><span style="color: #9CDCFE">t</span><span style="color: #D4D4D4">]* </span><span style="color: #4FC1FF">SINGLE_NL</span><span style="color: #D4D4D4">?)</span></span>
<span class="line"><span style="color: #D4D4D4">    | </span><span style="color: #4FC1FF">SLASH_END</span></span>
<span class="line"><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #9CDCFE">fragment</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">SQLPLUS_END</span><span style="color: #D4D4D4">: </span><span style="color: #4FC1FF">EOF</span><span style="color: #D4D4D4">|</span><span style="color: #4FC1FF">SINGLE_NL</span><span style="color: #D4D4D4">;</span></span></code></pre></div>



<h3 class="wp-block-heading">Lexer Options</h3>



<p class="wp-block-paragraph">On lines 3-6 we define the grammar <a href="https://github.com/antlr/antlr4/blob/master/doc/options.md">options</a>.</p>



<p class="wp-block-paragraph">The first option defines the superclass that the generated lexer class should extend from. We use this class to define semantic predicates that can be used in the lexer grammar. Semantic predicates are very powerful. However, they bind a grammar to a target language. Of course, you can implement a superclass in different target languages. But would you want to do that for every supported <a href="https://github.com/antlr/antlr4/blob/master/doc/targets.md">target language</a>?</p>



<p class="wp-block-paragraph">The second option defines the grammar as case-insensitive. This simplifies the grammar. We can simply write <code>select</code> instead of <code>S E L E C T</code> where every letter is a fragment (e.g.&nbsp;<code>fragment S: [sS];</code>).</p>



<h3 class="wp-block-heading">Channels</h3>



<p class="wp-block-paragraph">A token can be either ignored (skipped) or placed on a channel. ANTLR provides by default the following two channels:</p>



<ul class="wp-block-list">
<li><code>DEFAULT_CHANNEL</code>: for visible tokens that are relevant to the parser grammar</li>



<li><code>HIDDEN</code>: for tokens that are not relevant to the parser grammar</li>
</ul>



<p class="wp-block-paragraph">We do not skip tokens in this grammar. This has the advantage that we can access hidden tokens when we need to. For example, for accessing hints or for lossless serialisation of chosen parts.</p>



<h3 class="wp-block-heading">Comments and Alike</h3>



<p class="wp-block-paragraph">On lines 8-33, we define hidden tokens using <a href="https://github.com/antlr/antlr4/blob/master/doc/lexer-rules.md">lexer rules</a>.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">The notation for the token definitions should be familiar to those with regular expression experience.<br />&#8212; Terence Parr, The Definitive ANTLR 4 Reference, 2nd edition, page 36</p>
</blockquote>



<p class="wp-block-paragraph">The tokens defined in this section are similar to comments and therefore should be ignored and placed on the hidden channel.</p>



<p class="wp-block-paragraph">The order of the rules is important in case of conflicting definitions. The first rule wins.&nbsp;<code>ML_COMMENT</code> defines a multiline comment starting with <code>/*</code>&nbsp; and ending with <code>*/</code>. The rules defined afterwards&nbsp;cannot define tokens that are a subset of <code>ML_COMMENT</code>. For example,&nbsp; a <code>select</code> statement within a <code>ML_COMMENT</code> is not visible for subsequent rules. This is the reason for the rules in this section. We want to hide <code>select</code> statements within comments and alike.</p>



<p class="wp-block-paragraph">However, it is possible to define a token that contains a <code>ML_COMMENT</code>, e.g. in a <code>select</code> statement.</p>



<h3 class="wp-block-heading">Islands of Interest</h3>



<p class="wp-block-paragraph">You find the islands of interest on lines 35-45. The rule <code>PLSQL_DECLARATION</code> covers the <code>select</code> statement with a <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6__GUID-28DA0E1D-87BF-462E-BCB8-8F77921022F9">plsql_declarations</a> clause. And the rule <code>SELECT</code> covers the <code>select</code> statement without a <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6__GUID-28DA0E1D-87BF-462E-BCB8-8F77921022F9">plsql_declarations</a> clause. Identifying the end of the <code>select</code> statement is a bit tricky.</p>



<p class="wp-block-paragraph">This definition works in many cases, but it will falsely match subqueries in other statements such as <code>insert</code>, <code>update</code>, <code>delete</code>, <code>merge</code>, etc. In such cases, everything up to the next semicolon is considered part of the <code>select</code> statement. We will address this flaw in a future version of the grammar.</p>



<p class="wp-block-paragraph">How do we ignore semicolons in comments and strings? By using the fragment <code>SQL_TEXT</code>. A <code>SQL_TEXT</code> is either a <code>ML_COMMENT</code>, a <code>SL_COMMENT</code>, a <code>STRING</code> or any other character (<code>.</code>). Again, the order is important. The lexer considers comments and strings as single tokens. A semicolon in comments or strings is not visible. As a result, a semicolon in comments or strings will not be interpreted as the end of a <code>select</code> statement.</p>



<p class="wp-block-paragraph">Did you wonder why I use ANTLR instead of regular expressions? Well, it&#8217;s simply not possible to write a regular expression that can match a complete <code>select</code> statement ending on a semicolon while ignoring the semicolons in comments and strings. Simply put, ANTLR is more powerful.</p>



<p class="wp-block-paragraph">These two rules produce a single token for the whole <code>select</code> statement. This is simple and enough for our current use case. However, in the coming versions of the grammar, we will rewrite this section to parse a <code>select</code> statement completely and address the issues with subqueries in other statements.</p>



<h3 class="wp-block-heading">Whitespace</h3>



<p class="wp-block-paragraph">The <code>WS</code> rule on line 51 defines whitespace characters. They are not relevant for the parser, hence we put them on the <code>HIDDEN</code> channel.</p>



<h3 class="wp-block-heading">Other Tokens</h3>



<p class="wp-block-paragraph">The <code>ANY_OTHER</code> rule on line 57 covers any other character. They are not relevant for the parser and we put them also on the <code>HIDDEN</code> channel.</p>



<h3 class="wp-block-heading">Fragments</h3>



<p class="wp-block-paragraph">And finally, on lines 59-74 we have fragments. A fragment allows one to name an expression and use the fragment name instead of the expression in other fragments or rules. This makes the grammar more readable without introducing additional token types.</p>



<h2 class="wp-block-heading">Parser Grammar</h2>



<p class="wp-block-paragraph">We use the output of the lexer &#8211; the token stream &#8211; in the parser. By default, only the tokens on the <code>DEFAULT_CHANNEL</code> are visible in the parser grammar. This makes the grammar quite simple.</p>



<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">IslandSqlParser.g4: based on https://github.com/IslandSQL/IslandSQL/tree/v0.1.0/</span><span role="button" tabindex="0" data-code="parser grammar IslandSqlParser;

options {
    tokenVocab=IslandSqlLexer;
}

/*----------------------------------------------------------------------------*/
// Start rule
/*----------------------------------------------------------------------------*/

file: selectStatement* EOF;

/*----------------------------------------------------------------------------*/
// Rules for reduced SQL grammar (islands of interest)
/*----------------------------------------------------------------------------*/

selectStatement: PLSQL_DECLARATION | SELECT;" 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: #9CDCFE">parser</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">grammar</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">IslandSqlParser</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">tokenVocab</span><span style="color: #D4D4D4">=</span><span style="color: #9CDCFE">IslandSqlLexer</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"><span style="color: #6A9955">// Start rule</span></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C8C8C8">file</span><span style="color: #D4D4D4">: </span><span style="color: #9CDCFE">selectStatement</span><span style="color: #D4D4D4">* </span><span style="color: #4FC1FF">EOF</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"><span style="color: #6A9955">// Rules for reduced SQL grammar (islands of interest)</span></span>
<span class="line"><span style="color: #6A9955">/*----------------------------------------------------------------------------*/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C8C8C8">selectStatement</span><span style="color: #D4D4D4">: </span><span style="color: #4FC1FF">PLSQL_DECLARATION</span><span style="color: #D4D4D4"> | </span><span style="color: #4FC1FF">SELECT</span><span style="color: #D4D4D4">;</span></span></code></pre></div>



<h3 class="wp-block-heading">Parser Options</h3>



<p class="wp-block-paragraph">On line 4 we include the token vocabulary based on the lexer grammar. The vocabulary defines integer values for each token type, e.g. <code>PLSQL_DECLARATION=7</code> or <code>SELECT=8</code>. The token stream uses these integer values to identify token types. Integers are shorter than their string counterparts and therefore use fewer resources.</p>



<h3 class="wp-block-heading">Start Rule</h3>



<p class="wp-block-paragraph">You find the <a href="https://github.com/antlr/antlr4/blob/master/doc/parser-rules.md#start-rules-and-eof">start rule</a> <code>file</code> on line 11. This is the entry point for the parser. The root node of the parse tree.</p>



<p class="wp-block-paragraph">A <code>file</code>&nbsp;may contain an unbounded number of <code>selectStatement</code> rules. And a file ends on the pseudo token<code>EOF</code> (end-of-file). This way we ensure that the parser reads the complete file.</p>



<h3 class="wp-block-heading">Select Statement</h3>



<p class="wp-block-paragraph">On line 17 the <code>selectStatement</code>&nbsp;is defined either as a <code>PLSQL_DECLARATION</code> or <code>SELECT</code> token.</p>



<p class="wp-block-paragraph">That&#8217;s it. All other tokens are hidden and invisible to the parser.</p>



<p class="wp-block-paragraph">Furthermore, it&#8217;s not possible to produce a parse error with this grammar. Everything that is not a <code>selectStatement</code>&nbsp;is on the hidden channel and irrelevant.</p>



<h2 class="wp-block-heading">Interpreter</h2>



<p class="wp-block-paragraph">ANTLR is a parser generator. It takes the lexer and parser grammar as input and produces a lexer and parser in a chosen target language as output.</p>



<p class="wp-block-paragraph">However, there are also ANTLR interpreters. As a plugin in IDEs such as IntelliJ, as a standalone application or as a web application. After pasting the grammers into the UI you can play with it. Here&#8217;s a screenshot of the web variant of <a href="http://lab.antlr.org/">ANTLR lab</a>.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab.png"><img decoding="async" width="2334" height="1358" src="https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab.png" alt="" class="wp-image-12047" srcset="https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab.png 2334w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-300x175.png 300w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-1024x596.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-768x447.png 768w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-1536x894.png 1536w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-2048x1192.png 2048w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-251x146.png 251w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-50x29.png 50w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-129x75.png 129w, https://www.salvis.com/blog/wp-content/uploads/2023/02/antlr_lab-1x1.png 1w" sizes="(max-width:767px) 480px, (max-width:2334px) 100vw, 2334px" /></a></figure>



<p class="wp-block-paragraph">What about semantic predicates? &#8211; The interpreter acts as the call returned true. As a result, for example, <code>xselect * from dual;</code> is falsely recognized as a <code>select</code> statement. Nevertheless, this is usually good enough to explore most parts of an ANTLR grammar.</p>



<h2 class="wp-block-heading">IslandSQL on GitHub</h2>



<p class="wp-block-paragraph">The source code of the IslandSQL parser is available on <a href="https://github.com/IslandSQL/IslandSQL">GitHub</a>, licensed under the Apache License, Version 2.0.</p>



<p class="wp-block-paragraph">However, you will not find any test cases in this repo. I have written 92 test cases for the initial version 0.1.0. They are stored in a private repository. I do not plan to release them. It&#8217;s a way to make an unfriendly fork harder. Not right now. But once the grammar has evolved to cover a significant subset of SQL of the relevant database management systems, the situation might be different.</p>



<h2 class="wp-block-heading">IslandSQL on Maven Central</h2>



<p class="wp-block-paragraph">The IslandSQL parser is available on <a href="https://central.sonatype.com/artifact/ch.islandsql/islandsql/0.1.0">Maven Central</a>. This makes integration into your preferred build system easy.</p>



<p class="wp-block-paragraph">And using the parser is also easy. Here is an example:</p>



<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">Demo.java</span><span role="button" tabindex="0" data-code="import ch.islandsql.grammar.IslandSqlDocument;
import ch.islandsql.grammar.IslandSqlParser;

class Demo {
    public static void main(String[] args) {
        var doc = IslandSqlDocument.parse(&quot;&quot;&quot;
                /* select * from t1; */
                -- select * from t2;
                rem select * from t3;
                prompt select * from t4;
                -- simple
                select * from dept;
                -- subquery_factoring_clause
                with d as (select * from dept) select * from d;
                -- other statements
                delete from t5;
                update t6 set c1 = null;
                commit;
                &quot;&quot;&quot;);
        System.out.println(doc.getFile().getText());
        System.out.println(&quot;----------&quot;);
        doc.getFile().children.forEach(child -&gt; System.out.print(child.getText()));
        System.out.println(&quot;\n----------&quot;);
        doc.getAllContentsOfType(IslandSqlParser.SelectStatementContext.class)
                .forEach(stmt -&gt; System.out.print(stmt.getText()));
    }
}" 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">import</span><span style="color: #D4D4D4"> ch.islandsql.grammar.IslandSqlDocument;</span></span>
<span class="line"><span style="color: #569CD6">import</span><span style="color: #D4D4D4"> ch.islandsql.grammar.IslandSqlParser;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Demo</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">main</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">String</span><span style="color: #D4D4D4">[] </span><span style="color: #9CDCFE">args</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">doc</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">IslandSqlDocument</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">parse</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #CE9178">                /* select * from t1; */</span></span>
<span class="line"><span style="color: #CE9178">                -- select * from t2;</span></span>
<span class="line"><span style="color: #CE9178">                rem select * from t3;</span></span>
<span class="line"><span style="color: #CE9178">                prompt select * from t4;</span></span>
<span class="line"><span style="color: #CE9178">                -- simple</span></span>
<span class="line"><span style="color: #CE9178">                select * from dept;</span></span>
<span class="line"><span style="color: #CE9178">                -- subquery_factoring_clause</span></span>
<span class="line"><span style="color: #CE9178">                with d as (select * from dept) select * from d;</span></span>
<span class="line"><span style="color: #CE9178">                -- other statements</span></span>
<span class="line"><span style="color: #CE9178">                delete from t5;</span></span>
<span class="line"><span style="color: #CE9178">                update t6 set c1 = null;</span></span>
<span class="line"><span style="color: #CE9178">                commit;</span></span>
<span class="line"><span style="color: #CE9178">                &quot;&quot;&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">out</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">println</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">doc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getFile</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">());</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">out</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">println</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;----------&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">doc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getFile</span><span style="color: #D4D4D4">().</span><span style="color: #9CDCFE">children</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">forEach</span><span style="color: #D4D4D4">(child </span><span style="color: #569CD6">-&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">out</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">child</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">()));</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">out</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">println</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;</span><span style="color: #D7BA7D">\n</span><span style="color: #CE9178">----------&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">doc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getAllContentsOfType</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">IslandSqlParser</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">SelectStatementContext</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                .</span><span style="color: #DCDCAA">forEach</span><span style="color: #D4D4D4">(stmt </span><span style="color: #569CD6">-&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">out</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">stmt</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">()));</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">The output of the main method is:</p>



<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">Output of Demo.java</span><span role="button" tabindex="0" data-code="select * from dept;
with d as (select * from dept) select * from d;
<EOF&gt;
----------
select * from dept;
with d as (select * from dept) select * from d;
<EOF&gt;
----------
select * from dept;
with d as (select * from dept) select * from d;" 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: #D4D4D4">select * from dept;</span></span>
<span class="line"><span style="color: #D4D4D4">with d as (select * from dept) select * from d;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;EOF&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">----------</span></span>
<span class="line"><span style="color: #D4D4D4">select * from dept;</span></span>
<span class="line"><span style="color: #D4D4D4">with d as (select * from dept) select * from d;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;EOF&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">----------</span></span>
<span class="line"><span style="color: #D4D4D4">select * from dept;</span></span>
<span class="line"><span style="color: #D4D4D4">with d as (select * from dept) select * from d;</span></span></code></pre></div>



<h2 class="wp-block-heading">IslandSQL on Visual Studio Code Marketplace</h2>



<p class="wp-block-paragraph">The extension for IslandSQL is available in the <a href="https://marketplace.visualstudio.com/items?itemName=phsalvisberg.islandsql">Visual Studio Code Marketplace</a>. You can install it directly from any VS Code installation.</p>



<p class="wp-block-paragraph">Agreed, this extension is currently of limited value. However, it was a good opportunity to learn about VSCode extension development and how to use the tooling around Microsoft&#8217;s Language Server Protocol (LSP) to integrate a grammar that is written in Java.</p>



<p class="wp-block-paragraph">I am sure I can use this knowledge for other projects like utPLSQL once <a href="https://www.thatjeffsmith.com/archive/2022/10/whats-next-for-oracle-sql-developer-a-quick-sneek-peek/">SQL Developer for VScode</a> is available.</p>



<h2 class="wp-block-heading">Outlook</h2>



<p class="wp-block-paragraph">I plan to extend the IslandSQL grammar step by step and blog about the progress. At some point, it will be necessary to move the logic from the lexer to the parser. Before that, I&#8217;ll be working on the lexer side a bit longer.</p>



<p class="wp-block-paragraph">Adding the missing DML statements to the grammar will be the next item on my to-do list.</p>



<p class="wp-block-paragraph">Another topic is utPLSQL. The utPLSQL annotations in package specifications could be easily parsed with a dedicated island grammar. We could visualise test suite hierarchies in the IDE and also consider tags. Of course, we would duplicate some of utPLSQL&#8217;s code in the database. The advantage of such an approach is that we know where a test package is located in the file system. This helps in navigating to the right place, e.g. after test execution failures and could greatly improve the experience of file-based development (compared to SQL Developer). I am looking forward to the next generation of SQL Developer based on VS Codium, where such an extension would bring the most value.</p>
<p>The post <a href="https://www.salvis.com/blog/2023/02/04/islandsql-episode-1-select-statement/">IslandSQL Episode 1:  Select Statement</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.salvis.com/blog/2023/02/04/islandsql-episode-1-select-statement/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Testing With utPLSQL &#8211; Made Easy With SQL Developer</title>
		<link>https://www.salvis.com/blog/2022/07/31/testing-with-utplsql-made-easy-with-sql-developer/</link>
		
		<dc:creator><![CDATA[Philipp Salvisberg]]></dc:creator>
		<pubDate>Sat, 30 Jul 2022 22:39:16 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQL Developer]]></category>
		<category><![CDATA[utPLSQL]]></category>
		<guid isPermaLink="false">https://www.salvis.com/blog/?p=11573</guid>

					<description><![CDATA[<p>Nowadays, everything is about automation. Software testing is no exception. After an introduction, we will create and run utPLSQL tests with Oracle SQL Developer for a fictitious user story. Our tests will cover a PL/SQL package, a virtual column, two database triggers and a view. The full code samples can be found<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2022/07/31/testing-with-utplsql-made-easy-with-sql-developer/">Testing With utPLSQL &#8211; Made Easy With SQL Developer</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Nowadays, everything is about automation. Software testing is no exception. After an introduction, we will create and run utPLSQL tests with Oracle SQL Developer for a fictitious user story. Our tests will cover a PL/SQL package, a virtual column, two database triggers and a view. The full code samples can be found <a href="https://github.com/PhilippSalvisberg/utplsql-red-stack-demo">here</a>.</p>



<p class="wp-block-paragraph">This blog post is based on my German article <a href="https://backoffice.doag.org/formes/pubfiles/14512922/docs/Publikationen/Red-Stack-Magazin-inkl-Business-News/2022/04-2022/04_2022-Red_Stack_Magazin-WEB.pdf#page=32">Testen mit utPLSQL &#8211; Leicht gemacht mit SQL Developer</a>, published in issue 4/2022 of the <a href="https://www.doag.org/de/mitmachen/red-stack-business-news-archiv/" rel="nofollow">Red Stack Magazin</a>.</p>



<h2 class="wp-block-heading">What is Test Automation?</h2>



<p class="wp-block-paragraph">Here&#8217;s the definition from the <a href="https://en.wikipedia.org/wiki/Test_automation">English Wikipedia page</a>:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Test automation is the use of software separate from the software being tested to control the execution of tests and the comparison of actual outcomes with predicted outcomes.</p>
</blockquote>



<p class="wp-block-paragraph">If the actual results match the expected results, then the tests are successful. An important aspect of test automation is the prior definition of expected results. Expected results are simply requirements. In other words, we use automated tests to check whether the requirements are met.</p>



<h2 class="wp-block-heading">Why Do We Need Automated Tests?</h2>



<p class="wp-block-paragraph">The use of agile methods and the associated shorter release cycles mean that we need to test software more frequently. In a CI/CD environment, automated tests can be executed directly after a commit in a version control system. Through this continuous testing, the state of the software quality is constantly available. In case of deviations, the possible causes can be identified more quickly due to the shorter intervals between tests. In addition, the changes, since the last successful test run, are still present in the minds of the developers. All this simplifies troubleshooting.</p>



<p class="wp-block-paragraph">When software is tested only semi-automatically or manually, this leads to higher costs, higher risks, delivery delays or quality problems. It is simply not efficient to perform automatable tasks manually.</p>



<p class="wp-block-paragraph">Repeating automated tests is cheap. Unlike manual tests, there is no reason to compromise. This is especially important for central components of a software solution. We need to ensure that changes do not have any unwanted effects on other parts of the software solution.</p>



<h2 class="wp-block-heading">Do Automated Tests Have Downsides?</h2>



<p class="wp-block-paragraph">In the end, an automated test is also software that needs to be maintained. Tests that are not reliable are especially problematic. These are so-called &#8220;flaky&#8221; tests, which sometimes fail, but if you repeat them often enough, deliver the expected result. Such tests quickly do more harm than good. The more flaky tests there are, the higher the probability that the CI job will fail, resulting in manual activities.</p>



<h2 class="wp-block-heading">What Can We Test in the Database?</h2>



<p class="wp-block-paragraph">Basically, every component of an application installed in the database can be tested automatically. These are, for example, object types, packages, procedures, functions, triggers, views and constraints.</p>



<p class="wp-block-paragraph">We do not write tests for ordinary not null check constraints since we do not want to test the basic functionality of a database. However, it might be useful to test a complex expression of a virtual column or a complex condition of a check constraint. A view is based on a <code>select</code> statement. <code>select</code> statements can be quite complex even without PL/SQL units in the <code>with_clause</code>. Consequently, we also have the need to check whether views return the expected results.</p>



<p class="wp-block-paragraph">Object types, packages, procedures, functions and triggers mainly contain code in PL/SQL. However, PL/SQL allows the embedding of various other languages such as SQL, C, Java or JavaScript. It goes without saying that these components should be tested with automated tests. This also applies to database triggers, which cannot be executed directly. Of course, it simplifies testing if the logic of the database triggers is stored in PL/SQL packages and database triggers only contain simple calls to package procedures.</p>



<h2 class="wp-block-heading">What Is utPLSQL?</h2>



<p class="wp-block-paragraph">utPLSQL is a testing suite for code in the Oracle database and is based on concepts from other unit testing frameworks such as JUnit and RSpec. The figure below shows the components of the utPLSQL suite.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql.png"><img decoding="async" width="2992" height="1520" src="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql.png" alt="utPLSQL" class="wp-image-11580" srcset="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql.png 2992w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-300x152.png 300w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-1024x520.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-768x390.png 768w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-1536x780.png 1536w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-2048x1040.png 2048w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-260x132.png 260w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-50x25.png 50w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-148x75.png 148w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-1x1.png 1w" sizes="(max-width:767px) 480px, (max-width:2992px) 100vw, 2992px" /></a></figure>



<h3 class="wp-block-heading">Core Testing Framework</h3>



<p class="wp-block-paragraph">The Core Testing Framework is installed in a dedicated schema of the Oracle database. All tables in this schema are used for temporary data or caching purposes. That means newer or older versions of utPLSQL can be installed at any time without data loss. Test suites are PL/SQL packages which are annotated with a special comment <code>--%suite</code>. These special comments are called annotations and provide utPLSQL with all information for test execution. utPLSQL uses different concepts to efficiently read the required data from the Oracle data dictionary views so that tests can be executed without noticeable time delay even in schemas with more than 40 thousand PL/SQL packages.</p>



<h3 class="wp-block-heading">Development</h3>



<p class="wp-block-paragraph">The utPLSQL plugins for SQL Developer from Oracle and PL/SQL Developer from Allround Automations support database developers in creating, executing and debugging tests. Code coverage reports provide information about which code is covered by tests. These two plugins are provided by the utPLSQL team. Quest and JetBrains position utPLSQL as the standard for testing code in an Oracle database and support utPLSQL directly in their products TOAD and DataGrip.</p>



<h3 class="wp-block-heading">Test Automation</h3>



<p class="wp-block-paragraph">utPLSQL uses reporters to produce the results of a test run in any number of output formats. The command line client or the Maven plugin can be used for this purpose. A popular output format is the JUnit since it is compatible with most tools in the CI/CD area. TeamCity and Team Foundation Server are supported by specific reporters. For code coverage, utPLSQL provides reporters for SonarQube, Coveralls and Cobertura. Thanks to the flexible reporter concept, utPLSQL can be integrated into any CI/CD environment. For example, in Jenkins, Bamboo, Azure DevOps, Travis CI, GitLab, GitHub Actions and many more.</p>



<h2 class="wp-block-heading">Case Study And Solution Approach</h2>



<p class="wp-block-paragraph">We use a schema <code>redstack</code> with the known Oracle tables <code>dept</code> and <code>emp</code>. In the current sprint, the following user story from our HR manager is scheduled:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">As a HR manager, I need a table with the key figures salary total, number of employees and average salary per department to assess fairness.</p>
</blockquote>



<p class="wp-block-paragraph">I interpret the requirement literally and create a table according to listing 1.</p>



<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">Listing 1: table deptsal</span><span role="button" tabindex="0" data-code="create table deptsal (
    deptno   number(2, 0)  not null
       constraint deptsal_pk primary key,
    dname    varchar2(14)  not null,
    sum_sal  number(10, 2) not null,
    num_emps number(4, 0)  not null,
    avg_sal  number(7, 2)  
       generated always as (
          case
             when num_emps != 0 then
                round(sum_sal / num_emps, 2)
             else
                0
             end
       ) virtual
);" 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</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">table</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">deptsal</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    deptno   </span><span style="color: #569CD6">number</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">)  </span><span style="color: #569CD6">not null</span></span>
<span class="line"><span style="color: #D4D4D4">       </span><span style="color: #569CD6">constraint</span><span style="color: #D4D4D4"> deptsal_pk </span><span style="color: #569CD6">primary key</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    dname    </span><span style="color: #569CD6">varchar2</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">14</span><span style="color: #D4D4D4">)  </span><span style="color: #569CD6">not null</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    sum_sal  </span><span style="color: #569CD6">number</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">not null</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    num_emps </span><span style="color: #569CD6">number</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">4</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">)  </span><span style="color: #569CD6">not null</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    avg_sal  </span><span style="color: #569CD6">number</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">7</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">)  </span></span>
<span class="line"><span style="color: #D4D4D4">       </span><span style="color: #569CD6">generated</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">always</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #569CD6">case</span></span>
<span class="line"><span style="color: #D4D4D4">             </span><span style="color: #569CD6">when</span><span style="color: #D4D4D4"> num_emps != </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">then</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #DCDCAA">round</span><span style="color: #D4D4D4">(sum_sal / num_emps, </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">             </span><span style="color: #569CD6">else</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #B5CEA8">0</span></span>
<span class="line"><span style="color: #D4D4D4">             </span><span style="color: #569CD6">end</span></span>
<span class="line"><span style="color: #D4D4D4">       ) virtual</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span></code></pre></div>



<p class="wp-block-paragraph">I populate the table using a PL/SQL package procedure called &#8220;etl.refresh_deptsal&#8221;. The exact implementation is not so important. If you are interested you can find it <a href="https://github.com/PhilippSalvisberg/utplsql-red-stack-demo/blob/a-diy-test-1-error/src/main/package/etl.pkb">here</a>.</p>



<h2 class="wp-block-heading">Test Suite and Test Run</h2>



<p class="wp-block-paragraph">Writing the code before the test is not test-driven development. Anyway, I am a pragmatist and not a dogmatist. In the end, I feel the need to check if my code does what it should do. To execute the code, I use utPLSQL. The utPLSQL extension for SQL Developer helps me to do this. For example, I can generate the skeleton of a test suite based on my existing implementation or use the code templates to create a test package, a test package body or a test procedure. The following figure shows the utPLSQL preferences of the generator and the code templates in SQL Developer.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate.png"><img loading="lazy" decoding="async" width="1400" height="1000" src="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate.png" alt="utPLSQL preferences" class="wp-image-11586" srcset="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate.png 1400w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate-300x214.png 300w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate-1024x731.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate-768x549.png 768w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate-204x146.png 204w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate-50x36.png 50w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate-105x75.png 105w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-preferences-generate-1x1.png 1w" sizes="auto, (max-width:767px) 480px, (max-width:1400px) 100vw, 1400px" /></a></figure>



<p class="wp-block-paragraph">In a new worksheet, I entered <code>ut_spec</code> followed by <code>Ctrl-Space</code> to fill in the template for a test suite. Listing 2 shows the result.</p>



<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">Listing 2: test suite test_etl</span><span role="button" tabindex="0" data-code="create or replace package test_etl is
   --%suite

   --%test
   procedure refresh_deptsal;
end test_etl;
/" 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"> </span><span style="color: #569CD6">package</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">test_etl</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">is</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">--%suite</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">--%test</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">procedure</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">refresh_deptsal</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">end</span><span style="color: #D4D4D4"> test_etl;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span></code></pre></div>



<p class="wp-block-paragraph">The annotation <code>--%suite</code> identifies the package <code>test_etl</code> as a test suite. The annotation for the package must follow the keyword <code>is</code> or <code>as</code>. Up to the first empty line, all annotations are assigned to the package. The annotation <code>--%test</code> marks the procedure <code>refresh_deptsal</code> as a test. A test suite can consist of any number of tests. Tests can optionally be grouped with the annotations <code>--%context(&lt;name>)</code> and  <code>--%endcontext</code>. utPLSQL supports more than 20 annotations, which are available as snippets in SQL Developer.</p>



<p class="wp-block-paragraph">I can now run this test suite. Either via the context menu in the editor or on a node in the navigation tree. The following figure shows the utPLSQL test runner after the execution of the test.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error.png"><img loading="lazy" decoding="async" width="1650" height="724" src="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error.png" alt="utPLSQL error" class="wp-image-11588" srcset="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error.png 1650w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error-300x132.png 300w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error-1024x449.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error-768x337.png 768w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error-1536x674.png 1536w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error-260x114.png 260w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error-50x22.png 50w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-1-test-error-150x66.png 150w" sizes="auto, (max-width:767px) 480px, (max-width:1650px) 100vw, 1650px" /></a></figure>



<p class="wp-block-paragraph">The test runner uses two additional temporary connections to the database in the background. This way the SQL Developer is never blocked, which is very helpful, especially for more time-consuming tests. It is even possible to run independent tests simultaneously.</p>



<h2 class="wp-block-heading">Test Implementation and Test Run</h2>



<p class="wp-block-paragraph">The test <code>refresh_deptsal</code> produces an error. The reason is obvious. The implementation of the test is missing. So, let&#8217;s create the missing package body.</p>



<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);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);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">Listing 3: implementation of test suite test_etl</span><span role="button" tabindex="0" data-code="create or replace package body test_etl is
   procedure refresh_deptsal is
      c_actual   sys_refcursor;
      c_expected sys_refcursor;
   begin
      -- act
      etl.refresh_deptsal;
      
      -- assert;
      open c_actual for select * from deptsal;
      open c_expected for
         select d.deptno,
                d.dname,
                nvl(sum(e.sal), 0) as sum_sal,
                nvl(count(e.empno), 0) as num_emps,
                nvl(trunc(avg(e.sal), 2), 0) as avg_sal
           from dept d
           left join emp e
             on e.deptno = d.deptno
          group by d.deptno, d.dname;
      ut.expect(c_actual).to_equal(c_expected)
                         .join_by('DEPTNO');
   end refresh_deptsal;
end test_etl;
/" 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"> </span><span style="color: #569CD6">package body</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">test_etl</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">is</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">procedure</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">refresh_deptsal</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">is</span></span>
<span class="line"><span style="color: #D4D4D4">      c_actual   sys_refcursor;</span></span>
<span class="line"><span style="color: #D4D4D4">      c_expected sys_refcursor;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">begin</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #6A9955">-- act</span></span>
<span class="line"><span style="color: #D4D4D4">      etl.refresh_deptsal;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #6A9955">-- assert;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">open</span><span style="color: #D4D4D4"> c_actual </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> deptsal;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">open</span><span style="color: #D4D4D4"> c_expected </span><span style="color: #C586C0">for</span></span>
<span class="line"><span style="color: #D4D4D4">         </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> d.deptno,</span></span>
<span class="line"><span style="color: #D4D4D4">                d.dname,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #DCDCAA">nvl</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">sum</span><span style="color: #D4D4D4">(e.sal), </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> sum_sal,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #DCDCAA">nvl</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(e.empno), </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> num_emps,</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">                </span><span style="color: #DCDCAA">nvl</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">trunc</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">avg</span><span style="color: #D4D4D4">(e.sal), </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">), </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> avg_sal</span></span>
<span class="line"><span style="color: #D4D4D4">           </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> dept d</span></span>
<span class="line"><span style="color: #D4D4D4">           </span><span style="color: #569CD6">left join</span><span style="color: #D4D4D4"> emp e</span></span>
<span class="line"><span style="color: #D4D4D4">             </span><span style="color: #569CD6">on</span><span style="color: #D4D4D4"> e.deptno = d.deptno</span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #569CD6">group by</span><span style="color: #D4D4D4"> d.deptno, d.dname;</span></span>
<span class="line"><span style="color: #D4D4D4">      ut.expect(c_actual).to_equal(c_expected)</span></span>
<span class="line"><span style="color: #D4D4D4">                         .join_by(</span><span style="color: #CE9178">&#39;DEPTNO&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">end</span><span style="color: #D4D4D4"> refresh_deptsal;</span></span>
<span class="line"><span style="color: #569CD6">end</span><span style="color: #D4D4D4"> test_etl;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span></code></pre></div>



<p class="wp-block-paragraph">The implementation of the test in listing 3 uses the <a href="https://java-design-patterns.com/patterns/arrange-act-assert/">AAA pattern</a>. It stands for Arrange-Act-Assert. In the arrange step, the test is prepared. Here, this step is missing since the test is based on existing data. In the act step, we execute the code under test. And in the assert step, we compare the actual result with the expected result. utPLSQL provides a variety of type-safe matchers for this purpose. In this case, we compare two cursors. The data types as well as the contents of the columns are compared for all rows. The figure below shows the result after our next test run.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure.png"><img loading="lazy" decoding="async" width="1648" height="958" src="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure.png" alt="utPLSQL failure" class="wp-image-11590" srcset="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure.png 1648w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure-300x174.png 300w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure-1024x595.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure-768x446.png 768w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure-1536x893.png 1536w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure-251x146.png 251w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure-50x29.png 50w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure-129x75.png 129w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-2-test-failure-1x1.png 1w" sizes="auto, (max-width:767px) 480px, (max-width:1648px) 100vw, 1648px" /></a></figure>



<p class="wp-block-paragraph">Now the test run produces a failure. This means that the test does not deliver the expected result. In the `Failures` tab of the test runner, the details are displayed. We see that we expected 4 rows and also got 4 rows. However, there are differences in the column <code>avg_sal</code> for two records. This looks like a rounding difference. Obviously, we round up in the code and round down in the test. What is correct now? I would say rounding up is common and that&#8217;s why we should adjust the test. After replacing <code>trunc</code> with <code>round</code> in the test implementation (see line 16 in listing 3), the test will complete successfully.</p>



<h2 class="wp-block-heading">Test Cases for Insert, Update and Delete</h2>



<p class="wp-block-paragraph">The test <code>refresh_deptsal</code> expects data in the tables <code>dept</code> and <code>emp</code> If the tables are empty, the test still works. This is not wrong, but it shows that the quality of the test is strongly dependent on our existing data. Also, we run the risk that the test code for the assert will mirror the implementation and any errors will be repeated. Listing 4 shows an additional test based on our own test data.</p>



<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-start:70;--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">Listing 4: test based on artificial data</span><span role="button" tabindex="0" data-code="   procedure refresh_deptsal_upd_dept_and_emp is
      c_actual   sys_refcursor;
      c_expected sys_refcursor;
   begin
      -- arrange
      insert into dept (deptno, dname, loc)
      values (-10, 'utPLSQL', 'Winterthur');
      insert into emp (empno, ename, job, hiredate, sal, deptno)
      values (-1, 'Jacek', 'Developer', trunc(sysdate), 4700, -10);
      insert into emp (empno, ename, job, hiredate, sal, deptno)
      values (-2, 'Sam', 'Developer', trunc(sysdate), 4300, -10);
      
      -- act
      update dept set dname = 'Testing' where deptno = -10;
      update emp set sal = 5000 where empno = -2;
      
      -- assert
      open c_actual for select * from deptsal where deptno = -10;
      open c_expected for
         select -10 as deptno,
                'Testing' as dname,
                9700 as sum_sal,
                2 as num_emps,
                4850 as avg_sal
           from dual;
      ut.expect(c_actual).to_equal(c_expected).join_by('DEPTNO');
   end refresh_deptsal_upd_dept_and_emp;" 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: #D4D4D4">   </span><span style="color: #569CD6">procedure</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">refresh_deptsal_upd_dept_and_emp</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">is</span></span>
<span class="line"><span style="color: #D4D4D4">      c_actual   sys_refcursor;</span></span>
<span class="line"><span style="color: #D4D4D4">      c_expected sys_refcursor;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">begin</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #6A9955">-- arrange</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">insert</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">into</span><span style="color: #D4D4D4"> dept (deptno, dname, loc)</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">values</span><span style="color: #D4D4D4"> (-</span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;utPLSQL&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;Winterthur&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">insert</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">into</span><span style="color: #D4D4D4"> emp (empno, ename, job, hiredate, sal, deptno)</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">values</span><span style="color: #D4D4D4"> (-</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;Jacek&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;Developer&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #DCDCAA">trunc</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">sysdate</span><span style="color: #D4D4D4">), </span><span style="color: #B5CEA8">4700</span><span style="color: #D4D4D4">, -</span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">insert</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">into</span><span style="color: #D4D4D4"> emp (empno, ename, job, hiredate, sal, deptno)</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">values</span><span style="color: #D4D4D4"> (-</span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;Sam&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;Developer&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #DCDCAA">trunc</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">sysdate</span><span style="color: #D4D4D4">), </span><span style="color: #B5CEA8">4300</span><span style="color: #D4D4D4">, -</span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">      </span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #6A9955">-- act</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">update</span><span style="color: #D4D4D4"> dept </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4"> dname = </span><span style="color: #CE9178">&#39;Testing&#39;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">where</span><span style="color: #D4D4D4"> deptno = -</span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">update</span><span style="color: #D4D4D4"> emp </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4"> sal = </span><span style="color: #B5CEA8">5000</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">where</span><span style="color: #D4D4D4"> empno = -</span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #6A9955">-- assert</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">open</span><span style="color: #D4D4D4"> c_actual </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> * </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> deptsal </span><span style="color: #569CD6">where</span><span style="color: #D4D4D4"> deptno = -</span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">open</span><span style="color: #D4D4D4"> c_expected </span><span style="color: #C586C0">for</span></span>
<span class="line"><span style="color: #D4D4D4">         </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> -</span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> deptno,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #CE9178">&#39;Testing&#39;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> dname,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #B5CEA8">9700</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> sum_sal,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> num_emps,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #B5CEA8">4850</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> avg_sal</span></span>
<span class="line"><span style="color: #D4D4D4">           </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> dual;</span></span>
<span class="line"><span style="color: #D4D4D4">      ut.expect(c_actual).to_equal(c_expected).join_by(</span><span style="color: #CE9178">&#39;DEPTNO&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">end</span><span style="color: #D4D4D4"> refresh_deptsal_upd_dept_and_emp;</span></span></code></pre></div>



<p class="wp-block-paragraph">First, a department <code>-10</code> is created with employees <code>-1</code> and <code>-2</code>. Then the salaries of these two employees are adjusted. The expected results are based entirely on literals. Real identifiers are often positive. The use of negative values makes the tests independent of existing data. utPLSQL automatically sets a savepoint before running a test. At the end of the test, a rollback to this savepoint is performed. However, if a <code>commit</code> occurs somewhere, the test data with negative identifiers are quickly found and deleted.</p>



<p class="wp-block-paragraph">The next figure shows the successful execution of further tests, which I created <a href="https://github.com/PhilippSalvisberg/utplsql-red-stack-demo/tree/a-diy-test-4-ins-upd-del/src/test/package">here</a>.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del.png"><img loading="lazy" decoding="async" width="1648" height="1068" src="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del.png" alt="successful utPLSQL test run" class="wp-image-11596" srcset="https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del.png 1648w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del-300x194.png 300w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del-1024x664.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del-768x498.png 768w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del-1536x995.png 1536w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del-225x146.png 225w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del-50x32.png 50w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del-116x75.png 116w, https://www.salvis.com/blog/wp-content/uploads/2022/07/utplsql-a-diy-4-test-ins-upd-del-1x1.png 1w" sizes="auto, (max-width:767px) 480px, (max-width:1648px) 100vw, 1648px" /></a></figure>



<h2 class="wp-block-heading">Automatic Refresh</h2>



<p class="wp-block-paragraph">The table <code>deptsal</code> is updated by calling <code>etl.refresh_deptsal</code>. We can fire this call using database triggers. Listing 5 shows how.</p>



<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">Listing 5: After statement triggers to refresh table deptsal</span><span role="button" tabindex="0" data-code="create or replace trigger dept_as_iud
   after insert or update or delete on dept
begin
   etl.refresh_deptsal;
end;
/
create or replace trigger emp_as_iud
   after insert or update or delete on emp
begin
   etl.refresh_deptsal;
end;
/" 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"> </span><span style="color: #569CD6">trigger</span><span style="color: #D4D4D4"> dept_as_iud</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">after</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">insert</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">update</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">delete</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">on</span><span style="color: #D4D4D4"> dept</span></span>
<span class="line"><span style="color: #569CD6">begin</span></span>
<span class="line"><span style="color: #D4D4D4">   etl.refresh_deptsal;</span></span>
<span class="line"><span style="color: #569CD6">end</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #569CD6">create or replace</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">trigger</span><span style="color: #D4D4D4"> emp_as_iud</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">after</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">insert</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">update</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">delete</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">on</span><span style="color: #D4D4D4"> emp</span></span>
<span class="line"><span style="color: #569CD6">begin</span></span>
<span class="line"><span style="color: #D4D4D4">   etl.refresh_deptsal;</span></span>
<span class="line"><span style="color: #569CD6">end</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span></code></pre></div>



<p class="wp-block-paragraph">Now the table <code>deptsal</code>  is updated after each DML statement on tables <code>emp</code> and <code>dept</code>. To test if the database triggers work, I just need to remove the call of <code>etl.refresh_deptsal</code> in the existing tests. For the test on the existing data, I can fire a refresh with a technical update. And voilà, all tests run through without errors.</p>



<p class="wp-block-paragraph">Time to commit the changes in the version control system, create a pull request, and perform a review.</p>



<h2 class="wp-block-heading">Incorporating Review Comments</h2>



<p class="wp-block-paragraph">I showed the code to my colleague Lisa. She said that the solution is more complex than necessary and inefficient. On the one hand, the <code>deptsal</code> table is updated too often, for example before a <code>rollback</code>or when more than one DML statement is used on the underlying tables within a transaction. On the other hand, providing a table is not absolutely necessary. Even though the HR manager specifically mentions &#8220;table&#8221; in her story, it is legitimate to use a view here instead of a table. Lisa thinks that with the amount of data we have, the performance should be good enough, especially if the query is limited to a few departments. Also, these metrics are not queried that often. A view would significantly reduce our code base and simplify maintenance.</p>



<p class="wp-block-paragraph">Of course, Lisa is right. I can drop the table <code>deptsal</code>, the package <code>etl</code> and the database triggers. A simple view called <code>deptsal</code>, which is based on the fixed query in Listing 3, is sufficient here. But what do I do with my tests? &#8211; Nothing! They still describe the requirements, which have not changed, and therefore completed successfully. Unless I made a mistake when defining the view.</p>



<p class="wp-block-paragraph">Refactoring is much easier with existing tests. Some people would argue that tests are what make safe refactoring possible.</p>



<h2 class="wp-block-heading">Core Messages And Recommendations</h2>



<p class="wp-block-paragraph">The first steps with utPLSQL are the hardest. Make <a href="https://github.com/utPLSQL/utPLSQL">utPLSQL</a> available in all your development and testing environments. For shared environments, ask your DBA for help. Install also the <a href="https://github.com/utPLSQL/utPLSQL-SQLDeveloper">extension for SQL Developer</a>, it is not a requirement, but it simplifies the work with utPLSQL considerably.</p>



<p class="wp-block-paragraph">Start with small steps. Use utPLSQL to reproduce bugs or to test new requirements. After a short time you will experience how utPLSQL changes the way you code. You will write smaller units. You will isolate code that is difficult to test. This will make your code easier to test and easier to maintain.</p>
<p>The post <a href="https://www.salvis.com/blog/2022/07/31/testing-with-utplsql-made-easy-with-sql-developer/">Testing With utPLSQL &#8211; Made Easy With SQL Developer</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>utPLSQL for SQL Developer 1.2 &#8211; What&#8217;s New?</title>
		<link>https://www.salvis.com/blog/2020/06/07/utplsql-for-sql-developer-1-2-whats-new/</link>
		
		<dc:creator><![CDATA[Philipp Salvisberg]]></dc:creator>
		<pubDate>Sun, 07 Jun 2020 14:07:15 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[SQL Developer]]></category>
		<category><![CDATA[utPLSQL]]></category>
		<guid isPermaLink="false">https://www.salvis.com/blog/?p=9836</guid>

					<description><![CDATA[<p>Today I released an update of the utPLSQL extension for SQL Developer. In this blog post, I explain the changes in the latest version 1.2. These are the new features: Download the latest version from GitHub. Debug Test Now you can run one or more utPLSQL tests with the PL/SQL Debugger. To<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2020/06/07/utplsql-for-sql-developer-1-2-whats-new/">utPLSQL for SQL Developer 1.2 &#8211; What&#8217;s New?</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Today I released an update of the <a href="https://github.com/utPLSQL/utPLSQL-SQLDeveloper">utPLSQL extension for SQL Developer</a>. In this blog post, I explain the changes in the latest version 1.2. These are the new features:</p>



<ul class="wp-block-list">
<li><a href="#DebugTest">Debug Test</a></li>



<li><a href="#CancelTestRun">Cancel Test Run</a></li>



<li><a href="#TestRunWithCodeCoverage">Test Run with Code Coverage</a></li>



<li><a href="#JavaInsteadOfXtend">Java instead of Xtend</a></li>
</ul>



<p class="wp-block-paragraph">Download the latest version from <a href="https://github.com/utPLSQL/utPLSQL-SQLDeveloper/releases">GitHub</a>.</p>



<h2 class="wp-block-heading"><a id="DebugTest"></a>Debug Test</h2>



<p class="wp-block-paragraph">Now you can run one or more utPLSQL tests with the PL/SQL Debugger. To do this, select the context menu item <code>Debug utPLSQL test...</code> from the Connections window.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2029/06/connections-window-debug-utplsql-test.png"><img loading="lazy" decoding="async" width="412" height="371" src="https://www.salvis.com/blog/wp-content/uploads/2029/06/connections-window-debug-utplsql-test.png" alt="" class="wp-image-9839" srcset="https://www.salvis.com/blog/wp-content/uploads/2029/06/connections-window-debug-utplsql-test.png 412w, https://www.salvis.com/blog/wp-content/uploads/2029/06/connections-window-debug-utplsql-test-300x270.png 300w, https://www.salvis.com/blog/wp-content/uploads/2029/06/connections-window-debug-utplsql-test-162x146.png 162w, https://www.salvis.com/blog/wp-content/uploads/2029/06/connections-window-debug-utplsql-test-50x45.png 50w, https://www.salvis.com/blog/wp-content/uploads/2029/06/connections-window-debug-utplsql-test-83x75.png 83w, https://www.salvis.com/blog/wp-content/uploads/2029/06/connections-window-debug-utplsql-test-1x1.png 1w" sizes="auto, (max-width:767px) 412px, 412px" /></a></figure>



<p class="wp-block-paragraph">This context menu item is also available in</p>



<ul class="wp-block-list">
<li>the PL/SQL Editor</li>



<li>the Worksheet</li>



<li>the Realtime Reporter</li>
</ul>



<p class="wp-block-paragraph">Additionally, you can rerun all tests with the PL/SQL Debugger from the toolbar in the Realtime Reporter.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2029/06/rerun-all-tests-with-plsql-debugger.png"><img loading="lazy" decoding="async" width="310" height="89" src="https://www.salvis.com/blog/wp-content/uploads/2029/06/rerun-all-tests-with-plsql-debugger.png" alt="" class="wp-image-9840" srcset="https://www.salvis.com/blog/wp-content/uploads/2029/06/rerun-all-tests-with-plsql-debugger.png 310w, https://www.salvis.com/blog/wp-content/uploads/2029/06/rerun-all-tests-with-plsql-debugger-300x86.png 300w, https://www.salvis.com/blog/wp-content/uploads/2029/06/rerun-all-tests-with-plsql-debugger-260x75.png 260w, https://www.salvis.com/blog/wp-content/uploads/2029/06/rerun-all-tests-with-plsql-debugger-50x14.png 50w, https://www.salvis.com/blog/wp-content/uploads/2029/06/rerun-all-tests-with-plsql-debugger-150x43.png 150w" sizes="auto, (max-width:767px) 310px, 310px" /></a></figure>



<p class="wp-block-paragraph">Debugging works with</p>



<ul class="wp-block-list">
<li><code>DBMS_DEBUG_JDWP</code> (the default in SQL Developer, opens a TCP/IP connection from the database to the client, suited for remote debugging of PL/SQL code, e.g. from an APEX application) and</li>



<li><code>DBMS_DEBUG</code> (deprecated package without remote debugging capabilities, using SQL*Net based sessions only).</li>
</ul>



<p class="wp-block-paragraph">You can select the debugging package in the preferences of SQL Developer 20.2. For older versions see <a href="https://www.salvis.com/blog/2019/04/08/using-dbms_debug-in-sql-developer/">this blog post</a> to learn how to switch the debugging package.</p>



<p class="wp-block-paragraph">See this <a href="https://www.slideshare.net/PhilippSalvisberg/using-the-plsql-debugger">slide deck</a> or this <a href="https://www.youtube.com/watch?v=b7Q55YvrcgA">video</a> for more information about the PL/SQL Debugger and how to make it work in your environment.</p>



<h2 class="wp-block-heading"><a id="CancelTestRun"></a>Cancel Test Run</h2>



<p class="wp-block-paragraph">By default, a test run is terminated when the initialization takes longer than 60 seconds or the whole test run exceeds the limit of four hours.</p>



<p class="wp-block-paragraph">When debugging a test run, you might want to debug the utPLSQL framework execution itself. Therefore we changed the initialization timeout to one hour when debugging a utPLSQL test. When the debug session crashes or you decide to stop it, the Realtime Reporter continues to wait for test events. But since the producer session is stopped, no test events are created for the Realtime Reporter any more. In this case, you can just cancel the test run by clicking on the red button in the toolbar of the Realtime Reporter. As you see in the next screenshot.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2029/06/stop-utplsql-run-small.png"><img loading="lazy" decoding="async" width="510" height="116" src="https://www.salvis.com/blog/wp-content/uploads/2029/06/stop-utplsql-run-small.png" alt="" class="wp-image-9851" srcset="https://www.salvis.com/blog/wp-content/uploads/2029/06/stop-utplsql-run-small.png 510w, https://www.salvis.com/blog/wp-content/uploads/2029/06/stop-utplsql-run-small-300x68.png 300w, https://www.salvis.com/blog/wp-content/uploads/2029/06/stop-utplsql-run-small-260x59.png 260w, https://www.salvis.com/blog/wp-content/uploads/2029/06/stop-utplsql-run-small-50x11.png 50w, https://www.salvis.com/blog/wp-content/uploads/2029/06/stop-utplsql-run-small-150x34.png 150w" sizes="auto, (max-width:767px) 480px, 510px" /></a></figure>



<p class="wp-block-paragraph">Of course, you can use this feature also to cancel any other test run. For example, if you started a long-running test by accident.</p>



<p class="wp-block-paragraph">What happens behind the scene when you&#8217;re cancelling a test run?</p>



<p class="wp-block-paragraph">First, the JDBC session associated with the Realtime Reporter (the consumer) is aborted. This will lead to the termination of the test run and if the producer session is still running, then it is aborted as well. An <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/jajdb/oracle/jdbc/OracleConnection.html#abort__">abort</a> of a JDBC connection will delegate the termination of the associated database session to the Oracle Database. However, it might be necessary to kill those sessions using <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ALTER-SYSTEM.html#GUID-2C638517-D73A-41CA-9D8E-A62D1A0B7ADB__I2260288"><code>ALTER SYSTEM KILL SESSION</code></a> or <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ALTER-SYSTEM.html#GUID-2C638517-D73A-41CA-9D8E-A62D1A0B7ADB__I2198603"><code>ALTER SYSTEM DISCONNECT SESSION</code></a>.</p>



<p class="wp-block-paragraph">Second, all remaining tests of the terminated test run are marked as &#8220;disabled&#8221;. A warning message &#8220;Test disabled due to abortion of the test run.&#8221; is added to each disabled test.</p>



<h2 class="wp-block-heading"><a id="TestRunWithCodeCoverage"></a>Test Run with Code Coverage</h2>



<p class="wp-block-paragraph">utPLSQL uses reporters to show the results of a test run. I categorize the active reporters as follows:</p>



<ul class="wp-block-list">
<li><strong>Test run events reported as stream</strong> (for continuous consumption by other applications such as SQL Developer)
<ul class="wp-block-list">
<li>Realtime Reporter (<code>ut_realtime_reporter</code>, XML document per event)</li>
</ul>
</li>



<li><strong>Test run result reported as stream</strong> (for full or partial, continuous consumption)
<ul class="wp-block-list">
<li>Documentation Reporter (<code>ut_documenation_reporter</code>, plain text optionally with ANSI escape sequences)</li>
</ul>
</li>



<li><strong>Test run result reported as document</strong> (for full consumption)
<ul class="wp-block-list">
<li>Instrumentation
<ul class="wp-block-list">
<li>Debug Reporter (<code>ut_debug_reporter</code>, XML).</li>
</ul>
</li>



<li>Test results
<ul class="wp-block-list">
<li>JUnit Reporter (<code>ut_junit_reporter</code>, XML)</li>



<li>Teamcity Reporter (<code>ut_teamcity_reporter_helper</code>, XML)</li>



<li>TFS/VSTS Reporter (<code>ut_tfs_junit_reporter</code>, XML)</li>



<li>SonarQube Reporter (<code>ut_sonar_test_reporter</code>, XML)</li>
</ul>
</li>



<li>Code coverage
<ul class="wp-block-list">
<li>HTML Coverage Reporter (<code>ut_coverage_html_reporter</code>, HTML)</li>



<li>Coveralls Reporter (<code>ut_coveralls_reporter</code>, JSON)</li>



<li>Cobertura Reporter (<code>ut_coverage_cobertura_reporter</code>, XML)</li>



<li>SonarQube Reporter (<code>ut_coverage_sonar_reporter</code>, XML)</li>
</ul>
</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph">utPLSQL supports an unbound number of reports per test run. As a result you can produce with a single test run as many results as you like. The <a href="https://github.com/utPLSQL/utPLSQL-cli">utPLSQL-cli</a> supports that very well.</p>



<p class="wp-block-paragraph">The SQL Developer extension now also uses this feature. <span class="highlight highlight-" style="background-color:Lemonchiffon;color:black;">The Realtime Reporter and the HTML Coverage Reporter run together.</span>
 The GUI shows the progress of the test run. And at the end of the test run the HTML Coverage opens in your default browser. Nice. However, most of the work for the HTML Coverage Report is done at the very end of the tests run. Therefore you might experience a delay before the HTML Coverage Report becomes visible.</p>



<p class="wp-block-paragraph">You can also run Code Coverage from the Realtime Reporter for the complete test run via the toolbar or for selected tests via the context menu.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter.png"><img loading="lazy" decoding="async" width="811" height="618" src="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter.png" alt="" class="wp-image-9866" srcset="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter.png 811w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter-300x229.png 300w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter-768x585.png 768w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter-192x146.png 192w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter-50x38.png 50w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter-98x75.png 98w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-menu-in-realtime-reporter-1x1.png 1w" sizes="auto, (max-width:767px) 480px, (max-width:811px) 100vw, 811px" /></a></figure>



<p class="wp-block-paragraph">In this case it is possible to calculate a reasonable default value for <code>Schemas under test</code>. As you see here:</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-dialog.png"><img loading="lazy" decoding="async" width="500" height="342" src="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-dialog.png" alt="" class="wp-image-9868" srcset="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-dialog.png 500w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-dialog-300x205.png 300w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-dialog-213x146.png 213w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-dialog-50x34.png 50w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-dialog-110x75.png 110w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-dialog-1x1.png 1w" sizes="auto, (max-width:767px) 480px, 500px" /></a></figure>



<p class="wp-block-paragraph">As in previous versions, we calculate the <code>Include objects</code> based on the test package dependencies. In this case we know the owner of the <code>Include objects</code> and these owners are the <code>Schemas under test</code>. Sorted descending by the number of objects in the <code>Include objects</code>. I like this feature since in the utPLSQL core project we store the test packages in dedicated schemas. And a good default means less typing. Time to press <code>Run</code>.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview.png"><img loading="lazy" decoding="async" width="1356" height="586" src="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview.png" alt="" class="wp-image-9870" srcset="https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview.png 1356w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview-300x130.png 300w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview-1024x443.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview-768x332.png 768w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview-260x112.png 260w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview-50x22.png 50w, https://www.salvis.com/blog/wp-content/uploads/2029/06/code-coverage-report-overview-150x65.png 150w" sizes="auto, (max-width:767px) 480px, (max-width:1356px) 100vw, 1356px" /></a></figure>



<p class="wp-block-paragraph">The default produced a bit more than I really wanted. But the <code>ut3_devlop.ut_realtime_reporter</code> is part of the result and the report was fast. So who cares?</p>



<h2 class="wp-block-heading"><a id="JavaInsteadOfXtend"></a>Java instead of Xtend</h2>



<p class="wp-block-paragraph">We migrated the complete code base from Xtend to Java. As a side-effect, the extension is now smaller, because we do not need the Xtend runtime libraries anymore. Besides that, you should not notice any difference when using this extension. Please read <a href="https://www.salvis.com/blog/2020/06/06/bye-bye-xtend-welcome-java/">this blog post</a>, if you&#8217;re interested to know why we migrated to Java and how we did it.</p>
<p>The post <a href="https://www.salvis.com/blog/2020/06/07/utplsql-for-sql-developer-1-2-whats-new/">utPLSQL for SQL Developer 1.2 &#8211; What&#8217;s New?</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
