<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Lee Lindley Scratchpad</title>
    <description>Posts on Technical Subjects, mostly Oracle and Linux</description>
    <link>https://lee-lindley.github.io/</link>
    <atom:link href="https://lee-lindley.github.io/sitemap.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 18 Aug 2024 12:50:00 -0400</pubDate>
    <lastBuildDate>Sun, 18 Aug 2024 12:50:00 -0400</lastBuildDate>
    <generator>Jekyll v4.3.1</generator>
    
      <item>
        <title>Creating Multiple Row Headers with ExcelGen PL/SQL Spreadsheet Generator</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/mbleron/ExcelGen&quot;&gt;Marc Bleron’s ExcelGen&lt;/a&gt; package version 3 has added multi-table sheets, merged cells,
and writing to individual cells. This allows us to construct workbooks with sheets that have multiple-row column
headers. The business users I support are happy about that!&lt;/p&gt;

&lt;p&gt;This article walks through an example of constructing a spreadsheet with a banner title, multiple-row
column headers, automatic filtering, plus frozen rows and columns. We also set font and background
colors in a fairly tame way, but it demonstrates the tools we have to make an attractive end product.&lt;/p&gt;

&lt;p&gt;See the manual page at the above link to ExcelGen on github to get an idea of how this all works. Refer
back to it as we walk through the example if you have questions.&lt;/p&gt;

&lt;h1 id=&quot;walking-through-an-example&quot;&gt;Walking Through an Example&lt;/h1&gt;

&lt;p&gt;Here is an image of the spreadsheet we will produce.&lt;/p&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;Figure 1 - Multi-Row Spreadsheet Headers&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/multirowHeaders1.gif&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
  &lt;p&gt;The ‘+’ characters and NULLs in the first header row are to demonstrate a peculiarity of ExcelGen. You
will notice in the image above that even though we told the tool to make the columns in our
mini-table have a light gray background, the cells that did not have any content, did not
get the background style applied. I opened an issue and expect it will be addressed in the next release.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The declared variables can be thought of as object handles to various parts of our logical spreadsheet.
The values are returned by functions we call to create/declare those parts.
In practice they are indexes into package global collections, but we do not need to know that.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;             &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctxHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sheetHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_tableId&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headerTableId&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headerCellStyle&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cellStyleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_cellStyle&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cellStyleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- discussed below. There are other ways to accomplish this&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;+,,+,
First,Last,Department,Yearly
Name,Name,Name,Salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;
                                                &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We want to use a small font for the report and for the report header to have bold text on a light gray
background. To get started we create our workbook and two style handles we can assign as needed. You can
use CSS syntax for styles, but I’m sticking with the familiar style construction from ExcelGen version 2 for now.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- create a workbook and some initial styles to use in assignments&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- style to use for header cells&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headerCellStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeCellStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_font&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeFont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Arial&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_fill&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makePatternFill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;solid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lightgray&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- style to use inside the report proper&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_cellStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeCellStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_font&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeFont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Arial&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Next we will create our first (and only) workbook sheet with the sheet name of ‘Employees’. We’ll
give the tab a color just because we can (I don’t normally). Then we put a text value
into the &lt;em&gt;A1&lt;/em&gt; cell of the sheet. This will be our report title banner at the top of the sheet.&lt;/p&gt;

&lt;p&gt;We create another style for this cell on the fly that will automatically apply to the cells we merge together 
with &lt;em&gt;A1&lt;/em&gt; in a moment.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;-- add a sheet to the workbook&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addSheet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetName&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employees&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_tabColor&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

   &lt;span class=&quot;c1&quot;&gt;-- place banner text in top left corner cell&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;putStringCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_rowIdx&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_colIdx&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_value&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employee Salary Report&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeCellStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_font&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeFont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Calibri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_fill&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makePatternFill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;solid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_alignment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeAlignment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                        &lt;span class=&quot;n&quot;&gt;p_horizontal&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                                        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_vertical&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So far that is just in a single cell. We want to make it span across the top two rows
of the report, centered. We could span a single row of cells and set the height of that row, but will
merge across two rows here instead. We could get fancy and figure out the range from a
number of rows and perhaps the number of columns in our header collection, but the water
is muddy enough for now.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mergeCells&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_range&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;A1:D2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Next, we want to create the first two rows of our column headers. We do not put our
last row of the column headers here as they must be handled separately. We could use &lt;em&gt;putStringCell&lt;/em&gt;
again to do this, but I have a plan for extending the functionality of multi-row column
header handling we’ll get to when we refactor. Therefor, we will create a cursor that
returns our column headers as data (but only the first 2 rows of header, not the last),
and add that as a table to our worksheet.&lt;/p&gt;

&lt;p&gt;The task of generating a cursor from generic two dimensional collection object turned
out to be harder than I expected. I covered it 
in &lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/2022/11/24/Cursor_from_Collections.html&quot;&gt;Create a PL/SQL Cursor from a Nested Table of Nested Tables&lt;/a&gt;. If you are not ready to install package &lt;em&gt;app_csv_pkg&lt;/em&gt; from my github
repository, that article shows other ways you can do it.&lt;/p&gt;

&lt;p&gt;You will notice that our anchor is one based. It can be confusing that absolute anchors
are one based while relative anchors are zero based. I do not think I will be the last person
to stumble and make mistakes with this.&lt;/p&gt;

&lt;p&gt;We assign a style to the columns of our little 2 row table as well.
Notice that we are using a style we call a &lt;em&gt;header&lt;/em&gt; style for the data of this table. The
table contains data we want to appear as column headers.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;v_headerTableId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_rc&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_cursor_from_collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;p_arr_arr&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_trim_rows&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- do not include last row of headers&lt;/span&gt;
                               &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorColOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- we start in column 1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorRowOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- leave 2 rows of room for our banner. we start in row 3&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1..4&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setTableColumnProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_tableId&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headerTableId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_columnId&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headerCellStyle&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Next, we add our real query to the sheet as a second table beneath the two row header table.
&lt;strong&gt;We use relative positioning for the location of this table.&lt;/strong&gt; The concepts of the anchor and
relative positioning are covered well in the documentation. There is a nice picture
with examples. I have gone back to it frequently when building this and other multi-table sheets
and/or positioning of &lt;em&gt;putCell&lt;/em&gt; calls. The zero based vs one based anchors and thinking
in terms of whether you are operating at the sheet level or the table level can be a challenge.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;v_tableId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_query&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;SELECT first_name, last_name, department_name , salary
FROM hr.employees e
INNER JOIN hr.departments d
    ON d.department_id = e.department_id
        AND department_name IN ('Finance', 'Executive')
ORDER BY salary DESC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorTableId&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headerTableId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorPosition&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BOTTOM_LEFT&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorColOffset&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorRowOffset&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For our data table we want to set the header that goes with the data. We could have simply
put the third header row in the table we constructed above, but if we did that we
could not turn on &lt;em&gt;autoFilter&lt;/em&gt;. It seems best to tell Excel that this is a header
even if you do not need &lt;em&gt;autoFilter&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We then override the column header values
from what was in the query. You could have made the query column aliases do what you needed here
and only had to set the column style properties, but this demonstrates how the query column
aliases can be ignored.&lt;/p&gt;

&lt;p&gt;Let me point out that &lt;em&gt;setTableColumnProperties&lt;/em&gt; allows us to set the value of the column header,
but the style we are applying is to this column in the data rows, not the header. The header style
is determined in the call to &lt;em&gt;setTableHeader&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The mixing of concepts between column header and 
column data in this function is unfortunate, but probably not worth making separate methods. Technically,
it all applies to a table column and the distinction in styles between header and data is a subdivision
of table column properties.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setTableHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_tableId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_tableId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headerCellStyle&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_autoFilter&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1..4&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setTableColumnProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_tableId&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_tableId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columnId&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_cellStyle&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- override the column header from query column aliases&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columnName&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We need to set the number format for the fourth data column, Salary.
We could have created a style that included the number format
and selectively used it with &lt;em&gt;setTableColumnProperties&lt;/em&gt; above.&lt;/p&gt;

&lt;p&gt;So far I’ve been choosing to set the date, timestamp and number formats separately
from the the styles.&lt;/p&gt;

&lt;p&gt;Since &lt;em&gt;setColumnFormat&lt;/em&gt; operates 
at the sheet level it applies to the header rows too, but
since those values are strings and not numbers, it doesn’t matter.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;-- This is at the sheet level&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setColumnFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columnId&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_format&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#,##0.00_);(#,##0.00)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- an accounting number style&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next we want to freeze our column headers and the first two columns of data (the first and last name values).
In this example freezing the first two columns is not very useful since the total number of
columns is so small, but it is instructive to see how it is done.&lt;/p&gt;

&lt;p&gt;Specifying the top left corner of the region that is allowed to 
scroll is what Excel does too, but we typically think in terms of which columns and rows
are frozen. Too bad. We need to specify where the scrollable active pane starts.&lt;/p&gt;

&lt;p&gt;The row we make top of the scrollable region is the first one with data – two rows
for the banner, three rows of header, so row six is first row of data. Column 3 is
the first one we want in the scrollable pane.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;-- freeze row headers and first two columns (aka setting active pane)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setSheetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_activePaneAnchorRef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeCellRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_colIdx&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_rowIdx&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We are done declaring the content of the sheet. We finish by executing everything to create the workbook file
and clean up after ourselves with the &lt;em&gt;closeContext&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;--  ExcelGen.createFile(ctx, 'TEST_DIR', 'multi-row-header.xlsx');&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TMP_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;multi-row-header.xlsx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;closeContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You saw the output spreadsheet at the top of the article.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;This journey through creating a spreadsheet touches on both the nuts and bolts of general spreadsheet
creation with &lt;em&gt;ExcelGen&lt;/em&gt; and also now some bells and whistles with a banner and multi-row column
headers.&lt;/p&gt;

&lt;p&gt;It is a lot of code though. I intend to refactor into a reusable utility procedure for multi-row
column headers. I was going to cover that utility in another article. I wound up doing the work
for an employer on their dime, so it isn’t mine to share. If you have read this far, I’m sure
you could create your own given the pattern shown here.&lt;/p&gt;

&lt;h1 id=&quot;appendix---full-code-listing&quot;&gt;Appendix - Full Code Listing&lt;/h1&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;             &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctxHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sheetHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_tableId&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headerTableId&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headerCellStyle&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cellStyleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_cellStyle&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cellStyleHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;+,,+,
First,Last,Department,Yearly
Name,Name,Name,Salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;
                                                &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- create a workbook and some initial styles to use in assignments&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headerCellStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeCellStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_font&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeFont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Arial&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_fill&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makePatternFill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;solid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lightgray&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_cellStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeCellStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_font&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeFont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Arial&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- add a sheet to the workbook&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addSheet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetName&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employees&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_tabColor&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- place banner text in top left corner cell&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;putStringCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_rowIdx&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_colIdx&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_value&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employee Salary Report&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeCellStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_font&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeFont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Calibri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_fill&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makePatternFill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;solid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_alignment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeAlignment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                        &lt;span class=&quot;n&quot;&gt;p_horizontal&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                                        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_vertical&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mergeCells&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_range&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;A1:D2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;


    &lt;span class=&quot;n&quot;&gt;v_headerTableId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_rc&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_cursor_from_collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;p_arr_arr&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_trim_rows&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- do not include last row of headers&lt;/span&gt;
                               &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorColOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorRowOffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- leave room for our banner&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1..4&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setTableColumnProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_tableId&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headerTableId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_columnId&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headerCellStyle&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;v_tableId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_query&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;SELECT first_name, last_name, department_name , salary
FROM hr.employees e
INNER JOIN hr.departments d
    ON d.department_id = e.department_id
        AND department_name IN ('Finance', 'Executive')
ORDER BY salary DESC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorTableId&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headerTableId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorPosition&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BOTTOM_LEFT&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorColOffset&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_anchorRowOffset&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setTableHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_tableId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_tableId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headerCellStyle&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_autoFilter&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1..4&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setTableColumnProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_tableId&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_tableId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columnId&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_cellStyle&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- override the column header from query column aliases&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columnName&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- This is at the sheet level&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setColumnFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columnId&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_format&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#,##0.00_);(#,##0.00)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- an accounting number style&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- freeze row headers and first two columns (aka setting active pane)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setSheetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_activePaneAnchorRef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeCellRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_colIdx&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_rowIdx&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;--  ExcelGen.createFile(ctx, 'TEST_DIR', 'multi-row-header.xlsx');&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TMP_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;multi-row-header.xlsx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;closeContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 27 Nov 2022 00:00:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/plsql/2022/11/27/ExcelGen_multirow_headers.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/plsql/2022/11/27/ExcelGen_multirow_headers.html</guid>
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        <category>Excel</category>
        
        
        <category>oracle</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Create a PL/SQL Cursor from a Nested Table of Nested Tables</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;How can we store a generic two dimensional table in PL/SQL, bind it to SQL as a data source and generate
a cursor from it? We have a variable number of columns, so a Record Type is not practical. We’ll explore
some options.&lt;/p&gt;

&lt;h1 id=&quot;a-well-defined-problem&quot;&gt;A Well Defined Problem&lt;/h1&gt;

&lt;p&gt;We have a table with known number of columns and column names. We want to create a structure
that represents spreadsheet column headers for a report that reads from this table. I better
repeat that. We are not discussing a structure for holding the data from the table. We want
a similar structure to hold multiple rows of column header information for a spreadsheet.&lt;/p&gt;

&lt;p&gt;Here is an image of the spreadsheet we will produce.&lt;/p&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;Figure 1 - Multi-Row Spreadsheet Headers Try 1&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/multirowHeaders1.gif&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Rows 3, 4 and 5 are our column header rows. The columns are from the &lt;em&gt;hr.employees&lt;/em&gt; and &lt;em&gt;hr.departments&lt;/em&gt; tables.
The tool we use to generate the spreadsheet requires that we provide a SYS_REFCURSOR or a SQL query string
as an input parameter. We’ll work with a SYS_REFCURSOR here to include bind variables.&lt;/p&gt;

&lt;p&gt;We could generate a SQL query string as:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sys_refcursor&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;SELECT '+', NULL, '+', NULL FROM dual
UNION ALL
SELECT 'First', 'Last', 'Department', 'Yearly' FROM dual
UNION ALL
SELECT 'Name', 'Name', 'Name', 'Salary' FROM dual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I’m less than impressed. We could create a pair of public types in our package (declared in the package
specification so that the SQL engine can see it):&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_header_rec&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;RECORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yearly_salary&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_arr_header_rec&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_header_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then we can assign the data to it like so in our procedure declaration:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;t_arr_header_rec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_arr_header_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;t_header_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t_header_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Department&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Yearly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t_header_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And open a cursor that reads from it:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sysrefcursor&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;SELECT
        first_name, last_name, department_name, yearly_salary
FROM TABLE(:header_table)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;blockquote&gt;
  &lt;p&gt;There is no &lt;em&gt;ORDER BY&lt;/em&gt; here. We are depending on the implementation to provide the rows
in the same order as the collection. It seems a pretty safe bet, but I have searched long and
hard for documentation that makes a promise on this. Oracle pulled the rug out from under us once
before when we were dependent on the implementation of &lt;em&gt;GROUP BY&lt;/em&gt; via a sort. Tom Kyte wrote
adamantly that the only way you can guarantee the order of records is to use an &lt;em&gt;ORDER BY&lt;/em&gt; clause.
Yet I cannot think of a reason why the current implementation providing the records in the same
order as the collection would change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;a-generic-solution&quot;&gt;A Generic Solution&lt;/h1&gt;

&lt;p&gt;I want to refactor my larger problem of providing a data structure containing the header row
content for any spreadsheet, not just this one report. We will not have a Record Type with
a fixed number of columns. For each row we need a nested table of strings. We’ll define
a schema level collection Object:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You probably already have one of those available to you in your own schema or perhaps a
common schema. Oracle ships with several of them.&lt;/p&gt;

&lt;p&gt;We also need a nested table of those puppies to build our two dimensional structure in our
PL/SQL program:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now our procedure declaration becomes:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Department&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Yearly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;There is a glitch in the matrix though. How do we extract the individual elements of each row in the SQL
statement for the cursor? Here is one way.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note the careful use of table aliases. Object access in SQL has a quirk
where an alias for the table or Common Table Expression (CTE) name is required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One triksie part here is the join in the CTE named &lt;em&gt;b&lt;/em&gt;. We are joining to a column in our
rowset from CTE named &lt;em&gt;a&lt;/em&gt;. Think that through. For each row from &lt;em&gt;a&lt;/em&gt;, we treat the column
in that row like it is a table. When we join to it we are essentially unpivoting that column
into multiple rows, one for each value in the array named &lt;em&gt;col&lt;/em&gt;. CTE &lt;em&gt;c&lt;/em&gt; has a separate
record for every column on every line of our headers. We rank them in the order they
came out of the collection so we can pivot them in the final select.&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;-- v_sql declared as a CLOB&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;WITH a AS (
        SELECT rownum AS rn, t.COLUMN_VALUE AS rec -- rec is a nested table arr_varchar2_udt
        FROM TABLE(:header_table) t
    )
    , b AS (
        SELECT rn, rownum AS crn, c.COLUMN_VALUE AS col
        FROM a t
        INNER JOIN TABLE(t.rec) c
            ON 1=1
    )
    , c AS ( -- could I have done math with rn and crn instead? not easily.
        SELECT rn, ROW_NUMBER() OVER (PARTITION BY rn ORDER BY crn) AS cn, col
        FROM b
    ) SELECT &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- create a pivot aggregate for each column&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l_comma&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_comma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;
                MAX(CASE WHEN cn = &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; THEN col END) AS c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;l_comma&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;
        FROM c
        GROUP BY rn
        ORDER BY rn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sysrefcursor&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once again we are depending on Oracle to provide the rows in the same order as the collection.
I’m hoping I’m retired before that changes. Yeah, yeah, you think I’m worried about a boogeyman
that will never pop out from under the bed. Told ya, I’ve seen it before.&lt;/p&gt;

&lt;p&gt;With some formatting to make it easier to see the output, here is what I ran
in &lt;em&gt;sqlplus&lt;/em&gt; for a test:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pagesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimspool&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c4&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;REFCURSOR&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Department&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Yearly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--v_sysrefcursor SYS_REFCURSOR;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;WITH a AS (
        SELECT rownum AS rn, t.COLUMN_VALUE AS rec -- rec is a nested table arr_varchar2_udt
        FROM TABLE(:header_table) t
    )
    , b AS (
        SELECT rn, rownum AS crn, c.COLUMN_VALUE AS col
        FROM a t
        INNER JOIN TABLE(t.rec) c
            ON 1=1
    )
    , c AS ( -- could I have done math with rn and crn instead? not easily.
        SELECT rn, ROW_NUMBER() OVER (PARTITION BY rn ORDER BY crn) AS cn, col
        FROM b
    ) SELECT &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- create a pivot aggregate for each column&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l_comma&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_comma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;
                MAX(CASE WHEN cn = &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; THEN col END) AS c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;l_comma&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;
        FROM c
        GROUP BY rn
        ORDER BY rn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And the output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SQL&amp;gt; @x.sql
PL/SQL procedure successfully completed.

+                                                 +
First                    Last                     Department               Yearly
Name                     Name                     Name                     Salary

SQL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It works. It’s ugly. Can we make it better?&lt;/p&gt;

&lt;h1 id=&quot;refactor-using-a-with-clause-plsql-function&quot;&gt;Refactor Using a WITH Clause PL/SQL Function&lt;/h1&gt;

&lt;p&gt;In PL/SQL we can get the value of a collection element
with the syntax &lt;em&gt;v_arr(i)&lt;/em&gt; where &lt;em&gt;i&lt;/em&gt; is the index value. There is no equivalent syntax
in SQL. That is why we had to do the self-join in the example above.&lt;/p&gt;

&lt;p&gt;Rather than self-joining to our collection of columns to break them into separate rows, 
then pivoting them back into columns afterwards, let’s give ourselves a Function
that we can call in SQL to do it. We could define the function in a package or standalone,
but as of Oracle 12.1 we can do it in-line with our SQL.&lt;/p&gt;

&lt;p&gt;I like this much better than the convolutions above:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pagesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimspool&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c4&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;REFCURSOR&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Department&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Yearly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--v_sysrefcursor SYS_REFCURSOR;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;WITH 
FUNCTION wget(
    p_arr   ARR_VARCHAR2_UDT
    ,p_i    NUMBER
) RETURN VARCHAR2
AS
BEGIN
    RETURN p_arr(p_i);
END;
a AS (
    SELECT rownum AS rn, t.COLUMN_VALUE AS arr 
    FROM TABLE(:header_table) t
)
SELECT wget(a.arr,1) AS c1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, wget(a.arr,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;) AS c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;
FROM a
ORDER BY rn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And the output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SQL&amp;gt; @y.sql

PL/SQL procedure successfully completed.

+                                                 +
First                    Last                     Department               Yearly
Name                     Name                     Name                     Salary

SQL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That looks pretty good. We could use it as-is for any project and be pretty pleased.&lt;/p&gt;

&lt;h1 id=&quot;refactoring-the-refactoring&quot;&gt;Refactoring the Refactoring&lt;/h1&gt;

&lt;p&gt;I like this concept of opening a cursor from a two dimensional collection object enough that I added a
function named &lt;em&gt;get_cursor_from_collections&lt;/em&gt; to my PL/SQL utility package named &lt;em&gt;app_csv_pkg&lt;/em&gt;. You can
find this at on github at &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;plsql_utilities&lt;/a&gt;. There is
a version of &lt;em&gt;get_cursor_from_collections&lt;/em&gt; in the package &lt;em&gt;perlish_util_pkg&lt;/em&gt; too if you are bent that way.&lt;/p&gt;

&lt;p&gt;Also in &lt;em&gt;app_csv_pkg&lt;/em&gt; is a function that will parse a CLOB containing CSV data (like our column headers)
and return an &lt;em&gt;arr_arr_varchar2_udt&lt;/em&gt; object. How handy is that? Let’s see.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pagesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimspool&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c4&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a24&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;REFCURSOR&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/*
    v_arr_headers   arr_arr_varchar2_udt := arr_arr_varchar2_udt(
        arr_varchar2_udt('+',NULL,'+',NULL)
        ,arr_varchar2_udt('First','Last','Department','Yearly')
        ,arr_varchar2_udt('Name','Name','Name','Salary')
    );
*/&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;arr_arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;+,,+,
First,Last,Department,Yearly
Name,Name,Name,Salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;--v_src   SYS_REFCURSOR;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--v_src := app_csv_pkg.get_cursor_from_collections(v_arr_headers);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_cursor_from_collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And the output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PL/SQL procedure successfully completed.
+                                                 +
First                    Last                     Department               Yearly
Name                     Name                     Name                     Salary

SQL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we’re cooking with gas!&lt;/p&gt;

&lt;p&gt;You may already have the column headers in a spreadsheet. I often have them as part of a requirements
document.
Export them as CSV (&lt;em&gt;Save As CSV&lt;/em&gt; might give you an evil windows character at the start of the
file - use Export instead). Now you can copy/paste that file content directly into your
PL/SQL program. It will even handle commas embedded in the fields correctly.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;This seemed like a problem that should not be so hard in PL/SQL, but it wasn’t as easy as one would like.
I like where this wound up with some utility methods to make it easier.&lt;/p&gt;
</description>
        <pubDate>Thu, 24 Nov 2022 22:00:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2022/11/24/Cursor_from_Collections.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2022/11/24/Cursor_from_Collections.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Versioning Oracle Code from Open Source</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;My team have a moderately large installed base of Oracle packages that 
use &lt;a href=&quot;https://github.com/mbleron/ExcelGen&quot;&gt;Marc Bleron’s ExcelGen&lt;/a&gt; package.
Version 3 of the package was released recently and we have use cases that can benefit from the new features.
Regression testing the installed base of code as is required under corporate
SDLC policy is not an attractive proposition.&lt;/p&gt;

&lt;p&gt;The strategy we chose is to name the newly changed Oracle objects differently than the existing ones so
that we can have multiple versions installed in production. For example &lt;em&gt;ExcelGen&lt;/em&gt; package will be named &lt;em&gt;ExcelGen_v3&lt;/em&gt;.
This isn’t ideal, but all future uses,
including any time a job is changed and must be regression tested anyway, will use the new version. Eventually,
the existing version will be retired.&lt;/p&gt;

&lt;p&gt;This article describes a technique and a Perl script to facilitate versioning of the changed objects. Full disclosure -
initially I did much of this manually. For this article I cleaned up the Perl script to address oddities I found
during the exercise and to keep my Perl skills from fading away.&lt;/p&gt;

&lt;h1 id=&quot;scenario&quot;&gt;Scenario&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;ExcelGen&lt;/em&gt; is open-source software hosted on &lt;em&gt;GitHub&lt;/em&gt;. Instructions for cloning the repository point out you
should use &lt;em&gt;–recurse-submodules&lt;/em&gt; or else you must copy the two submodules down separately. The use of submodules
is good technique, but complicates our task.&lt;/p&gt;

&lt;p&gt;The repository follows a model with a separate file (or files) for every Oracle object.
The files are also named the same as the Oracle objects except for the file suffix. 
This is best practice for source code control of Oracle artifacts in my opinion, and fortunate for the pattern I followed here.&lt;/p&gt;

&lt;p&gt;We need to identify all files that changed since our production deployment, the objects defined
in those files, and any dependent objects that would be impacted by the compile.&lt;/p&gt;

&lt;h1 id=&quot;identifying-changed-files-in-git&quot;&gt;Identifying Changed Files in Git&lt;/h1&gt;

&lt;p&gt;We could simply run diff commands to identify files that changed, but we have a nice tool in &lt;em&gt;git&lt;/em&gt; that
keeps track of all this. In order to use it
we must know the commit point of the files we already deployed. In my case I know that tag “v2.5.1” represents
the files I deployed from the main &lt;em&gt;ExcelGen&lt;/em&gt; repository. The &lt;em&gt;MSUtilities&lt;/em&gt; repository has no changes in the
last two years, and &lt;em&gt;ExcelCommons&lt;/em&gt; change I last deployed was commit &lt;em&gt;383ff141bffd3bd31ed3054106346a948575469a&lt;/em&gt;.
In git a tag, a branch and a commit are all simply pointers that are interchangeable for identifying 
the state of code at a particular point in the history.&lt;/p&gt;

&lt;p&gt;To identify changed files from the top level directory we run the command:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#git diff --name-only prior_version_tag_or_commit&lt;/span&gt;
git diff &lt;span class=&quot;nt&quot;&gt;--name-only&lt;/span&gt; v2.5.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Discounting the test_cases, samples and resources files, it turns out the only ones impacted in the main repository are&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;plsql/ExcelGen.pkb&lt;/li&gt;
  &lt;li&gt;plsql/ExcelGen.pks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Changing directory to &lt;em&gt;ExcelCommons&lt;/em&gt; we run the command for this submodule:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git diff &lt;span class=&quot;nt&quot;&gt;--name-only&lt;/span&gt; 383ff141bffd3bd31ed3054106346a948575469a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This yields:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;plsql/ExcelTableCell.tps&lt;/li&gt;
  &lt;li&gt;plsql/ExcelTypes.pkb&lt;/li&gt;
  &lt;li&gt;plsql/ExcelTypes.pks&lt;/li&gt;
  &lt;li&gt;plsql/xutl_xlsb.pkb&lt;/li&gt;
  &lt;li&gt;plsql/xutl_xlsb.pks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From this we have a list of changed Oracle objects:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ExcelGen&lt;/li&gt;
  &lt;li&gt;ExcelTableCell&lt;/li&gt;
  &lt;li&gt;ExcelTypes&lt;/li&gt;
  &lt;li&gt;xutl_xlsb&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could simply look at each one with Toad or SqlDeveloper for dependencies, but I felt like playing, so
we’ll let Perl find the dependencies for us while we prepare to change all occurrences of these
four identifiers by adding “_v3” to the end. Note it is not as simple as a global search and replace
with your text editor because these could be sub-parts of larger names that don’t change. For example
the string &lt;em&gt;ExcelTableCell&lt;/em&gt; appears in &lt;em&gt;ExcelTableCellList&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;perl-script-overkill&quot;&gt;Perl Script Overkill&lt;/h1&gt;

&lt;p&gt;At a whopping 125 lines this monster outgrew my immediate need, but it does some things you may find
interesting. The start of the script gathers the arguments. The first argument is the name of the install
script from which it will read the names of the files we would install if this were a fresh install.
After that we provide a version string to add to the object names. In this case I provided “_v3”.
Finally follow the names of the objects that we know need to be versioned as a seed. The script may add more.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;strict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;warnings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$usage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;USAGE: $0 install_script version_string identifier1 [identifier2..]&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;where 'identifier' is oracle object that you know has changed in the release.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# In each git module and submodule, &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# &quot;git diff --name-only prior_version_tag_or_commit&quot;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# That gives us list of files from which we should be able to name objects &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# we need to version and install that are different than&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# what we already deployed. &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# This script assumes the files are named after the objects they build.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# object_name_version.pl will also check recursively for objects in the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# install script that use changed objects as those need to be versioned too.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;die&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;$0: must provide at least 3 args.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$usage&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@ARGV&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$install_script&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$version_string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# now only idendifiters are left in @ARGV&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@on_re_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%on_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$on&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# we want unique list of lower case object names&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;@on_list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;lc&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$on&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next we create subroutines that we can call multiple times as we identify dependencies. The first creates
regular expressions for each versioned object that we can use both for searching and also
for modifying the code. The parsing of an identifier is something I’ve done before for
the &lt;em&gt;vim&lt;/em&gt; plsql syntax file and also for the &lt;em&gt;Ruby Rouge&lt;/em&gt; plsql lexer.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_on_re_list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# we know the objects that changed. &lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Find those and any files that contain them.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Let us construct an array of regular expressions to match on. Then&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# we can use those same regexp's to substiture the new names.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;STDERR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;calling build_on_re_list&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@on_re_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$identifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%on_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;STDERR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;adding re for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$identifier&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@on_re_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;qr/
    (                                       # Capture before identifier string if any
            ^                               # could be matching at start of line
        |
            [^a-z0-9_#\$]                   # any character that is not an identifier
    )
    (                                       # capture our object word
        $identifier
    )
    (                                       # capture character after our object word
            [^a-z0-9_#\$]                   # any character that is not an identifier
        |
            $                               # could be end of line
    )
/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;imx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ignore case, ignore whitespace in RE, match ^$ at newline&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# the replacement string will be &quot;${1}${2}${version_string}$3&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This subroutine can be called two ways. The first to identify impacted files
and the second option creates new files named with the version string that
we can deploy.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# sub find_files uses these so set before defining&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$install_script_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$install_script_out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$install_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;s/\.sql/${version_string}.sql/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;OFL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$install_script_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;die&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;failed open &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$install_script_out&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; for write: $!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_files&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# look through files for references to the objects that changed&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$look_only&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;STDERR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;calling find_files with look_only=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$look_only&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@file_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$install_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;die&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;failed to open install script $!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^@/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;chomp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/@@?(.+)/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;STDERR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;checking &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$fn&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$fn&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;die&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;undef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# slurp mode&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;close&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$match_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$re&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@on_re_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;$match_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$match_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# we did not find any interesting identifiers&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# reduce file name to oracle identifier. no slash or dot, followed&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# by last dot and non-dots to end of string.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$match_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;m!([^./]+)\.[^.]+$!i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
        &lt;span class=&quot;nv&quot;&gt;$on_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;lc&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$look_only&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     
        &lt;span class=&quot;c1&quot;&gt;# if we are done recursively looking for all objects we need to change&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# continue on to making a new version of the files needed.&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# need to change all occurences of versioned objects inside this file&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$re&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@on_re_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;s/$re/${1}${2}${version_string}$3/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# write the versioned file out with new name&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ofname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# new output file name has version string before dot&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ofname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$match_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;s/\.(.*)/${version_string}.$1/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;STDERR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;found changed object names in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${fn}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;. Creating &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$ofname&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ofname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;close&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# add it to the new install script&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OFL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ofname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# find_files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We call the subroutines repeatedly to add to the object lists as dependencies are found. When
we find no new ones we exit the loop and call the version that creates our new files.
We also write out a new install script with only the objects that we change.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# keep looking recursively for more objects we may need to version because&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# of dependencies until we don't find any new ones&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;build_on_re_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;find_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# could add some to on_list&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@on_re_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%on_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Now call it so that it creates the versioned files&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;find_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; 

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;STDERR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;writing &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$install_script_out&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;close&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OFL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h1 id=&quot;execute&quot;&gt;Execute&lt;/h1&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;C:/Users/Leeli/Documents/ExcelGen] &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;perl &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; object_name_version.pl install.sql _v3 excelgen exceltablecell exceltypes xutl_xlsb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-log&quot;&gt;calling build_on_re_list
adding re for exceltypes
adding re for exceltablecell
adding re for excelgen
adding re for xutl_xlsb
calling find_files with look_only=1
checking MSUtilities/CDFManager/xutl_cdf.pks
checking MSUtilities/CDFManager/xutl_cdf.pkb
checking MSUtilities/OfficeCrypto/xutl_offcrypto.pks
checking MSUtilities/OfficeCrypto/xutl_offcrypto.pkb
checking ExcelCommons/plsql/ExcelTableCell.tps
checking ExcelCommons/plsql/ExcelTableCellList.tps
checking ExcelCommons/plsql/ExcelTypes.pks
checking ExcelCommons/plsql/ExcelTypes.pkb
checking ExcelCommons/plsql/xutl_xlsb.pks
checking ExcelCommons/plsql/xutl_xlsb.pkb
checking plsql/ExcelGen.pks
checking plsql/ExcelGen.pkb
calling build_on_re_list
adding re for exceltablecelllist
adding re for excelgen
adding re for xutl_xlsb
adding re for exceltypes
adding re for exceltablecell
calling find_files with look_only=1
checking MSUtilities/CDFManager/xutl_cdf.pks
checking MSUtilities/CDFManager/xutl_cdf.pkb
checking MSUtilities/OfficeCrypto/xutl_offcrypto.pks
checking MSUtilities/OfficeCrypto/xutl_offcrypto.pkb
checking ExcelCommons/plsql/ExcelTableCell.tps
checking ExcelCommons/plsql/ExcelTableCellList.tps
checking ExcelCommons/plsql/ExcelTypes.pks
checking ExcelCommons/plsql/ExcelTypes.pkb
checking ExcelCommons/plsql/xutl_xlsb.pks
checking ExcelCommons/plsql/xutl_xlsb.pkb
checking plsql/ExcelGen.pks
checking plsql/ExcelGen.pkb
calling find_files with look_only=0
checking MSUtilities/CDFManager/xutl_cdf.pks
checking MSUtilities/CDFManager/xutl_cdf.pkb
checking MSUtilities/OfficeCrypto/xutl_offcrypto.pks
checking MSUtilities/OfficeCrypto/xutl_offcrypto.pkb
checking ExcelCommons/plsql/ExcelTableCell.tps
found changed object names in ExcelCommons/plsql/ExcelTableCell.tps. Creating ExcelCommons/plsql/ExcelTableCell_v3.tps
checking ExcelCommons/plsql/ExcelTableCellList.tps
found changed object names in ExcelCommons/plsql/ExcelTableCellList.tps. Creating ExcelCommons/plsql/ExcelTableCellList_v3.tps
checking ExcelCommons/plsql/ExcelTypes.pks
found changed object names in ExcelCommons/plsql/ExcelTypes.pks. Creating ExcelCommons/plsql/ExcelTypes_v3.pks
checking ExcelCommons/plsql/ExcelTypes.pkb
found changed object names in ExcelCommons/plsql/ExcelTypes.pkb. Creating ExcelCommons/plsql/ExcelTypes_v3.pkb
checking ExcelCommons/plsql/xutl_xlsb.pks
found changed object names in ExcelCommons/plsql/xutl_xlsb.pks. Creating ExcelCommons/plsql/xutl_xlsb_v3.pks
checking ExcelCommons/plsql/xutl_xlsb.pkb
found changed object names in ExcelCommons/plsql/xutl_xlsb.pkb. Creating ExcelCommons/plsql/xutl_xlsb_v3.pkb
checking plsql/ExcelGen.pks
found changed object names in plsql/ExcelGen.pks. Creating plsql/ExcelGen_v3.pks
checking plsql/ExcelGen.pkb
found changed object names in plsql/ExcelGen.pkb. Creating plsql/ExcelGen_v3.pkb
writing install_v3.sql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resulting install script “install_v3.sql” is&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelCommons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plsql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelTableCell_v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tps&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelCommons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plsql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelTableCellList_v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tps&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelCommons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plsql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelTypes_v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pks&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelCommons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plsql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelTypes_v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkb&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelCommons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plsql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xutl_xlsb_v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pks&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelCommons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plsql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xutl_xlsb_v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkb&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plsql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelGen_v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pks&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plsql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExcelGen_v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This install assumes other base objects were already installed and are neither changed nor impacted.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;This is overkill for the task. The regular expression for replacing object names is super useful, but
much of the rest of this task could have been (and was) completed manually this time. I can see a day when
it would be a much bigger effort though, and the exercise was useful to maintain my skills. Maybe you will
see something useful to you.&lt;/p&gt;

</description>
        <pubDate>Sun, 13 Nov 2022 10:00:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/perl/2022/11/13/version_db_objects.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/perl/2022/11/13/version_db_objects.html</guid>
        
        <category>oracle</category>
        
        <category>perl</category>
        
        
        <category>oracle</category>
        
        <category>perl</category>
        
      </item>
    
      <item>
        <title>Tuning Query with OR Conditions in a NOT EXISTS</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;While tuning an Oracle query provided by a business user I ran into a roadblock that confounded me. The query contained
a &lt;em&gt;NOT EXISTS&lt;/em&gt; clause against a large row set, one that was a Common Table Expression (CTE or WITH clause)
that was relatively complex. The CTE plan was fine, but it resulted in about a million rows. An ideal plan
to satisfy the &lt;em&gt;NOT EXISTS&lt;/em&gt; from my perspective
would use a &lt;em&gt;HASH JOIN ANTI&lt;/em&gt; against that row set. That was not happening. It was doing a &lt;em&gt;FILTER&lt;/em&gt; operation.
I’m not 100% sure what Oracle is doing under the covers on the &lt;em&gt;FILTER&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;problem&quot;&gt;Problem&lt;/h1&gt;

&lt;p&gt;The problem SQL is too complex and intertwined with my client’s business to reproduce here, but pseudo code is
good enough for the discussion.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;lookup_value&lt;/span&gt; 
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;large_result_set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_fancy_condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;--+ cardinality(a 1000000)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;my_key_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_lookup_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column_list&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;another_large_result_set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; 
    &lt;span class=&quot;c1&quot;&gt;-- this is done using an index one by one which is ok&lt;/span&gt;
    &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_other_table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_key_value&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- below is the problem antijoin&lt;/span&gt;
    &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_lookup_name&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- this DECODE represents an OR condition&lt;/span&gt;
            &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DECODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_key_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;cm&quot;&gt;/* --I would prefer this syntax
                AND (ne.lookup_value = '*' OR ne.lookup_value = a.my_key_value)
            */&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The picture below (Toad tree view) from the actual plan shows a &lt;em&gt;FILTER&lt;/em&gt; operation with 3 components - &lt;em&gt;another_large_result_set&lt;/em&gt;,
&lt;em&gt;some_other_table&lt;/em&gt;, and &lt;em&gt;ne&lt;/em&gt;. We can tell from the fact that it shows the index lookup, that it is doing a 
one by one index probe of &lt;em&gt;some_other_table&lt;/em&gt;. Less obvious what it is doing with &lt;em&gt;ne&lt;/em&gt;, but in the absence of other
evidence, we must assume it is walking through the heap in memory looking for a match. Maybe it has sorted it and is 
doing something smarter than that, but it is not a hash.&lt;/p&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;Figure 1 - Explain Plan of Filter Operation for Antijoin with OR&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/hash_aj_or_1.png&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Why could it not hash the &lt;em&gt;ne&lt;/em&gt; row set and probe it with a &lt;em&gt;HASH JOIN ANTI&lt;/em&gt;? Because you cannot hash an &lt;em&gt;OR&lt;/em&gt; condition.&lt;/p&gt;

&lt;p&gt;If it is a regular join instead of an anti-join that has this &lt;em&gt;OR&lt;/em&gt; condition, the optimizer can
break the problem into multiple &lt;em&gt;HASH JOIN&lt;/em&gt;’s and do
a &lt;em&gt;CONCATENATION&lt;/em&gt; operation of the results for each of the &lt;em&gt;OR&lt;/em&gt; conditions.&lt;/p&gt;

&lt;p&gt;I do not recall seeing a plan where the optimizer chooses two separate &lt;em&gt;JOIN ANTI&lt;/em&gt; operations in series. 
It would not be a &lt;em&gt;CONCATENATION&lt;/em&gt;
operation between them, but the opposite of that because they are anti-joins. 
I don’t think the Oracle optimizer has an operation for splitting an &lt;em&gt;JOIN ANTI&lt;/em&gt; into two &lt;em&gt;JOIN ANTI&lt;/em&gt;’s in sequence.&lt;/p&gt;

&lt;h1 id=&quot;solution&quot;&gt;Solution&lt;/h1&gt;

&lt;p&gt;I first thought maybe the &lt;em&gt;DECODE&lt;/em&gt; function on the value, being a function, was confusing the optimizer.
Rewriting that in my preferred syntax with an &lt;em&gt;OR&lt;/em&gt; condition rather than the implied &lt;em&gt;OR&lt;/em&gt; of the &lt;em&gt;DECODE&lt;/em&gt;
did not solve the issue. It was a long shot given my understanding of the optimizer, but I don’t know everything,
the optimizer is constantly evolving, and I wanted to give it the best chance to solve the issue.&lt;/p&gt;

&lt;p&gt;I’ve seen this before on regular joins with a similar construct of a lookup table having wild cards. In that
case it was multiple conditions in the join with wild cards optional on all of them. The optimizer
was overwhelmed and just did a giant filter as we saw with this anti-join. One would think it might
be able to break the problem into multiple joins with concatenation, but the multiple join conditions
seems to have exceeded the optimizer’s breadth of options.&lt;/p&gt;

&lt;p&gt;I recall having to break the problem down into two
separate joins manually, one for the wild card and one for the direct match of the key, at least for
the first of multiple wild-carded join conditions. That at least was able to reduce the join set
to one that could be filtered for the remaining conditions.&lt;/p&gt;

&lt;p&gt;I tried a similar trick here rewriting the query as two separate &lt;em&gt;NOT EXISTS&lt;/em&gt; with an &lt;em&gt;AND&lt;/em&gt; 
condition between them.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;lookup_value&lt;/span&gt; 
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;large_result_set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_fancy_condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;--+ cardinality(a 1000000)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;my_key_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_lookup_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column_list&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;another_large_result_set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; 
    &lt;span class=&quot;c1&quot;&gt;-- this is done using an index one by one which is ok&lt;/span&gt;
    &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- not exists 0&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_other_table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_key_value&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- not exists 1&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_lookup_name&lt;/span&gt;
            &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- not exists 2&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_lookup_name&lt;/span&gt;
            &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; 
            &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_key_value&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;Figure 2 - Explain Plan of Filter Operation for Antijoin no OR&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/hash_aj_or_2.png&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;That worked swimmingly. The optimizer hashed all of the rows from &lt;em&gt;ne&lt;/em&gt; with a lookup value of ‘*’,
also hashed all of the rows from &lt;em&gt;ne&lt;/em&gt; with at lookup value that was not ‘*’, and did a &lt;em&gt;HASH JOIN ANTI&lt;/em&gt;
against each in pipelined series. This was a much more efficient plan that will also scale well as
the number of rows increases over time. The actual run time was about a third what it was before the re-write.
Honestly I expected better than that, so maybe Oracle is doing some form of optimization on that filter
operation under the covers.&lt;/p&gt;

&lt;p&gt;It is still showing the antijoin of &lt;em&gt;some_other_table&lt;/em&gt; as a &lt;em&gt;FILTER&lt;/em&gt;. I do not know why this presents as
a filter rather than a &lt;em&gt;NESTED LOOP JOIN ANTI&lt;/em&gt;. I’ve seen nested loop anti-join be shown by the optimizer 
rather than filter in other situations and am unsure what the difference is. I went trolling through
Jonathan Lewis’s fine book &lt;em&gt;Cost-Based Oracle Fundamentals&lt;/em&gt;, but did not find an example of a plan
using &lt;em&gt;FILTER&lt;/em&gt;. It may be something that Oracle added after 10g which is the release the book covered.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;When dealing with &lt;em&gt;OR&lt;/em&gt; join conditions (&lt;em&gt;IN&lt;/em&gt; lists are &lt;em&gt;OR&lt;/em&gt; conditions too), 
there is only so much the optimizer can do. When you are
getting an unacceptable plan for a query with &lt;em&gt;OR&lt;/em&gt;s in the join conditions, or especially anti-join conditions,
consider how you might be able to rewrite the query with two joins, one for each of the &lt;em&gt;OR&lt;/em&gt;s.&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Oct 2022 02:00:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/2022/10/09/antijoins_with_or.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/2022/10/09/antijoins_with_or.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>antijoin</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
      </item>
    
      <item>
        <title>Manipulating XLSX Spreadsheets in PL/SQL</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;There are many common methods for inputting and outputting spreadsheet data from Oracle. Below are two limited lists 
for talking purposes. You may well have others.&lt;/p&gt;

&lt;p&gt;Our use case is to supplement the data in a user maintained spreadsheet using data from an Oracle database. We must
as faithfully as possible maintain the existing data in the spreadsheet while adding new columns.&lt;/p&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;app_read_xlsx_udt Use Case Diagram&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/spreadsheet_input_use_case.gif&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;One limitation shared by common XLSX/Oracle methods is the concept that a column may contain only one datatype. 
A date column contains only dates or NULLs. A numeric column contains only numbers or NULLS. Yet, spreadsheet
data cells may contain any kind of data and formatting can be specific to the cell. We traditionally
think of columns in the spreadsheet of being a single datatype and formatting is typically accomplished at the
column level, but our intrepid business user may place a string such as “Terminated” or “N/A” 
in a column that otherwise has Date values.&lt;/p&gt;

&lt;p&gt;If we were tasked with replacing their entire business process, we would of course normalize this data and separate
the non-date content into a separate column; however, our short term task is to supplement the data the business
uses in an existing process. We do not control their process or their data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can we maintain the polymorphic cell content of the spreadsheet and replicate it on output?&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;content&quot;&gt;Content&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#example-methods-for-manipulating-xlsx-with-oracle&quot;&gt;Example Methods for Manipulating XLSX with Oracle&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#input-spreadsheet-to-oracle&quot;&gt;Input Spreadsheet to Oracle&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#output-spreadsheet-from-oracle&quot;&gt;Output Spreadsheet from Oracle&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#option-elimination&quot;&gt;Option Elimination&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#a-microsoft-option&quot;&gt;A Microsoft Option&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#oracle-specific-options&quot;&gt;Oracle Specific Options&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#excelgen-and-anydata-columns&quot;&gt;ExcelGen and ANYDATA Columns&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#app_read_xlsx&quot;&gt;app_read_xlsx&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#overview&quot;&gt;Overview&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#considerations&quot;&gt;Considerations&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#technique&quot;&gt;Technique&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#code-dive&quot;&gt;Code Dive&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;example-methods-for-manipulating-xlsx-with-oracle&quot;&gt;Example Methods for Manipulating XLSX with Oracle&lt;/h1&gt;

&lt;h2 id=&quot;input-spreadsheet-to-oracle&quot;&gt;Input Spreadsheet to Oracle&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;An ODBC connection from Excel to insert spreadsheet content into Oracle table - perhaps using a VB Macro&lt;/li&gt;
  &lt;li&gt;Save as CSV and load with external table or sqlldr&lt;/li&gt;
  &lt;li&gt;Toad, SQL Developer or other client tool to read xlsx and extract into an Oracle table&lt;/li&gt;
  &lt;li&gt;An ETL tool like Informatica which can read XLSX&lt;/li&gt;
  &lt;li&gt;A PL/SQL tool that can parse XLSX such as &lt;a href=&quot;https://technology.amis.nl/languages/oracle-plsql/read-a-excel-xlsx-with-plsql/&quot;&gt;Anton Scheffer’s as_read_xlsx&lt;/a&gt; or &lt;a href=&quot;https://github.com/mbleron/ExcelTable&quot;&gt;Marc Bleron’s ExcelTable&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;output-spreadsheet-from-oracle&quot;&gt;Output Spreadsheet from Oracle&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;An ODBC connection from Excel to read a query resultset - perhaps using a VB Macro&lt;/li&gt;
  &lt;li&gt;Output a CSV text file from Oracle and open with Excel&lt;/li&gt;
  &lt;li&gt;Toad, SQL Developer or other client tool generate an XLSX file from a query resultset&lt;/li&gt;
  &lt;li&gt;An ETL tool like Informatica which can generate XLSX&lt;/li&gt;
  &lt;li&gt;A PL/SQL tool to generate XLSX directly in the database like &lt;a href=&quot;https://github.com/mbleron/ExcelGen&quot;&gt;Marc Bleron’s ExcelGen&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Python xlsxwriter&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;option-elimination&quot;&gt;Option Elimination&lt;/h1&gt;

&lt;h2 id=&quot;a-microsoft-option&quot;&gt;A Microsoft Option&lt;/h2&gt;

&lt;p&gt;The most full featured option for this use case is the Microsoft Excel library available from Visual Basic or C#.
One can create a macro directly inside the spreadsheet using an ODBC connection to the database to perform the task. Limitations
are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The user’s personal Oracle login must have access to all needed queries and functions. Since the user’s personal login is not tracked as an IT asset, care must be taken to encapsulate all necessary access via appropriate roles. Even so, maintenance of user accounts and associating the correct roles to include access needed by this spreadsheet becomes a burden.&lt;/li&gt;
  &lt;li&gt;The code lives on a User’s desktop or maybe a shared drive. Perhaps the gold copy could be kept under source code control and made available to the users, but it doesn’t really fit well with common SDLC practices. The business could also change the spreadsheet parts that are not related to the data pull. At that point it is out of sync with the IT maintained copy.&lt;/li&gt;
  &lt;li&gt;When the user experiences problems, the first thing IT must question is whether the user is employing the latest version of the code.&lt;/li&gt;
  &lt;li&gt;The expertise to perform this task, though not uncommon, is not necessarily in the toolbox for a journeyman Oracle developer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An extension of this option may be to use a Sql Server database (or whatever it is called now) to host the operation on the 
spreadsheet. I have not done this and do not know limitations, 
but my understanding is these Excel libraries should be available to code that is run from the database. DBLink accounts
that can connect to the Oracle database can be maintained for this server and the code can be put under source code control.
This added complexity does not appeal to me, but a shop who already run Sql Server databases, especially those supporting
batch operations and/or existing user interaction screens that include file transfer may favor this option.&lt;/p&gt;

&lt;h2 id=&quot;oracle-specific-options&quot;&gt;Oracle Specific Options&lt;/h2&gt;

&lt;p&gt;As mentioned, most of the listed options stumble when faced with a column that can contain cells with different data types.
We can reduce everything to text, but then when we output to a spreadsheet again, we’ve lost information and our
business partner is not pleased. This eliminates using CSV or a client like Toad, SqlDeveloper or Informatica to deliver the data
to the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We are left with &lt;em&gt;as_read_xlsx&lt;/em&gt; or &lt;em&gt;ExcelTable&lt;/em&gt; for the input, both of which can deliver the raw
spreadsheet data at the cell level with data type intact.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Assuming we can maintain the polymorphic content information about individual cells on input, we face the same
limitation on output. A tool like Toad, SQL Developer or Informatica reads resultsets from queries. I do not believe
any of them have been enhanced to support the Oracle &lt;em&gt;ANYDATA&lt;/em&gt; object type so we are restricted to cell data that is
either character, date or number.&lt;/p&gt;

&lt;p&gt;I mention Python xlsxwriter, but I really don’t know if it has such a capability. What I have seen of it in practice
is that it takes a resultset from a query similarly to the others.&lt;/p&gt;

&lt;p&gt;I set up this straw man with a non-exhaustive list of options, so there may well be another choice out there. I would love
to hear about it if so. The choice I made is to &lt;strong&gt;use &lt;em&gt;ExcelGen&lt;/em&gt; to create the output spreadsheet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Of the two possible input tools, &lt;em&gt;ExcelTable&lt;/em&gt; is more sophisticated and already supports providing the raw
cell data as &lt;em&gt;ANYDATA&lt;/em&gt; values. It has other functions that return &lt;em&gt;ANYDATASET&lt;/em&gt; results which is fantastic, but
these require you to provide the column type and header information in your code. I badly wanted to use the first row of
the spreadsheet for the column headers and do not want to specify the data type. 
I considered adding functionality to do so, but was not encouraged. Perhaps I did not
sell it well enough.&lt;/p&gt;

&lt;p&gt;Although I wanted to use &lt;em&gt;ExcelTable&lt;/em&gt;, the more I looked into it the bigger the sinking feeling I had that
it was beyond a level of complexity I felt I could leave with my employer. Without community support it was not an option.&lt;/p&gt;

&lt;p&gt;By process of elimination we are left with &lt;em&gt;as_read_xlsx&lt;/em&gt; which is a perfectly serviceable tool that is
in widespread use.&lt;/p&gt;

&lt;h1 id=&quot;excelgen-and-anydata-columns&quot;&gt;ExcelGen and ANYDATA Columns&lt;/h1&gt;

&lt;p&gt;The currently published version of &lt;em&gt;ExcelGen&lt;/em&gt; does not support &lt;em&gt;ANYDATA&lt;/em&gt; data type input columns. I created
a fork and Pull Request adding this functionality, but was not aware that Marc was in the midst of a refactor/redesign
of &lt;em&gt;ExcelGen&lt;/em&gt; and he politely declined the PR. He agreed that allowing &lt;em&gt;ANYDATA&lt;/em&gt; input columns was useful and
he liked the idea of supporting it. I’ll call that a soft commitment. If the next version of &lt;em&gt;ExcelGen&lt;/em&gt; does not
support &lt;em&gt;ANYDATA&lt;/em&gt; input, I’ll create another pull request to add it. I’m fairly confident it will be included
in a future release one way or another.&lt;/p&gt;

&lt;p&gt;Meanwhile, to implement the solution discussed here you will need to use &lt;a href=&quot;https://github.com/lee-lindley/ExcelGen/tree/anydata&quot;&gt;my forked version of ExcelGen&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;app_read_xlsx&quot;&gt;app_read_xlsx&lt;/h1&gt;

&lt;p&gt;Although it uses &lt;em&gt;as_read_xlsx&lt;/em&gt; as the underlying workhorse, &lt;a href=&quot;https://github.com/lee-lindley/app_read_xlsx&quot;&gt;app_read_xlsx&lt;/a&gt; 
takes care of many of the details needed
to treat a spreadsheet as a row source, using the first row data values as the column names and output column headers.&lt;/p&gt;

&lt;p&gt;The documentation found in the README.md at the above link discusses some of what is covered in this article. It
also contains what I think is a decent example showing the problem and the solution. I am not going to repeat those
here but am going to borrow the &lt;em&gt;How it Works&lt;/em&gt; section from that document.&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;The input datastream from &lt;em&gt;as_read_xlsx&lt;/em&gt; is a table of cell data. The ordinal row and column numbers of the spreadsheet are columns/attributes in this data stream.&lt;/li&gt;
  &lt;li&gt;Empty cells are not present in the data.&lt;/li&gt;
  &lt;li&gt;The concept of column headers and database identifiers for the columns is not present in this structure.&lt;/li&gt;
  &lt;li&gt;Each cell is represented with a polymorphic structure containing a &lt;em&gt;cell_type&lt;/em&gt; attribute and a value in one of the attributes &lt;em&gt;string_val&lt;/em&gt;, &lt;em&gt;number_val&lt;/em&gt;, &lt;em&gt;date_val&lt;/em&gt;, &lt;em&gt;formula&lt;/em&gt;. &lt;em&gt;formula&lt;/em&gt; is out of scope for this implementation. Our design pattern converts this polymorphic structure into an Oracle &lt;em&gt;ANYDATA&lt;/em&gt; object type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Presenting the cell data in a two dimensional standard database pattern requires&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;extract column identifiers and number of columns from the first row of the input data&lt;/li&gt;
  &lt;li&gt;pivot spreadsheet columns into rows&lt;/li&gt;
  &lt;li&gt;densify the missing/empty cells&lt;/li&gt;
  &lt;li&gt;convert multi-attribute polymorphic cell representation into &lt;em&gt;ANYDATA&lt;/em&gt; objects&lt;/li&gt;
  &lt;li&gt;present the &lt;em&gt;ANYDATA&lt;/em&gt; cell objects in standard database TABLE structure with rows and columns named from the spreadsheet column headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doing this requires a runtime determination of the resultset type. It is not difficult to do this for a PL/SQL cursor
as we can use a weakly typed SYS_REFCURSOR. It is much harder to present the results to the SQL engine in a way
that the resultset may be joined and extended.&lt;/p&gt;

&lt;h2 id=&quot;considerations&quot;&gt;Considerations&lt;/h2&gt;

&lt;p&gt;When one hears the term &lt;strong&gt;polymorphic resultset&lt;/strong&gt;, we instantly turn to the cool new Oracle toy (well, new as of Oracle 18c)
of &lt;strong&gt;Polymorphic Table Functions&lt;/strong&gt;. Unfortunately, this design pattern only supports standard Oracle datatypes. Object
types such as &lt;em&gt;ANYDATA&lt;/em&gt; are not supported, at least as of Oracle 19c.&lt;/p&gt;

&lt;p&gt;Another method for achieving this is the &lt;strong&gt;ANYDATASET&lt;/strong&gt; technique which is built with &lt;strong&gt;ANYTYPE&lt;/strong&gt;. Building these requires
producing ODCI level code, whether in PL/SQL or another compiled language such as Java or C. Although this pattern
can be followed reasonably well at a cookbook level for standard data types with a moderate level of study, 
extending it to handle piece-wise construction of complex object types such as the &lt;em&gt;ANYDATA&lt;/em&gt; objects is non-trivial.
(see &lt;em&gt;ExcelTable.getRows&lt;/em&gt; in &lt;a href=&quot;https://github.com/mbleron/ExcelTable&quot;&gt;ExcelTable&lt;/a&gt; for an example of using &lt;strong&gt;ANYDATASET&lt;/strong&gt;
with standard datatypes.)
This is a level of complexity the author has seldom observed within most corporate IT departments. If there were
community support of this I would be willing, but for this project it exceeds the complexity level with which I’m comfortable
encumbering my current employer.&lt;/p&gt;

&lt;p&gt;The level of complexity I settled on was using a compile time known object type representing a row,
and standard pipelined table function returning a collection of that row object type. This is a well known
and documented technique that should be in the wheelhouse of most journeyman level Oracle practitioners.
The only slightly tricky part I added was the use of a nested table collection inside this object
and an object method named &lt;strong&gt;get&lt;/strong&gt; for extracting members of that nested table in a SQL statement.&lt;/p&gt;

&lt;h2 id=&quot;technique&quot;&gt;Technique&lt;/h2&gt;

&lt;p&gt;We start with a collection object type of &lt;em&gt;ANYDATA&lt;/em&gt; objects.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_anydata_udt&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FORCE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anydata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next we build an object type that can be piped from our table function:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_read_xlsx_row_udt&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FORCE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OBJECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data_row_nr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aa&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;arr_anydata_udt&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MEMBER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_i&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anydata&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BODY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_read_xlsx_row_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;MEMBER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_i&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anydata&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;em&gt;get&lt;/em&gt; method is necessary to access a member
of the nested table collection from within SQL (inside PL/SQL you could just use aa(i)).&lt;/p&gt;

&lt;p&gt;Then to be able to define our pipelined table function we need a nested table type of these elements:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_app_read_xlsx_row_udt&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FORCE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_read_xlsx_row_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our pipelined table function (which is a static method of our main object type &lt;em&gt;app_read_xlsx_udt&lt;/em&gt;) 
can then be declared as:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;STATIC&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_data_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;p_ctx&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_cnt&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_app_read_xlsx_row_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This still leaves the task of generating a SQL select list that turns the collection elements &lt;em&gt;aa.get(i)&lt;/em&gt;
into columns with an identifier based on the first row of the spreadsheet. That is done by calling the &lt;em&gt;get_sql&lt;/em&gt;
method of &lt;em&gt;app_read_xlsx_udt&lt;/em&gt;. It builds a dynamic SQL statement for you that you can then use as part
of a larger application level SQL statement as shown in the examples section and reproduced here.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_row_nr&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data_row_nr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ddata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VALUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- full object, not the object members * would provide&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_read_xlsx_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_data_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;code-dive&quot;&gt;Code Dive&lt;/h2&gt;

&lt;p&gt;The manual page for &lt;em&gt;app_read_xlsx_udt&lt;/em&gt; constructor describes how the output from &lt;em&gt;as_read_xlsx&lt;/em&gt;
is read into a global temporary table (GTT). Subsequent reads of this data for the first row
determine the number of columns and column headers to populate the object attributes during
the constructor call. 
After that we read the row data from the GTT using the static pipelined table function &lt;em&gt;app_read_xlsx_udt.get_data_rows&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This code is a little interesting in how it uses a restricted package ( ACCESSIBLE BY (app_read_xlsx_udt) )
to maintain a set of session specific context numbers allowing for multiple spreadsheets to be read simultaneously
in a single session. The package also implements the call to &lt;em&gt;as_read_xlsx&lt;/em&gt; when it populates the GTT. You
can look at your leisure.&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;#Overview&quot;&gt;Overview&lt;/a&gt; we listed the tasks the function needed to perform in a bullet list. Most
of these tasks are handled in the static pipelined table function &lt;em&gt;app_read_xlsx_udt.get_data_rows&lt;/em&gt; via a cursor
that uses a bind variable for the number of columns we have in our input (gathered during object
constructor call).&lt;/p&gt;

&lt;p&gt;These two bullet items are covered by the first code section:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;densify the missing/empty cells&lt;/li&gt;
  &lt;li&gt;convert multi-attribute polymorphic cell representation into ANYDATA objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, generate a Common Table Expression (CTE or WITH Clause view) consisting of an integer for each column.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;k&quot;&gt;CURSOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_filled_gaps&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cols&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col_nr&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;CONNECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_col_cnt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, grab our data from the GTT, but only the rows and columns of interest.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;this_ctx_cols&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row_nr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col_nr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number_val&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_read_xlsx_gtt&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_ctx&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row_nr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col_nr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_col_cnt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;CASE&lt;/em&gt; statement below examines the data type of the cell and calls the appropriate static
constructor for an &lt;em&gt;ANYDATA&lt;/em&gt; object. Notice the funky method for creating a NULL &lt;em&gt;ANYDATA&lt;/em&gt; object.
If you find a less kludgey method, let me know.&lt;/p&gt;

&lt;p&gt;The PARTITION BY and RIGHT OUTER JOIN are an Oracle technique I do not know the name of for
densifying data. Heck, that could be the official name of the technique. It makes sure that
on any given &lt;em&gt;row_nr&lt;/em&gt; we selected from the GTT, there &lt;strong&gt;will&lt;/strong&gt; be a row in the output for every
column.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ad_cols&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_nr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col_nr&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell_type&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ANYDATA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convertVarchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ANYDATA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convertDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ANYDATA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convertNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ANYDATA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convertVarchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- must have a placeholder for collect&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ad&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;this_ctx_cols&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PARTITION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_nr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- fill gaps for empty cells&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RIGHT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OUTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cols&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col_nr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col_nr&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we are promised a row for every cell, we can use a &lt;em&gt;COLLECT&lt;/em&gt; aggregation function
to build a nested table collection object in SQL. That takes care of pivoting the column
data into a single row per spreadsheet row, if not exactly pivoting into columns yet.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;pivot spreadsheet columns into rows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To complete the cursor query
we plug these values into the default constructor for our &lt;em&gt;app_read_xlsx_row_udt&lt;/em&gt; object described
earlier. That is what our function must PIPE ROW out.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ad_arr&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row_nr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data_row_nr&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;COLLECT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ad&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col_nr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_anydata_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vaa&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ad_cols&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row_nr&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_read_xlsx_row_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_row_nr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vaa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ad_arr&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The rest of the function is simple boilerplate for a pipelined table function:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_filled_gaps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;FETCH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_filled_gaps&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BULK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;COLLECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;EXIT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;PIPE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;CLOSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_filled_gaps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No matter how many columns we have or what our column names are, we have a single compile-time representation
of our resultset coming from this pipelined table function. With a little help from member function &lt;em&gt;app_read_xlsx_udt.get_sql&lt;/em&gt;
we can get our columns out with proper names using regular SQL and use it as if it had come from a table.&lt;/p&gt;

&lt;p&gt;You can look at the code for &lt;em&gt;get_sql&lt;/em&gt; at your leisure, but the example shown in the &lt;a href=&quot;#technique&quot;&gt;Technique&lt;/a&gt; section
and repeated next is the best way to understand it:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_row_nr&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data_row_nr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ddata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VALUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- full object, not the object members * would provide&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_read_xlsx_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_data_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ll mention something that confounds those who have not used Oracle objects often. In order to
access a member method or element of an object you must use a table alias as part of the name. One cannot
rely on an actual table name or an implied (no) table name; it must be a table alias. In this
case the table alias is ‘X’.&lt;/p&gt;

&lt;p&gt;The other thing to mention is the use of the &lt;em&gt;VALUE&lt;/em&gt; function. The most common way we extract data
from a pipelined table function is via &lt;em&gt;SELECT *&lt;/em&gt;. If we do that here, we will get the elements of the &lt;em&gt;app_read_xlsx_row_udt&lt;/em&gt;
object rather than the object itself. We need the &lt;em&gt;get&lt;/em&gt; member method of the object, so it is important
that we retrieve the object intact rather than the object elements. &lt;em&gt;VALUE&lt;/em&gt; gives us the actual object
returned from the pipelined table function rather than the object elements. It’s relatively obscure.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;This turned out to be a much harder problem than I thought it would be when I started. Limitations
of &lt;em&gt;Polymorphic Table Functions&lt;/em&gt; and the complexity of &lt;em&gt;ANYTYPE/ANYDATASET&lt;/em&gt; took me off guard. It would
have been easiest to craft one-off solutions for each spreadsheet by writing a custom cursor, but it
felt like the wrong answer. Creating &lt;em&gt;app_read_xlsx&lt;/em&gt; was the result. It is a bit messier than I would like
in the way it gives you a SQL statement for your program to incorporate, but I feel it is an appropriate
level of abstraction given the requirements and limitations.&lt;/p&gt;

&lt;p&gt;Hope you find both the tool and the journey to get here helpful.&lt;/p&gt;
</description>
        <pubDate>Sun, 18 Sep 2022 07:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/plsql/2022/09/18/spreadsheets_with_plsql.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/plsql/2022/09/18/spreadsheets_with_plsql.html</guid>
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        
        <category>oracle</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Using Perl DBD::Oracle to write LOB content</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;We have a need to get large volumes of data and/or &lt;em&gt;BLOB/CLOB&lt;/em&gt; content out of the database
and onto disk on a remote client machine.&lt;/p&gt;

&lt;p&gt;We can do it from &lt;em&gt;sqlplus&lt;/em&gt;. Even &lt;em&gt;BLOB&lt;/em&gt; data can be output as I demonstrated 
in &lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/2021/12/18/sqlplus-blob.html&quot;&gt;Extracting BLOB from Oracle with Sqlplus&lt;/a&gt;.
I happen to think it is a clunky way to do things when there are nice scripting languages around, 
but the one constant you can count on having available on a client is &lt;em&gt;sqlplus&lt;/em&gt; and some form of &lt;em&gt;shell&lt;/em&gt; script capability,
even if it is &lt;em&gt;Powershell&lt;/em&gt; on Windows.&lt;/p&gt;

&lt;p&gt;If you have access to &lt;em&gt;Pro-C&lt;/em&gt; compilation, there is a nice tool Tom Kyte published long ago named &lt;em&gt;flat&lt;/em&gt;
you can find &lt;a href=&quot;https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:459020243348&quot;&gt;here&lt;/a&gt;.
It is a super-fast and efficient solution.
It doesn’t do &lt;em&gt;CLOB&lt;/em&gt; or &lt;em&gt;BLOB&lt;/em&gt;, but with a little elbow grease I’m sure one could manage it. &lt;em&gt;Pro-C&lt;/em&gt;
has everything you need to handle a &lt;em&gt;LOB Locator&lt;/em&gt; and retrieve the content.&lt;/p&gt;

&lt;p&gt;I know it is doable in Java, and I suspect most other languages that have database connection libraries.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;sqlCL&lt;/em&gt; can do it as described in &lt;a href=&quot;https://www.thatjeffsmith.com/archive/2020/07/using-sqlcl-to-write-out-blobs-to-files-in-20-lines-of-js/&quot;&gt;Using SQLcl to write out BLOBs to files in 20 lines of js&lt;/a&gt;; however, as I’ve mentioned
in other posts, &lt;em&gt;sqlCL&lt;/em&gt; is strangely lacking on every ETL server I’ve had the pleasure of visiting. For that
matter the security folk aren’t crazy about having java client programs on servers. I think they overreacted
to a security issue from many years ago and have never gotten over it, but it is what it is.&lt;/p&gt;

&lt;p&gt;This article is about extracting &lt;em&gt;CSV&lt;/em&gt; flat files, &lt;em&gt;CLOB&lt;/em&gt;’s and &lt;em&gt;BLOB&lt;/em&gt;’s from Oracle using &lt;em&gt;Perl&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;do-you-have-perl-dbdoracle&quot;&gt;Do You Have Perl DBD::Oracle?&lt;/h1&gt;

&lt;p&gt;You may not. It does not ship with any OS I know of other than Oracle’s own version of RHL. Neither
is it available in any &lt;em&gt;YUM&lt;/em&gt; repository I could find, so your Unix Admin is not going to be able to
install it easily. They may balk at doing it at all depending on how strict the rules are under which
they work.&lt;/p&gt;

&lt;p&gt;That said, it has gotten easier as I demonstrated 
in &lt;a href=&quot;https://lee-lindley.github.io/oracle/perl/linux/2022/04/28/Perl-DBD-Oracle-RHL.html&quot;&gt;Installing Perl DBD::Oracle on RHL&lt;/a&gt;.
There is a good chance your Unix Admin isn’t allowed to alter the vendor &lt;em&gt;Perl&lt;/em&gt;. If that is the case you may
need to install your own &lt;em&gt;Perl&lt;/em&gt; and add &lt;em&gt;DBD::Oracle&lt;/em&gt; to that. Perhaps the Unix admin can do that for you.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I don’t mean to make light of this, but if you are going to have a useful scripting environment, whether it be
&lt;em&gt;Perl&lt;/em&gt;, &lt;em&gt;Python&lt;/em&gt;, or something else, at some point the security mavens and corporate rule makers need to give
you one and it needs to have the database connect libraries linked in. “Of course that’s true” you are thinking.
Surprise! You may find the security mavens and IT infrastructure folk are not sympathetic to your need
for a decent scripting environment with a built-in Oracle connection library on the ETL server. At one company I
encountered a mindset that shell and &lt;em&gt;sqlplus&lt;/em&gt;, along with a vendor ETL tool, were all you need. It was a
large, sophisticated organization too. They were responding to pressures from senior management that resulted in
what I would call unintended consequences.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;running-data-through-perl&quot;&gt;Running Data Through Perl&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Perl&lt;/em&gt; is fantastic as a scripting language. But like all scripting languages it has some overhead costs.
It isn’t the bytecode, which is pretty efficient. The overhead is in the data structures which are fat pigs.
If you look at the underlying structure of a &lt;em&gt;Perl&lt;/em&gt; &lt;em&gt;scalar&lt;/em&gt; variable, you will find multiple pointer and length
members. For example it has a place to store an integer, a floating point number, a string, an array, and something
called “magic” among other things.  There is a lot of wasted space for any given scalar object.&lt;/p&gt;

&lt;p&gt;Under normal
circumstances that hardly matters. If you are running a &lt;em&gt;fetchrow_array&lt;/em&gt; operation over a large number of rows
on a big &lt;em&gt;SQL&lt;/em&gt; query, you are creating and tearing down a scalar for every column on every row. It can really
add up.&lt;/p&gt;

&lt;p&gt;Yeah, don’t do that. You would be better off spooling it out from &lt;em&gt;sqlplus&lt;/em&gt;. On the other hand, if you are reading
and writing large chunks of data through Perl, the overhead is negligible. The underlying data movement is
all handled in tight C code and is efficient.&lt;/p&gt;

&lt;h1 id=&quot;using-dbdoracle-to-read-clobblob-data&quot;&gt;Using DBD::Oracle to Read CLOB/BLOB Data&lt;/h1&gt;

&lt;p&gt;In the article &lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/2021/09/10/Ubiquitous-CSV_file.html&quot;&gt;The Ubiquitous CSV File&lt;/a&gt;
I described multiple ways for generating CSV data and getting it out of the database.
One of those ways was for the use case that you were doing it for a business user who
just turns around and loads it to a spreadsheet. You can up your game by producing an &lt;em&gt;XLSX&lt;/em&gt; 
file directly from the database using &lt;a href=&quot;https://github.com/mbleron/ExcelGen&quot;&gt;ExcelGen&lt;/a&gt;. The output
of that is a &lt;em&gt;BLOB&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I also have a CSV file generator in package named &lt;em&gt;app_csv_pkg&lt;/em&gt; available in &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#app_csv_pkg&quot;&gt;plsql_utilities&lt;/a&gt;. I described how it it works in
&lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/2021/12/31/Polymorphic-Table-Functions-3.html&quot;&gt;Polymorphic Table Function (PTF) for CSV (take 3)&lt;/a&gt; The output of that is a &lt;em&gt;CLOB&lt;/em&gt;. Well, you can take the output as single column rows in a fetch, but
you still have to stream the data out somehow.&lt;/p&gt;

&lt;p&gt;Given that you have a procedure that can produce a &lt;em&gt;BLOB&lt;/em&gt; or &lt;em&gt;CLOB&lt;/em&gt; with everything you need to put in the file
on the client, how can we get it from the database? Let’s walk through an example.&lt;/p&gt;

&lt;p&gt;This first part is boilerplate you will have in all of your scripts unless you have 
refactored it into a connection object, perhaps one that handles the password management.&lt;/p&gt;

&lt;p&gt;We need the Oracle Type definitions, thus the extra argument to use DBD::Oracle.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DBI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DBD::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Oracle&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;qw(:ora_types)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;strict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;warnings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DBI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;('&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;dbi:Oracle:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lee@rhl1pdb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;my secret password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;RaiseError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AutoCommit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;RowCacheSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;102400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ora_module_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Perl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;die&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Database connection not made: DBI::errstr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alter session set nls_date_format = 'mm/dd/yyyy'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Next we prepare our &lt;em&gt;PL/SQL&lt;/em&gt; anonymous block. This could have been a call to ExcelGen or your own procedure
that produces a &lt;em&gt;BLOB&lt;/em&gt; or &lt;em&gt;CLOB&lt;/em&gt;. In this case I’m demonstrating with &lt;em&gt;app_csv_pkg&lt;/em&gt; where we pass it a query
to run as a string. It executes the query, fetches the data, converts it into &lt;em&gt;CSV&lt;/em&gt; rows, and concatenates
them into a &lt;em&gt;CLOB&lt;/em&gt;. It then returns the &lt;em&gt;CLOB&lt;/em&gt; as an OUT parameter.&lt;/p&gt;

&lt;p&gt;The local hash with &lt;em&gt;ora_auto_lob&lt;/em&gt; setting to false is so that we get back a &lt;em&gt;LOB&lt;/em&gt; locator rather than
letting the driver convert the &lt;em&gt;CLOB&lt;/em&gt; into one giant string. We might not want to put that much data into 
memory, plus we have to know in advance how big it could be and set some variables to allow for it.
Search the DBD::Oracle perldoc for CLOB and read all about it. Unfortunately, it is a pretty big topic.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$sth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;q!
    BEGIN
        app_csv_pkg.get_clob(
            p_sql           =&amp;gt; :sql
            ,p_clob         =&amp;gt; :clob
            ,p_rec_count    =&amp;gt; :rec_count
        ); 
    END;
!&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ora_auto_lob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# this says pass LOB locator, not entire lob&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Next we create a variable with our query and 2 variables we will bind as OUT parameters.
The bind options are important. The &lt;em&gt;SQLT_CHR&lt;/em&gt; type will convert
a perl string to the proper type for the &lt;em&gt;CLOB&lt;/em&gt; input parameter up to about 2MB in size.
If your query is bigger than that, you are doing something wrong. If you really need to
input a CLOB, read the DBD::Oracle perldoc.&lt;/p&gt;

&lt;p&gt;The output parameter type &lt;em&gt;ORA_CLOB&lt;/em&gt; in this case means we will get back a &lt;em&gt;LOB&lt;/em&gt; locator.
If in the &lt;em&gt;prepare&lt;/em&gt; statement above we had allowed &lt;em&gt;ora_auto_lob&lt;/em&gt; to be the default TRUE,
we would get back a string (as long as it wasn’t too big).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;rec_count&lt;/em&gt; is just a number output parameter. After it runs we can find out how
many records the query returned and were placed in our &lt;em&gt;CLOB&lt;/em&gt;. Useful for logging.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;SELECT * FROM v$reserved_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;';&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$rec_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$sth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ora_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;SQLT_CHR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# This type converts to CLOB on input up to 2MB which is plenty&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$sth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bind_param_inout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ora_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ORA_CLOB&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# will be a CLOB locator&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$sth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bind_param_inout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:rec_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$rec_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$sth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After the &lt;em&gt;execute&lt;/em&gt;, &lt;em&gt;$rec_count&lt;/em&gt; has the number of rows which we can report and &lt;em&gt;$clob&lt;/em&gt; has a &lt;em&gt;LOB&lt;/em&gt;
locator (which is only good to use during this transaction; a &lt;em&gt;commit&lt;/em&gt; or &lt;em&gt;rollback&lt;/em&gt; destroys the underlying &lt;em&gt;LOB&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;Now we write out the data. We are writing to STDOUT here, but you could have opened
a named file and be writing to that.&lt;/p&gt;

&lt;p&gt;Rather than bring back the entire CLOB into one huge chunk of memory, we will read and 
write it in pieces. Each of these is a round trip to the database so do not make them
too small, but neither do we want to use so much memory on both sides of the connection
that it is an issue. Just like where Oracle has a rule of thumb that 100 rows is a good
bulk fetch size, the right answer is probably below 1MB and more than 10K. I don’t really know
where the sweet spot is without some experimentation. Here I picked 256K bytes.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;STDERR&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rec_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; rows returned in clob&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 256K chunks are not that much memory, but still big enough perl scalar creation/destruction not an issue&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$chunk_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# starts at 1, not 0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ora_lob_read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$chunk_size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$chunk_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# to end the transaction concerning temp lob locator and freeing it so perl destructor doesn't complain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I’m not going to print out the results, but this works just fine. My sample query output is not bigger than
the chunk size, so it didn’t loop at first. I had to make it smaller and put in some debug
prints to prove it, but it works.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;I’ve waved my hands around a lot in prior articles saying
you can get &lt;em&gt;CLOB&lt;/em&gt; and &lt;em&gt;BLOB&lt;/em&gt; data out on the client. I showed it
with &lt;em&gt;sqlplus&lt;/em&gt; and &lt;em&gt;sqlCL&lt;/em&gt;. I have used Tom Kyte’s &lt;em&gt;flat&lt;/em&gt; utility for years (though the security mavens
don’t want me near a C compiler and Devops won’t give me a deploy method for it). It was time to put up
some proof that we can do it all with a scripting language efficiently too. Here ya’ go.&lt;/p&gt;

&lt;p&gt;Hope this helps.&lt;/p&gt;
</description>
        <pubDate>Sat, 30 Apr 2022 10:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/perl/2022/04/30/perl_dbd_oracle_clob.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/perl/2022/04/30/perl_dbd_oracle_clob.html</guid>
        
        <category>oracle</category>
        
        <category>perl</category>
        
        <category>DBI</category>
        
        <category>DBD</category>
        
        <category>CLOB</category>
        
        <category>BLOB</category>
        
        
        <category>oracle</category>
        
        <category>perl</category>
        
      </item>
    
      <item>
        <title>Syntax Highlighting for PL/SQL in vim</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;In the blog post &lt;a href=&quot;https://lee-lindley.github.io/plsql/sql/2022/03/20/Ruby-Rouge-Lexer-PLSQL.html&quot;&gt;A Ruby/Rouge Lexer Class for Oracle PL/SQL&lt;/a&gt;
I describe how I wound up creating a PL/SQL lexer in Ruby-Rouge so that I could make the PL/SQL code
blocks in my posts look good.&lt;/p&gt;

&lt;p&gt;I tried to apply my color scheme to &lt;em&gt;vim&lt;/em&gt; but quickly ran into trouble. The PL/SQL syntax file for &lt;em&gt;vim&lt;/em&gt;
had not been updated since Oracle 9i and it had flaws such as not supporting q-quote operator. 
It also did not have folding.&lt;/p&gt;

&lt;p&gt;Previously having done work to get all of the 19c keywords straight, and familiar with what needed to
be done to parse PL/SQL and SQL, I applied what I knew to the existing &lt;em&gt;plsql.vim&lt;/em&gt; file.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It does a good job with Oracle SQL too, not just PL/SQL. &lt;em&gt;sqloracle.vim&lt;/em&gt; works differently (not better or worse), and folding support is simpler. You may be satisifed with &lt;em&gt;sql.vim/sqloracle.vim&lt;/em&gt; for SQL, especially if you write SQL for multiple databases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;from-supplicant-to-maintainer&quot;&gt;From Supplicant to Maintainer&lt;/h1&gt;

&lt;p&gt;I tried to
find the maintainer of the file to offer my work. Sadly, that individual’s email account is no longer
recognized by the provider, and he has not updated anything online in many years.&lt;/p&gt;

&lt;p&gt;I contacted the &lt;em&gt;vim-dev&lt;/em&gt; mailing list asking what to do. Their suggestion was to assume ownership
of the maintainer role. Balking at first, I quickly ran out of excuses I was willing to live with and agreed to do it.
Once committed, I felt obligated to go at it full-bore.&lt;/p&gt;

&lt;p&gt;I spent way more of my evenings trying to understand
the arcane rules of both &lt;em&gt;vim&lt;/em&gt; regular expressions and the API rules for &lt;em&gt;syntax&lt;/em&gt;, especially &lt;em&gt;region&lt;/em&gt;’s, than
I care to quantify. There are limitations that made it exceedingly difficult to do folding the way I initially
envisioned it.&lt;/p&gt;

&lt;h1 id=&quot;vim-plsql-syntax-file&quot;&gt;Vim PL/SQL Syntax File&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/lee-lindley/vim_plsql_syntax&quot;&gt;vim_plsql_syntax&lt;/a&gt; repository now lives on &lt;em&gt;github&lt;/em&gt;. It contains both the 
syntax file &lt;em&gt;plsql.vim&lt;/em&gt; and the colors file &lt;em&gt;lee.vim&lt;/em&gt;. The colors file is completely gratuitous. You don’t need
to use it to take advantage of the updates in &lt;em&gt;plsql.vim&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The repository &lt;em&gt;README.md&lt;/em&gt; is extensive. It includes screenshots and an installation section.&lt;/p&gt;

&lt;p&gt;At this time the &lt;em&gt;main&lt;/em&gt;
branch contains the version submitted to the &lt;em&gt;vim&lt;/em&gt; project for inclusion in version 9 (which should be released
soon for some definition of soon). You should select a more current branch, which although they are works in process, 
are passing my eyeball tests. Right now the branch 
&lt;a href=&quot;https://github.com/lee-lindley/vim_plsql_syntax/tree/procedure_folding&quot;&gt;procedure_folding&lt;/a&gt; 
is stable with some nice new folding features.  It is likely to be merged into &lt;em&gt;main&lt;/em&gt; soon.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;I think I’ve done good work here, but if this is something that interests you, you will have to decide for yourself.
I’m pretty happy with it. I will of course attempt to accomodate suggestions. Submitting a Pull Request is the best
way to get what you want.&lt;/p&gt;

&lt;p&gt;The secondary conclusion is to be careful when you report a problem. It isn’t that uncommon to be told “so fix it.”
I’m apparently a really slow learner on that lesson.&lt;/p&gt;
</description>
        <pubDate>Fri, 29 Apr 2022 12:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/plsql/sql/vim/2022/04/29/vim-plsql-syntax.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/plsql/sql/vim/2022/04/29/vim-plsql-syntax.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>vim</category>
        
        <category>syntax-highlighting</category>
        
        <category>syntax</category>
        
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        <category>sql</category>
        
        <category>vim</category>
        
      </item>
    
      <item>
        <title>Installing Perl DBD::Oracle on RHL</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;Over the years I’ve had many opportunities to struggle with compiling &lt;em&gt;Perl&lt;/em&gt; and &lt;em&gt;DBD::Oracle&lt;/em&gt; on many different
flavors of Unix. It was never easy.&lt;/p&gt;

&lt;p&gt;When package management (yum, etc..) became a thing and I eagerly started looking at it, reality slapped me back down.
Nobody was packaging &lt;em&gt;DBD::Oracle&lt;/em&gt; and the reason seemed to be the Oracle client library license requirements.&lt;/p&gt;

&lt;p&gt;When I went looking to do it all again on my home Linux instances, I was pleasantly surprised to find
&lt;em&gt;DBD::Oracle&lt;/em&gt; was already included on my &lt;strong&gt;Oracle Linux Server release 7.9&lt;/strong&gt; server. Oracle apparently doesn’t
have an issue getting a distribution license for their own dogfood and were able to include &lt;em&gt;DBD::Oracle&lt;/em&gt;. Nice!&lt;/p&gt;

&lt;p&gt;I also have a &lt;strong&gt;Red Hat Enterprise Linux release 8.5 (Ootpa)&lt;/strong&gt; server running under Microsoft &lt;em&gt;Hyper-V&lt;/em&gt;.
In spite of the close business relationship Oracle has with Red Hat, RHL does not have &lt;em&gt;DBD::Oracle&lt;/em&gt; installed,
nor was it found in a &lt;em&gt;dnf&lt;/em&gt; search. Note that I’ve already installed
Oracle Database on this puppy, so I’ve added Oracle repositories. If there is a yum repository out there that
contains DBD::Oracle for Linux, I haven’t found it.&lt;/p&gt;

&lt;p&gt;So here we go again.&lt;/p&gt;

&lt;p&gt;I know before I start that I need my shell environment to be set so that I can connect with sqlplus.
My database SID is &lt;strong&gt;rhl1db&lt;/strong&gt; which is a container database.
This works for me given how I installed Oracle:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ORACLE_HOSTNAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ORAENV_ASK&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;NO
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ORACLE_SID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;rhl1db
&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; oraenv &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;From the README for DBD::Oracle I know I need to set a variable &lt;strong&gt;ORACLE_USERID&lt;/strong&gt; with the connection string.
I could include the @pdbname part there I think, but I also know I can use &lt;strong&gt;TWO_TASK&lt;/strong&gt; to specify
the PDB. Remember my SID is for the Container DB. I’m going to connect to a PDB named &lt;strong&gt;rhl1pdb&lt;/strong&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TWO_TASK&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;rhl1pdb
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ORACLE_USERID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;lee/MYPASSWORD
sqlplus &lt;span class=&quot;nv&quot;&gt;$ORACLE_USERID&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# BINGO! I'm in&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point I’ve taken care of prerequisites in my environment for doing the make. We could download
it and run the steps outlined in the README, but I thought I would swing for the fence.&lt;/p&gt;

&lt;h1 id=&quot;trying-cpan&quot;&gt;Trying CPAN&lt;/h1&gt;

&lt;p&gt;First, I tried to run a cpan shell.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;perl &lt;span class=&quot;nt&quot;&gt;-MCPAN&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; shell
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yeah, we could be so lucky. It wasn’t there, but ahoy! There is a yum package for it.&lt;/p&gt;

&lt;p&gt;I’m running as root. I realize we are supposed to use &lt;em&gt;sudo&lt;/em&gt;. You do you, and leave the old dinosaur alone.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dnf search perl-CPAN
&lt;span class=&quot;c&quot;&gt;# bingo&lt;/span&gt;
dnf &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;perl-CPAN
perl &lt;span class=&quot;nt&quot;&gt;-MCPAN&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; shell
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Terminal does not support AddHistory.

cpan shell -- CPAN exploration and modules installation (v2.18)
Enter 'h' for help.

cpan[1]&amp;gt; install DBD::Oracle
Reading '/root/.local/share/.cpan/Metadata'
    Database was generated on Thu, 28 Apr 2022 07:29:03 GMT
Running install for module 'DBD::Oracle'
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My first attempt the make failed for missing library &lt;em&gt;aio&lt;/em&gt;. A little google search and we find we need to
install &lt;em&gt;libaio-devel&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dnf &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;libaio-devel
perl &lt;span class=&quot;nt&quot;&gt;-MCPAN&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; shell
cpan[1]&amp;gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;DBD::Oracle
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time it compiles, but I run into trouble in the tests. It wants &lt;em&gt;Test::More&lt;/em&gt; and &lt;em&gt;Devel::Peek&lt;/em&gt;. OK, I’ll try to get those.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;perl &lt;span class=&quot;nt&quot;&gt;-MCPAN&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; shell
&lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;Test::More
&lt;span class=&quot;c&quot;&gt;# that worked&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;Devel::Peek
&lt;span class=&quot;c&quot;&gt;# no dice&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Devel::Peek&lt;/em&gt; is tied up somehow. I didn’t try to figure it out. I know that not all tests will pass anyway.
How would you know that? You wouldn’t. It is the source of endless frustration for people new to this.
But if you google install issues for DBD::Oracle you will see the blase response to just ignore the
failed tests (when there are only a few non-critical failures!) and run make install anyway.&lt;/p&gt;

&lt;p&gt;This time when I ran the install from CPAN shell it completed the majority of the tests and I honestly
don’t care about the failures.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Scanning cache /root/.local/share/.cpan/build for sizes
...
Files=41, Tests=1867, 17 wallclock secs ( 0.27 usr  0.05 sys +  3.86 cusr  0.68 csys =  4.86 CPU)
Result: FAIL
Failed 5/41 test programs. 2/1867 subtests failed.
make: *** [Makefile:1105: test_dynamic] Error 2
  ZARQUON/DBD-Oracle-1.83.tar.gz
  /usr/bin/make test -- NOT OK
//hint// to see the cpan-testers results for installing this module, try:
  reports ZARQUON/DBD-Oracle-1.83.tar.gz
Failed during this command:
 ZARQUON/DBD-Oracle-1.83.tar.gz               : make_test NO

cpan[2]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We are left with it not installed though. Bummer. Maybe there is a way to tell the CPAN shell to install even
after a failed test, but I didn’t bother. At the top of the block above note that it tells me it
is doing this make in a cache directory “/root/.local/share/.cpan/build”. I go down that path
and find “DBD-Oracle-1.83-0”. In that directory is the Makefile that was created for this build. I do&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All done.&lt;/p&gt;

&lt;h1 id=&quot;testing-dbdoracle&quot;&gt;Testing DBD::Oracle&lt;/h1&gt;

&lt;p&gt;My test script (sans my database password and notice I’m not root anymore):&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DBI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DBD::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Oracle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;strict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;warnings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DBI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;('&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;dbi:Oracle:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lee@rhl1pdb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;my secret password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;RaiseError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AutoCommit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;RowCacheSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;102400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ora_module_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Perl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;die&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Database connection not made: DBI::errstr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alter session set nls_date_format = 'mm/dd/yyyy'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;selectrow_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;SELECT sysdate from dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&quot;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$dbh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;disconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lee@rhl1 [/home/lee/git/perl_oracle]
$ ./test_dbd_oracle.pl 
04/28/2022
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That may have been the easiest install of &lt;em&gt;DBD::Oracle&lt;/em&gt; I’ve ever done.&lt;/p&gt;

&lt;p&gt;Granted, I had already installed the Oracle database on this server, configured it, and configured
connections (listener, TNS, etc…). I had all that working before I started. If you are on a new
machine you must first install the Oracle client and get that all working. That’s also easier
than it used to be, but no picnic.&lt;/p&gt;

&lt;p&gt;Hope this helped.&lt;/p&gt;
</description>
        <pubDate>Thu, 28 Apr 2022 12:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/perl/linux/2022/04/28/Perl-DBD-Oracle-RHL.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/perl/linux/2022/04/28/Perl-DBD-Oracle-RHL.html</guid>
        
        <category>oracle</category>
        
        <category>perl</category>
        
        <category>installation</category>
        
        <category>linux</category>
        
        <category>DBI</category>
        
        <category>DBD</category>
        
        <category>RHL</category>
        
        
        <category>oracle</category>
        
        <category>perl</category>
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>Profiling PL/SQL to Examine Context Switch Penalty</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;In a prior post, &lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/2022/04/02/Object-Methods-in-SQL.html&quot;&gt;Cost of UDT Object Methods in SQL&lt;/a&gt;, 
I hypothesized a performance issue was due to &lt;strong&gt;Context Switching&lt;/strong&gt; between SQL and PL/SQL engine. Moving
the suspect PL/SQL function call into a PIPELINED Table function solved the issue.&lt;/p&gt;

&lt;p&gt;I had three outstanding doubts about what is really going on.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The magnitude of the performance penalty caught me off gaurd. I’ve remediated context switch issues with 
function calls in a SQL SELECT list before. I haven’t seen one where the impact was this dramatic except
where the PL/SQL function was also calling SQL.&lt;/li&gt;
  &lt;li&gt;I am unsure whether Object type method calls in a SELECT list incur the context switch penalty. I still do not know and this analysis did not answer that question, but it did put the related subject of collection creation/extension cost into perspective.&lt;/li&gt;
  &lt;li&gt;Unrelated, documentation for chained PIPELINED Table functions suggests bulk collect logic is not needed. I wanted to verify that.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;plsql-hierarchical-profiler&quot;&gt;PL/SQL Hierarchical Profiler&lt;/h1&gt;

&lt;p&gt;If you haven’t used the profiler before, this &lt;a href=&quot;https://www.thatjeffsmith.com/archive/2019/02/sql-developer-the-pl-sql-hierarchical-profiler/&quot;&gt;Jeff Smith post&lt;/a&gt; is a nice shortcut for getting started with SqlDeveloper taking care of some of the details.
Remember to recompile with debug any of your packages and types that are called by the outer block if you want details
for them. Once you get your feet wet, you can read the Oracle 
document &lt;em&gt;Database Development Guide&lt;/em&gt;, Chapter 15, &lt;em&gt;Using the PL/SQL Hierarchical Profiler&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;chained-pieplined-table-functions-and-bulk-fetch&quot;&gt;Chained PIEPLINED Table Functions and Bulk Fetch&lt;/h1&gt;

&lt;p&gt;My working solution used chained PIPELINED Table functions. Per what I perceived the
documetation to be implying (without ever coming out and saying it), I implemented the function that reads the cursor
from the chain (not the first entry in the chain) without any bulk collection/array processing. The profiler
showed that function taking 3.1% of the execution time.&lt;/p&gt;

&lt;p&gt;Adding BULK COLLECT LIMIT 100 to the cursor fetch and processing the resulting array caused it to take 3.2% of the execution
time (which could be noise) and there was no reduction in total run time. 
This appears to confirm that there is no advantage to buffering that cursor from one
pipe row call to another. It is extra, unneeded processing.&lt;/p&gt;

&lt;h1 id=&quot;refactored-code-to-analyze&quot;&gt;Refactored Code to Analyze&lt;/h1&gt;

&lt;p&gt;The two variants we are comparing share most code. The one that does almost everything in the
PL/SQL engine with a chained pipeline function is called like so:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;INSERT /*+ APPEND WITH_PLSQL */ INTO ora$ptt_csv 
WITH
a AS (
    SELECT t.p AS pu
        --perlish_util_udt(t.arr) AS pu
    FROM TABLE(
                app_csv_pkg.split_lines_to_fields(
                    CURSOR(SELECT * 
                           FROM TABLE( app_csv_pkg.split_clob_to_lines(:p_clob, p_skip_lines =&amp;gt; 1) )
                    )
                    , p_separator =&amp;gt; :p_separator, p_strip_dquote =&amp;gt; :p_strip_dquote, p_keep_nulls =&amp;gt; 'Y'
                )
    ) t
) SELECT &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- must use table alias and fully qualify object name with it to be able to call function or get attribute of object&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- Thus alias x for a and use x.p.get vs a.p.get.&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;X.pu.get($##index_val##) AS &quot;$_&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
FROM a X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The one that does the &lt;em&gt;perlish_util_udt&lt;/em&gt; constructor call (which calls &lt;em&gt;app_csv_pkg.split_csv&lt;/em&gt;) from SQL is called like so:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;INSERT /*+ APPEND WITH_PLSQL */ INTO ora$ptt_csv 
WITH 
a AS (
    SELECT perlish_util_udt(
            p_csv =&amp;gt; t.s
            ,p_separator =&amp;gt; :p_separator, p_strip_dquote =&amp;gt; :p_strip_dquote, p_keep_nulls =&amp;gt; 'Y'
            ,p_expected_cnt =&amp;gt; :p_expected_cnt
        ) AS pu
    FROM TABLE( app_csv_pkg.split_clob_to_lines(:p_clob, p_skip_lines =&amp;gt; 1) ) t
) SELECT &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- must use table alias and fully qualify object name with it to be able to call function or get attribute of object&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- Thus alias x for a and use x.p.get vs a.p.get.&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;X.pu.get($##index_val##) AS &quot;$_&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
FROM a X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;perlish_util_udt&lt;/em&gt; constructor calls &lt;em&gt;app_csv_pkg.csv&lt;/em&gt;. Both are called directly from PL/SQL in the first
variant, but from SQL in the second. That is where we focus for this analysis.&lt;/p&gt;

&lt;p&gt;Both were compiled with the following settings for the initial timed run:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SESSION&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;plsql_code_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NATIVE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SESSION&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;plsql_optimize_level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using a 10,766 row, 20 column CLOB input file the total run times are&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Variant&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Run Time&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;PL/SQL&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;9.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;SQL&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;94.9&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;If you read the prior article you will notice these times do not foot to those. There were multiple
optimizations to the code since that article, most notably using &lt;em&gt;REGEXP_INSTR&lt;/em&gt; to parse the CSV rows
rather than &lt;em&gt;REGEXP_SUBSTR&lt;/em&gt;. There is still a large percentage disparity between the two variants,
though both are much faster now.&lt;/p&gt;

&lt;h1 id=&quot;running-with-hierarchical-profile-enabled&quot;&gt;Running with Hierarchical Profile Enabled&lt;/h1&gt;

&lt;p&gt;The &lt;em&gt;app_csv_pkg.split_csv&lt;/em&gt; procedure is modifed here to support additional data capture. 
You can see the 
original full package at &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;https://github.com/lee-lindley/plsql_utilities&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;po_arr&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OUT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOCOPY&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d_arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;                &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- also unquotes \&quot; and &quot;&quot; pairs within the field to just &quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_expected_cnt&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;      &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- will get an array with at least this many elements&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;c1&quot;&gt;-- when p_s IS NULL, returns initialized collection with COUNT=0&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;                   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;-- individual parsed values cannot exceed 4000 chars&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt;                     &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_pos&lt;/span&gt;                   &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_pos_last&lt;/span&gt;              &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_last_had_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_len&lt;/span&gt;                   &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;                &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gc_csv_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;__p_separator__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The local procedures defined next are to separate calls to built-in methods so that the profiler
can capture the time spent on each separately.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;c1&quot;&gt;-- these are to get profile info&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_create_arr&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;po_arr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d_arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.();&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_extend_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_cnt&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;po_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXTEND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_instr&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_INSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_pos_last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_substr&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TRIM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_pos_last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_repl_sep&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_strip_dq&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; 
	                        &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
	                                    &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^&quot;|&quot;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;-- leading &quot; or ending &quot;&lt;/span&gt;
	                                    &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;|[&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- or one of chars &quot; or \&lt;/span&gt;
	                                        &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(&quot;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;-- that is followed by a &quot; and we capture that one in \1&lt;/span&gt;
	                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;-- We put any '&quot;' we captured back without the backwack or &quot; quote&lt;/span&gt;
	                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;-- start at position 1 in v_str&lt;/span&gt;
	                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;-- 0 occurence means replace all of these we find&lt;/span&gt;
	                                &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--po_arr := &amp;amp;&amp;amp;d_arr_varchar2_udt.();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l_create_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_expected_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;--po_arr.EXTEND(p_expected_cnt);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;l_extend_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_expected_cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;--v_pos := REGEXP_INSTR(p_s, v_regexp, v_pos_last, 1, 1); -- get end char of matching string&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;l_instr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- the regexp WILL match until it matches the end of the string. Once v_pos_last &lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- is on a character past the end of the string, it will return 0.&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;EXIT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_last_had_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_pos_last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_last_had_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;--v_str := TRIM(SUBSTR(p_s, v_pos_last, v_len)); -- could still be null after trim&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;l_substr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- otherwise keep everything after trim which means should end on dquote&lt;/span&gt;
                            &lt;span class=&quot;cm&quot;&gt;/*
	                        v_str := REGEXP_REPLACE(v_str, 
	                                    '^&quot;|&quot;$'         -- leading &quot; or ending &quot;
	                                    ||'|[&quot;\\]'  -- or one of chars &quot; or \
	                                        ||'(&quot;)'     -- that is followed by a &quot; and we capture that one in \1
	                                    ,'\1'           -- We put any '&quot;' we captured back without the backwack or &quot; quote
	                                    ,1              -- start at position 1 in v_str
	                                    ,0              -- 0 occurence means replace all of these we find
	                                ); 
                            */&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;l_strip_dq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;--v_str := REGEXP_REPLACE(v_str, '\\('||p_separator||')', '\1', 1, 0);&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;l_repl_sep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                    &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_expected_cnt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- otherwise we already have room&lt;/span&gt;
	                    &lt;span class=&quot;c1&quot;&gt;--po_arr.EXTEND;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;l_extend_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	                &lt;span class=&quot;n&quot;&gt;po_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_pos_last&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- walk the string to next token&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_last_had_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- trailing null&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_expected_cnt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- otherwise we already have room&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;--po_arr.EXTEND;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;l_extend_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;po_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- do not think this is necessary, but make it explicit&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- end if input string not null&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Recompiling in DEBUG mode and with Profiling turned on, I expected some overhead to add to the run
times and we do see that.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COMPILE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COMPILE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_HPROF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_profiling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;PLSHPROF_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;test.trc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- clob not listed for brevity&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;APP_CSV_PKG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_ptt_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;...
...
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_HPROF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stop_profiling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ora$ptt_csv&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;runid&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;runid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_HPROF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;analyze&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;LOCATION&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;PLSHPROF_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FILENAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;test.trc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PUT_LINE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;runid = &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The times are longer with profiling and debug mode, but still proportional.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Variant&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Run Time&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;PL/SQL&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;SQL&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;141.7&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h1 id=&quot;reading-the-profiler-data&quot;&gt;Reading the Profiler Data&lt;/h1&gt;

&lt;p&gt;SqlDeveloper can show it to you including the HTML report it causes to be created in the database directory.
That is convenient and I use it, but for this article I teased out the data I wanted to show.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subtree_elapsed_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subtree_elapsed_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function_elapsed_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function_elapsed_time&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbmshp_function_info&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subtree_elapsed_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subtree_elapsed_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function_elapsed_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function_elapsed_time&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbmshp_function_info&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NVL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NVL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subtree_elapsed_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;999,999.9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;called_from_sql_cum&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subtree_elapsed_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;999,999.9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;called_from_plql_cum&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function_elapsed_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;999,999.9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;called_from_sql_secs&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function_elapsed_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;999,999.9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;called_from_plsql_secs&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;full&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;outer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;--and b.line# = a.line#&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;ow&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CREATE_PTT_CSV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CREATE_PTT_CSV_UDT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NVL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subtree_elapsed_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subtree_elapsed_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;desc&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;You may notice that the total times are a bit less than reported above. The difference I believe
is that I’m reporting above the elapsed time in sqlplus, which includes the time to load the
CLOB into memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;PL/SQL Hierarchical Profiler Data&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/Screenshot 2022-04-10 201308.gif&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;I find it interesting that two of the operations I was concerned with, creating the collection
(SPLIT_CSV.L_CREATE_ARR) and extending/growing the collection (SPLIT_CSV.L_EXTEND_ARR), are non-factors.&lt;/p&gt;

&lt;p&gt;My original premise was that context switching was the villain. If that were the entire story,
I would expect the overall time for the function called from SQL to show the longer run time
while the internal components of the call did not. In other words the impact of the context switch
should only be at the beginning and end of the single call as we move memory around for the 
context switch. That is NOT what we see here.&lt;/p&gt;

&lt;p&gt;Individual operations that should be taking place inside the PL/SQL engine for both runs have
an elapsed time sum that is ten to twenty times longer when the function is called from SQL
than when called from PL/SQL.&lt;/p&gt;

&lt;p&gt;It is possible that I do not understand all implications of “context switching” here.&lt;/p&gt;

&lt;p&gt;If the only discrepancies were in the regular expression engine, I would feel much better as that
beast could be a weirdo. But we have anomalous behavior in the simple substring operation as well (SPLIT_CSV.L_SUBSTR)
and that has nothing to do with the regular expression engine.&lt;/p&gt;

&lt;p&gt;What the problem operations all have in common is reading and/or writing character data in memory. It may be
that each of these operations incur the context switch penalty as they negotiate the “other”
memory space.&lt;/p&gt;

&lt;p&gt;I’m now at a point where I need to revisit my understanding of what context switch means for the
SQL/PL_SQL interface. It isn’t like I haven’t been reading everything I could find on the subject
for the last two weeks, yet somehow, exactly what goes on here has eluded me.&lt;/p&gt;

&lt;p&gt;Stay tuned.&lt;/p&gt;
</description>
        <pubDate>Sun, 10 Apr 2022 12:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2022/04/10/hierarchical-profiler-context-switch.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2022/04/10/hierarchical-profiler-context-switch.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>profiler</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Oracle REGEXP_INSTR and Beginning of Line Anchor</title>
        <description>&lt;h1 id=&quot;the-problem&quot;&gt;The Problem&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;REGEXP_INSTR&lt;/em&gt;, &lt;em&gt;REGEXP_COUNT&lt;/em&gt;, &lt;em&gt;REGEXP_REPLACE&lt;/em&gt;, and &lt;em&gt;REGEXP_SUBSTR&lt;/em&gt; all have a &lt;em&gt;position&lt;/em&gt; parameter
defined as&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;position&lt;/em&gt; is a positive integer indicating the character of &lt;em&gt;source_char&lt;/em&gt; where Oracle should begin the search. The default is 1, meaning that Oracle begins the search at the first character of &lt;em&gt;source_char&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is handy when you want to walk through a string applying the regular expression starting at different points, such as after the last match.&lt;/p&gt;

&lt;p&gt;Problem: &lt;strong&gt;When &lt;em&gt;position&lt;/em&gt; is not 1, the beginning of line anchor ‘^’ does not match the beginning of the substring&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There is also an &lt;em&gt;occurence&lt;/em&gt; parameter that can be used to similar effect. I presume that internally the regular expression
engine keeps track of the last match rather than parsing the entire string again, but Oracle does not say. Without details
about it I’m leary of trusting it for high performance, and in fact have some tangential evidence that using &lt;em&gt;occurence&lt;/em&gt;
for this purpose is not as performant as using &lt;em&gt;position&lt;/em&gt;. I could be wrong but am not going to try to prove it today.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;regexp_instr&quot;&gt;REGEXP_INSTR&lt;/h1&gt;

&lt;p&gt;This matches the beginning of the string which is two space characters. Since we specify &lt;em&gt;return_opt&lt;/em&gt;=1,
we are returned the character position AFTER the matched string. This meets expectations.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_INSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;  &amp;lt;&amp;lt;= string starts with space&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence */&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*return_opt*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end_position&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Same thing but without any leading spaces in &lt;em&gt;source_char&lt;/em&gt;. 
Since we have the zero or more modifer for spaces, we still expect a match
on the zero width start of line anchor.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_INSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;String starts with S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence */&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*return_opt*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end_position&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We do get a match (non-zero return value). As expected the character position AFTER the matched string (which is 0 length) 
is still 1.&lt;/p&gt;

&lt;p&gt;What happens when we advance
the position so that it is no longer at the start of &lt;em&gt;source_char&lt;/em&gt;? Here I set &lt;em&gt;position&lt;/em&gt; to 2 so that
we start looking for a match at character position 2.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_INSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;String starts with S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence */&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*return_opt*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end_position&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That was a huge surprise to me. 
The ‘^’ anchor no longer matches the beginning of what we think of as the string we are matching 
(sub-string starting at position 2). I don’t know that I can say it is a bug because Oracle does not 
explain how &lt;em&gt;position&lt;/em&gt; is applied, and Oracle is careful in the wording that ‘^’ matches the start of the
&lt;strong&gt;entire source string&lt;/strong&gt; (or after a newline when the ‘m’ &lt;em&gt;match_param&lt;/em&gt; is specified). Nowhere does it
say that ‘^’ matches at starting &lt;em&gt;position&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Needless to say, it does NOT work the way I expect which is as demonstrated by using &lt;em&gt;SUBSTR&lt;/em&gt; to achieve
the goal of starting at position 2 rather than using the &lt;em&gt;position&lt;/em&gt; argument to &lt;em&gt;REGEXP_INSTR&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_INSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;String starts with S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence */&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*return_opt*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end_position&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can work with this if we must, but we are making a copy of the rest of the string to do it rather than 
walking through it in place. This does not make me happy if I’m writing a tight subroutine that is called millions
of times.&lt;/p&gt;

&lt;p&gt;If we can write the &lt;em&gt;pattern&lt;/em&gt; such that it works correctly without ‘^’, we can be efficient using &lt;em&gt;position&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;regexp_count&quot;&gt;REGEXP_COUNT&lt;/h1&gt;

&lt;p&gt;The behavior is the same for &lt;em&gt;REGEXP_COUNT&lt;/em&gt; as &lt;em&gt;REGEXP_SUBSTR. Starting with the *position&lt;/em&gt;=1 we get what we expect.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cnt&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Moving to &lt;em&gt;position&lt;/em&gt;=2 my expectation would be to match the ‘X’ in the second character position 
along with the ones after newlines to give an answer of 3.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cnt&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course the behavior is same as for REGEXP_INSTR and we do not match until after a newline.&lt;/p&gt;

&lt;p&gt;Interestingly if we advance to &lt;em&gt;position&lt;/em&gt;=4 which means it is sitting on a newline character as
the starting position for the string, we still get an answer of 2.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cnt&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we advance to character position 5 which is after the newline, the ‘^’ no longer matches at our
start of string position and the first X on line 2 is not matched. We only match on the 3rd line.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cnt&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;regexp_replace&quot;&gt;REGEXP_REPLACE&lt;/h1&gt;

&lt;p&gt;Starting position of 1 works as expected as we anchor at the beginning of &lt;em&gt;source_char&lt;/em&gt;.
The starting ‘X’ on all three lines is replaced by ‘Y’.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_to_y&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;YXX
Yab
Ycd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Starting position of 2 my expectation is that the ‘X’ in character position 2 of line 1 be changed to ‘Y’. That
does not happen.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_to_y&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;XXX
Yab
Ycd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;regexp_substr&quot;&gt;REGEXP_SUBSTR&lt;/h1&gt;

&lt;p&gt;Starting position of 1 works as expected.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X.*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_to_y&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;XXX
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Setting &lt;em&gt;occurence&lt;/em&gt; to 2 also works as expected.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X.*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_to_y&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Xab
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Setting &lt;em&gt;position&lt;/em&gt; to 2 we do not get a match until after a newline which is same behavior as the other
regular expression functions.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;XXX
Xab
Xcd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^X.*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*position*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*occurence*/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*match_param*/&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_to_y&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Xab
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Maybe Oracle can claim this is working as designed. As far as I’m concerned it is a bug, but not one I
expect them to fix. It would break too much code already dependent on this behavior.  Oracle should amend 
the documentation to explain this behavior as it is different from the way we work with regular
expressions to walk through a string in other languages like Perl.&lt;/p&gt;
</description>
        <pubDate>Sun, 10 Apr 2022 02:45:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2022/04/10/Regexp_instr-Beginning-of-line-anchor.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2022/04/10/Regexp_instr-Beginning-of-line-anchor.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>regexp</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Cost of UDT Object Methods in SQL</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;In a prior post, &lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/23/CSV-Clob-PTT.html&quot;&gt;CSV Clob and Private Temporary Table&lt;/a&gt;,
I described how one could use some new tools I created 
to parse a CSV clob into records and fields, then use that parsed data in a SQL statement,
perhaps including DML.&lt;/p&gt;

&lt;p&gt;The article was about a work in progress and the code has changed, but it will give you an idea of how
we got here if you are interested.&lt;/p&gt;

&lt;p&gt;I ran into performance issues for large datasets while testing it. This article is about
the techniques I tried for tuning it, and a fact about Oracle Object Type methods that
I had not thought about much before.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: you will see what may be unfamiliar methods of &lt;em&gt;perlish_util_udt&lt;/em&gt; Object type in this code. They work like Perl operators on lists.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;sql-engine-and-plsql-method-calls&quot;&gt;SQL Engine and PL/SQL Method Calls&lt;/h1&gt;

&lt;p&gt;Here is an example query that is built as dynamic SQL in a PL/SQL procedure. It
returns a resultset built from reading CSV data provided to the program as a CLOB.&lt;/p&gt;

&lt;p&gt;You can find the packages at &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;https://github.com/lee-lindley/plsql_utilities&lt;/a&gt;.
The procedure I’m showing examples from is &lt;em&gt;app_csv_pkg.create_ptt_csv&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_skip_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The pipelined table function &lt;em&gt;split_clob_to_lines&lt;/em&gt; is efficient. 
What turned out to be not efficient is the call to the &lt;em&gt;perlish_util_udt&lt;/em&gt; object conststructor and/or the 
call to the function &lt;em&gt;split_csv&lt;/em&gt; in the SQL SELECT list. Those are both efficient when called inside a PL/SQL program.&lt;/p&gt;

&lt;p&gt;This should not have surprised me. I know that the SQL engine must do a context switch for each call to a PL/SQL
program unless it is cached. In this case the cost was much, much larger than I expected.&lt;/p&gt;

&lt;p&gt;For a 10K row CLOB of CSV data (20 fields each row) the above construct ran for 20 minutes to process the query on an AIX
based database, and 6 minutes on my Intel NUC running in a Hyper-V Linux partition.
It is difficult
to tell what it is doing from the WAIT events, but I suspected it was in-memory operations and context switching.&lt;/p&gt;

&lt;h1 id=&quot;removing-the-object&quot;&gt;Removing the Object&lt;/h1&gt;

&lt;p&gt;I also suspected the object method &lt;em&gt;get&lt;/em&gt; calls might be doing context switches. My first refinement attempt
was to remove the object from the picture and use a WITH FUNCTION to snag the field values from the
array returned by &lt;em&gt;split_csv&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;ARR_VARCHAR2_UDT&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_i&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_skip_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
     &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This also ran for 6 minutes on my Intel NUC database. We haven’t ruled out that the object could play a
part, but we know that the function call &lt;em&gt;split_csv&lt;/em&gt; is a problem.&lt;/p&gt;

&lt;p&gt;If you are thinking about using
a &lt;em&gt;UDF&lt;/em&gt; pragma on that puppy (&lt;em&gt;split_csv&lt;/em&gt;), I could not 
get any of the variations I tried to give an improvement. There are many examples shown of the limitations
of that pragma (or using a WITH function) that does anything very complicated.&lt;/p&gt;

&lt;h1 id=&quot;make-the-function-pipelined-in-a-chain&quot;&gt;Make the Function Pipelined in a Chain&lt;/h1&gt;

&lt;p&gt;The next refactoring I tried was moving the call to &lt;em&gt;split_csv&lt;/em&gt; into another Pipelined Table Function
to be called in a chain with &lt;em&gt;split_clob_to_lines&lt;/em&gt;. I called it &lt;em&gt;split_lines_to_fields&lt;/em&gt; (catcalls
from the peanut gallery may be deserved). Maybe I’ll come up with a better name before I merge this code.&lt;/p&gt;

&lt;p&gt;From the package header:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_csv_fields_rec&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;RECORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ARR_VARCHAR2_UDT&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_arr_csv_fields_rec&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_csv_fields_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_lines_to_fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_curs&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;t_curs_csv_row_rec&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- also unquotes \&quot; and &quot;&quot; pairs within the field to just &quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_arr_csv_fields_rec&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and from the package body:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_lines_to_fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_curs&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;t_curs_csv_row_rec&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- also unquotes \&quot; and &quot;&quot; pairs within the field to just &quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_arr_csv_fields_rec&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_row&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;t_csv_fields_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_in_row&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;t_csv_row_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;FETCH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_curs&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_in_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;EXIT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_curs&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;%NOTFOUND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_in_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_in_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;PIPE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_lines_to_fields&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The documentation never shows a Bulk Collect in the chained pipeline examples, and the descriptions
it uses and other hints make me believe it is not needed. I wish it was
more explicit.&lt;/p&gt;

&lt;p&gt;As recommended in the documentation, it is called in a chain of pipelined table functions 
directly in SQL with CURSOR casts as&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;ARR_VARCHAR2_UDT&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_i&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt; 
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_lines_to_fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;CURSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; 
                           &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_skip_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
     &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This ran in 23 seconds. Moving the function call into a pipelined table chain solves the problem, but I
still want to know whether or how much the Object plays a part.&lt;/p&gt;

&lt;h1 id=&quot;call-the-object-constructor-in-sql&quot;&gt;Call the Object Constructor in SQL&lt;/h1&gt;

&lt;p&gt;I could have moved the object constructor into the pipeline chain (and did try that), but it turns out
to not be necessary.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_lines_to_fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;CURSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; 
                           &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_skip_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
     &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This also ran in 23 seconds. I prefer the object way better than the WITH FUNCTION way, but if you don’t
already have an object wrapped around your array with a handy method, the WITH FUNCTION is just dandy.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Calls to a PL/SQL function in a SELECT list can lead to significant cost via context switching. This is
well known, and there are several caching strategies (DETERMINISTIC, RESULT_CACHE, Scalar Subquery) to mitigate
it. Using a Pipelined Table Function chain as I did here is also a viable mitigation strategy, and I believe
the appropriate one for this use case.&lt;/p&gt;

&lt;p&gt;My concern that object construction and simple method function calls in the SQL engine could context
switch seems to be unfounded. I re-read the Object Relational Developer Guide and searched Google several
ways looking for more information about how object methods are implemented, but it is not clearly stated.
They seem to be neither fish nor fowl, SQL engine nor PL/SQL engine.&lt;/p&gt;

&lt;p&gt;I am wondering why using an object type is not touted as another mitigation strategy for PL/SQL Function context switching
(at least for when the method does not need to call anything other than builtin functions)? 
I realize we have WITH FUNCTIONs, UDF pragma, and with Oracle 21c, SQL Macros,
but it would be nice to know if Object methods are an alternative.&lt;/p&gt;

&lt;p&gt;I may explore this as a context switching mitigation strategy another day.&lt;/p&gt;

&lt;h1 id=&quot;appendix---the-full-monty&quot;&gt;Appendix - The Full Monty&lt;/h1&gt;

&lt;p&gt;If you have a use case that calls for the ultimate in performance (this one does not, but
I happened to try this early in my analysis), you can get down and dirty with DBMS_SQL
and I believe what is called “Method 4” dynamic SQL. That classification is because we have a variable
number of bind elements.&lt;/p&gt;

&lt;p&gt;Unlike the other examples, this one shows the entire procedure that creates a
Private Temporary Table (PTT) and populates it from the CSV Clob.&lt;/p&gt;

&lt;p&gt;The best I could achieve with the code shown in the article was 23 seconds. This version
operates in 19 seconds. It isn’t worth the complexity for this package, but it is nice
to know how to do it for the rare occassions when you might need to squeeze out
that last little bit of efficiency.&lt;/p&gt;

&lt;p&gt;The first part is mostly the same as the code I didn’t show you for the other variations. 
It parses the first row of the CLOB to get the column names and creates the PTT.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_ptt_csv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;-- creates private temporary table &quot;ora$ptt_csv&quot; with columns named in first row of data (case preserved).&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;-- from a CLOB containing CSV lines.&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;-- All fields are varchar2(4000)&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
	     &lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
	    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
	    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- also unquotes \&quot; and &quot;&quot; pairs within the field to just &quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- for manipulating column names into SQL statement&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_first_row&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_ins_curs&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_num_rows&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_last_row_cnt&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_vals_1_row&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;ARR_VARCHAR2_UDT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- from split_csv on 1 line&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;DBMS_SQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;varchar2a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;-- from split_clob_to_lines fetch&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- variable number of columns, each of which has a bind array.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;varchar2a_tab&lt;/span&gt;  &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_SQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;varchar2a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_vals&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;varchar2a_tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;-- array of columns each of which holds array of values&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- We get all but the header row when we read the clob in a loop.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;CURSOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_read_rows&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_skip_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- read the first row only&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_first_row&lt;/span&gt; 
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_max_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_first_row&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;raise_application_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20222&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app_csv_pkg.create_ptt_csv did not find csv rows in input clob.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO_DATA_FOUND&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;raise_application_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20222&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app_csv_pkg.create_ptt_csv did not find csv rows in input clob.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- split the column header values into collection&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_first_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;-- create the private global temporary table with &quot;known&quot; name and columns matching names found&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- in csv first record&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;DROP TABLE ora$ptt_csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OTHERS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CREATE PRIVATE TEMPORARY TABLE ora$ptt_csv(
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;$_&quot;    VARCHAR2(4000)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The next part populates the PTT. This variation uses bulk collect to read the data as lines,
splits the lines into columns, stuffs the column data into individual arrays, then
executes a DBMS_SQL INSERT cursor bound to those arrays. Pretty slick, if a little hard core.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;c1&quot;&gt;-- &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- Dynamic sql for dbms_sql. will be used with bind arrays.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- Of note is that it reports conventional load even if specify append.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- I don't understand that as I've seen other reports that direct path load works.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- Does not seem to matter though.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;INSERT INTO ora$ptt_csv(
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;$_&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;-- the column names in dquotes&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
) VALUES (
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:$##index_val##&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- :1, :2, :3, etc... bind placeholders&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_ins_curs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_SQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open_cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_SQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ins_curs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_SQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;native&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_read_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;FETCH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_read_rows&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BULK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;COLLECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;EXIT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_vals_1_row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- j is column number&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_vals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_vals_1_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_last_row_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- will be true on first loop iteration and maybe last&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_last_row_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- bind each column array. v_vals has an array for every column&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;DBMS_SQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bind_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ins_curs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_vals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_last_row_cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;v_num_rows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_SQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ins_curs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_SQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close_cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ins_curs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;CLOSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_read_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_ptt_csv&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 02 Apr 2022 03:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2022/04/02/Object-Methods-in-SQL.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2022/04/02/Object-Methods-in-SQL.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>objects</category>
        
        <category>user-defined-type-objects</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Devops Data Deployment for Oracle</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;Most large corporations have adopted some form of &lt;strong&gt;Continuous Improvement&lt;/strong&gt; (CI)
strategy with a &lt;strong&gt;Devops&lt;/strong&gt; build and deploy component. 
The people who manage and direct these operations tend to
be Web guys and the tools tend to focus on deploying Web applications (this includes
middleware component on Java and .NET servers). If I am
guilty of stereotyping incorrectly, please tell me about organizations with 
a broader focus. I could be wrong, but that is what I have observed so far.&lt;/p&gt;

&lt;p&gt;Other application types are supported, just not as first class citizens.
Specifically, database application deployment is a red-headed step-child
of Devops. I’m not blaming the Devops guys either. We database practioners
are likely guilty of obstruction. Any tool other than what we know works
will be looked upon with suspicion.&lt;/p&gt;

&lt;h1 id=&quot;how-can-we-deploy&quot;&gt;How Can We Deploy?&lt;/h1&gt;

&lt;p&gt;We need to deploy database application code (packages, table definitions, etc..)
and we need to deploy data (configuration table entries for example).&lt;/p&gt;

&lt;p&gt;We have a suite of client tools we can use as Oracle clients for deploying
code and data manually, all of which require providing or storing a password:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SQL*PLUS&lt;/li&gt;
  &lt;li&gt;imp&lt;/li&gt;
  &lt;li&gt;sqlldr&lt;/li&gt;
  &lt;li&gt;external tables (assuming you can deliver files to a database directory)&lt;/li&gt;
  &lt;li&gt;sqlCL&lt;/li&gt;
  &lt;li&gt;sqlDeveloper&lt;/li&gt;
  &lt;li&gt;Toad&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can rule out the gui tools &lt;em&gt;SqlDeveloper&lt;/em&gt; and &lt;em&gt;Toad&lt;/em&gt;. These are not suitable for automated
deployment.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SQL*PLUS&lt;/em&gt; is perfectly adequate for deploying our code to the database. Oracle uses it to 
deploy database code and patches. We have
all been using &lt;em&gt;SQL*PLUS&lt;/em&gt; for as long as we have used Oracle. It is clunky, limiting and antiquated,
but it works and we know how to make it do the needful.&lt;/p&gt;

&lt;p&gt;Strangely, &lt;em&gt;sqlCL&lt;/em&gt; does not seem well supported. I know some reasons for that including
security concerns with having Java client software on servers, but the
primary reason is likely inertia. This is a shame because &lt;em&gt;sqlCl&lt;/em&gt; supports CSV file 
loading, which would address one of our two deployment needs.&lt;/p&gt;

&lt;p&gt;I have not seen a Devops deployment configuration that uses anything other than &lt;em&gt;SQL*PLUS&lt;/em&gt;
to deploy code. If there are some, my guess is the database application guys are less
than thrilled. I could be wrong.&lt;/p&gt;

&lt;h1 id=&quot;deploying-data&quot;&gt;Deploying Data&lt;/h1&gt;

&lt;p&gt;For batch operations we have mechanisms for transfering files between production
systems and/or with outside vendors. We have custom processes using
&lt;em&gt;sqlldr&lt;/em&gt;, External tables and perhaps an ETL tool to import the data from those files. 
Yet the mechanisms
we have for slinging files around in a production environment are probably not supported
through Devops.&lt;/p&gt;

&lt;p&gt;We have files in our build that are on the deployment server and we have a &lt;em&gt;SQL*PLUS&lt;/em&gt; client.&lt;/p&gt;

&lt;p&gt;Traditionally when faced with this we resort to what I call “stupid data load.” We
create a series of Insert or Merge statements with hard coded data and run those
in &lt;em&gt;SQL*PLUS&lt;/em&gt;. Maybe we get a little more ambitious and create a single INSERT
or MERGE statement with a series of SELECT FROM DUAL UNION ALL … statements
providing the input. It is still “stupid data load.” Works fine for a small number of
records. Not so fine when you need to create several thousand records.&lt;/p&gt;

&lt;p&gt;If Devops configured a capability to use &lt;em&gt;sqlldr&lt;/em&gt;, we could do what we must. The use case
for it is limited though and the firm may not choose to invest the resources necessary
to configure and manage that capability.&lt;/p&gt;

&lt;h1 id=&quot;an-alternative-for-loading-data-with-sqlplus&quot;&gt;An Alternative for Loading Data with SQL*PLUS&lt;/h1&gt;

&lt;p&gt;Using tools provided in &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;plsql_utilities&lt;/a&gt; we can
build deployment scripts that run in &lt;em&gt;SQL*PLUS&lt;/em&gt; handling relatively large amounts of
data as Comma Separated Value (CSV) records efficiently. 
Not as efficiently as using &lt;em&gt;sqlldr&lt;/em&gt;, but good enough.&lt;/p&gt;

&lt;p&gt;From the documentation at &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities/tree/main/app_csv_pkg&quot;&gt;plsql_utilities/app_csv_pkg&lt;/a&gt;…&lt;/p&gt;

&lt;p&gt;Read the data from a table (with an optional WHERE clause) and convert it
into a CSV CLOB with a header row. Break the CLOB into a set of quoted
string literals. Generate a script that&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Creates a Private Temporary Table from the CLOB (input as contactenated string literals).&lt;/li&gt;
  &lt;li&gt;Inserts or Merges records from the PTT into the target Table&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We call the deployment generator:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;APP_CSV_PKG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen_deploy_merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MY_TABLE_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_key_cols&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_where_clause&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;id &amp;lt;= 10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DUAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That provides the following script as a CLOB in your query result. Note that
if the size of the CSV CLOB holding the records was greater than 32767, 
it would be represented by a concatenated set of quoted literals instead
of just one as shown here.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;APP_CSV_PKG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_ptt_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&quot;ID&quot;,&quot;MSG&quot;,&quot;DT&quot;
1,&quot;testing...&quot;,&quot;03/26/2022&quot;
2,&quot;testing...&quot;,&quot;03/27/2022&quot;
3,&quot;testing...&quot;,&quot;03/28/2022&quot;
4,&quot;testing...&quot;,&quot;03/29/2022&quot;
5,&quot;testing...&quot;,&quot;03/30/2022&quot;
6,&quot;testing...&quot;,&quot;03/31/2022&quot;
7,&quot;testing...&quot;,&quot;04/01/2022&quot;
8,&quot;testing...&quot;,&quot;04/02/2022&quot;
9,&quot;testing...&quot;,&quot;04/03/2022&quot;
10,&quot;testing...&quot;,&quot;04/04/2022&quot;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;MERGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MY_TABLE_NAME&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ora$ptt_csv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;MATCHED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SET&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;MSG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;MSG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;MATCHED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;MSG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;MSG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;I explored multiple mechanisms to avoid “stupid data load.” The obvious best choice would be if the right
tools for the job like &lt;em&gt;sqlldr&lt;/em&gt;, &lt;em&gt;imp&lt;/em&gt;, or &lt;em&gt;sqlCL&lt;/em&gt; were available, but we play the hand we are dealt. If you
are stuck with &lt;em&gt;SQL*PLUS&lt;/em&gt;, this package can be a solution.&lt;/p&gt;

</description>
        <pubDate>Mon, 28 Mar 2022 10:00:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/plsql/sql/oracle/2022/03/28/Devops-Oracle-Deploy.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/plsql/sql/oracle/2022/03/28/Devops-Oracle-Deploy.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>devops</category>
        
        <category>deploy</category>
        
        <category>CLOB</category>
        
        
        <category>plsql</category>
        
        <category>sql</category>
        
        <category>oracle</category>
        
      </item>
    
      <item>
        <title>A Ruby/Rouge Lexer Class for Oracle PL/SQL</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;This blog is hosted by &lt;em&gt;Github Pages&lt;/em&gt;. The standard way to generate the site is via &lt;em&gt;Jekyll&lt;/em&gt; which 
can parse &lt;em&gt;Markdown&lt;/em&gt; files. It supports code syntax highlighting via a &lt;em&gt;Ruby&lt;/em&gt; gem named &lt;em&gt;Rouge&lt;/em&gt;.
It is every bit as confusing as it sounds, but strangely familiar if you have been hacking stuff
like this for a while.&lt;/p&gt;

&lt;p&gt;The nice thing about it is that I was already using &lt;em&gt;Markdown&lt;/em&gt; to build my pages and converting them to
HTML with &lt;em&gt;pandoc&lt;/em&gt;. It was messy and cumbersome, so switching to &lt;em&gt;Github Pages&lt;/em&gt; seemed like a good idea.
As with anything to do with HTML, CSS and web page development, &lt;strong&gt;nothing is ever easy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Github has decent support for SQL syntax highlighting if you use the fence construct:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;```sql
SELECT ...
```
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It renders my repository README.md files fairly well. Unfortunately, it uses a different process than 
&lt;em&gt;Github Pages&lt;/em&gt; via &lt;em&gt;Rouge&lt;/em&gt;. The &lt;em&gt;SQL&lt;/em&gt; lexer for &lt;em&gt;Rouge&lt;/em&gt; is not as robust as I wanted, and
when I started looking closer, I realize the existing one for Github wasn’t great either.&lt;/p&gt;

&lt;p&gt;I looked into what I could do about it.&lt;/p&gt;

&lt;h1 id=&quot;rouge&quot;&gt;Rouge&lt;/h1&gt;

&lt;p&gt;I got a little discouraged at first because I had not picked up &lt;em&gt;Ruby&lt;/em&gt; before and wasn’t
really interested in learning a new language. Nevertheless I dug into it a bit, got
&lt;em&gt;Ruby&lt;/em&gt;, &lt;em&gt;Jekyll&lt;/em&gt; and &lt;em&gt;Rouge&lt;/em&gt; installed locally on a Linux image (apparently it is harder
to do on Windows), and started trying to figure out how it fit together.&lt;/p&gt;

&lt;p&gt;I can’t tell you how I wound up here, but the documentation for &lt;em&gt;Rouge&lt;/em&gt; is very good. I found
&lt;a href=&quot;https://rouge-ruby.github.io/docs/file.LexerDevelopment.html&quot;&gt;Lexer Development&lt;/a&gt;. I looked
at the existing &lt;em&gt;SQL&lt;/em&gt; lexer and also the ones for &lt;em&gt;Perl&lt;/em&gt; and &lt;em&gt;Ruby&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The code is a state machine that walks through the text using regular
expressions and classifying the parsed tokens with names that correspond to the CSS file
we use for syntax highlighting it. Even though I had never looked at the language before,
it was fairly easy to read and understand what it was doing.&lt;/p&gt;

&lt;h1 id=&quot;a-plsql-lexer&quot;&gt;A PL/SQL Lexer&lt;/h1&gt;

&lt;p&gt;Long story long enough already, I faked it and wound up making what I believe is a decent 
lexer for Oracle PL/SQL. I did a lot of grunt work looking through the manuals and v$ tables
gathering all of the Oracle keywords, reserved words, types and built-in functions which
become part of the lexer. The regular expressions for breaking it into tokens is where
I think I did a decent job based on my own understanding of how Oracle parses it and similar
work I’ve done before. The q-quote operator was a nice challenge that consumed several hours.&lt;/p&gt;

&lt;p&gt;I submitted a &lt;a href=&quot;https://github.com/rouge-ruby/rouge/pull/1811&quot;&gt;Pull Request&lt;/a&gt; to the Rouge
team. There are quite a few in their queue, so I have no idea whether it will fly any time soon,
but I followed the guidelines they provided, so we will see.&lt;/p&gt;

&lt;h1 id=&quot;sample-output&quot;&gt;Sample Output&lt;/h1&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Department 
Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;departments&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Bruce&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Lee&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; 
 &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; 
  &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; 
   &lt;span class=&quot;n&quot;&gt;sample_pkg&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_re&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;DETERMINISTIC&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sample_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BODY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sample_pkg&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_re&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;DETERMINISTIC&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/*
            strip comment blocks that start with at least one blank, then
            '--' or '#', then everything to end of line or string
 ===&amp;gt; shows /* does not start a new comment
        */&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;-- quoted string with embedded newline&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;c_strip_comments_regexp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTANT&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'+&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;[[:blank:]](--|#).*($|
)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;$IF&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;$$is_dummy_needed&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;$then&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;c_dummy&lt;/span&gt;                 &lt;span class=&quot;k&quot;&gt;CONSTANT&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;newline&amp;gt;
&amp;lt;anotherline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_rec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;%ROWTYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_dummy2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;%type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;$End&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- note that \n, \r and \t will be replaced if not preceded by a \&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- \\n and \\t will not be replaced. Unfortunately, neither will \\\n or \\\t.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- If you need \\\n, use \\ \n since the space will be removed.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- We are not parsing into tokens, so this is as close as we can get cheaply&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
              &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_strip_comments_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- strip comments&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;-- strip spaces and newlines too like 'x' modifier&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;(^|[^\\])\\t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;-- replace \t with tab character value so it works like in perl&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;(^|[^\\])\\n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;-- replace \n with newline character value so it works like in perl&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;(^|[^\\])\\r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;-- replace \r with CR character value so it works like in perl&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;sample_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;-- shows a comment on last line&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;the-lexer-code&quot;&gt;The Lexer Code&lt;/h1&gt;

&lt;p&gt;The huge list of keywords makes this unwieldly, but the actual code part is not that big.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# -*- coding: utf-8 -*- #&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Rouge&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lexers&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PLSQL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RegexLexer&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PLSQL&quot;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Procedural Language Structured Query Language for Oracle relational databases&quot;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'plsql'&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;filenames&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.pls'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.typ'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.tps'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.tpb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.pks'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.pkb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.pkg'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.trg'&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;mimetypes&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'text/x-plsql'&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keywords&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@keywords&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%w(
        ACCESSIBLE AGENT ALL ALTER AND ANY AS ASC BETWEEN BFILE_BASE BLOB_BASE BY
        C CALLING CHARSET CHARSETFORM CHARSETID CHAR_BASE CHECK CLOB_BASE CLUSTER
        COLLATE COMPILED COMPRESS CONNECT CONNECT_BY_ROOT CONSTRUCTOR CREATE CUSTOMDATUM
        DATE_BASE DEFAULT DELETE DESC DISTINCT DROP DURATION ELSE ELSIF EXCEPT EXCLUSIVE
        EXISTS EXIT FIXED FOR FORALL FROM GENERAL GRANT GROUP HAVING IDENTIFIED IN INDEX
        INDICES INSERT INTERFACE INTERSECT INTO IS LARGE LIKE LIMITED LOCK LOOP MAXLEN
        MINUS MODE NOCOMPRESS NOT NOWAIT NULL NUMBER_BASE OCICOLL OCIDATE OCIDATETIME
        OCIDURATION OCIINTERVAL OCILOBLOCATOR OCINUMBER OCIRAW OCIREF OCIREFCURSOR
        OCIROWID OCISTRING OCITYPE OF ON OPTION OR ORACLE ORADATA ORDER ORLANY ORLVARY
        OUT OVERRIDING PARALLEL_ENABLE PARAMETER PASCAL PCTFREE PIPE PIPELINED POLYMORPHIC
        PRAGMA PRIOR PUBLIC RAISE RECORD RELIES_ON REM RENAME RESOURCE RESULT REVOKE ROWID 
        SB1 SB2 SELECT SEPARATE SET SHARE SHORT SIZE SIZE_T SPARSE SQLCODE SQLDATA
        SQLNAME SQLSTATE STANDARD START STORED STRUCT STYLE SYNONYM TABLE TDO THEN
        TRANSACTIONAL TRIGGER UB1 UB4 UNION UNIQUE UNSIGNED UNTRUSTED UPDATE VALIST
        VALUES VARIABLE VIEW VOID WHERE WHILE WITH
        )&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keywords_nresvd&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@keywords_nresvd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%w(
        ABORT ABS ABSENT ACCESS ACCESSED ACCOUNT ACL ACOS ACROSS ACTION ACTIONS
        ACTIVATE ACTIVE ACTIVE_COMPONENT ACTIVE_DATA ACTIVE_FUNCTION ACTIVE_TAG ACTIVITY
        ADAPTIVE_PLAN ADD ADD_COLUMN ADD_GROUP ADD_MONTHS ADG_REDIRECT_DML ADG_REDIRECT_PLSQL
        ADJ_DATE ADMIN ADMINISTER ADMINISTRATOR ADVANCED ADVISE ADVISOR AFD_DISKSTRING
        AFFINITY AFTER AGGREGATE AGGREGATES ALGORITHM ALIAS ALLOCATE ALLOW ALL_ROWS
        ALTERNATE ALWAYS ANALYTIC ANALYTIC_VIEW_SQL ANALYZE ANCESTOR ANCILLARY AND_EQUAL
        ANOMALY ANSI_REARCH ANSWER_QUERY_USING_STATS ANTIJOIN ANYSCHEMA ANY_VALUE
        APPEND APPENDCHILDXML APPEND_VALUES APPLICATION APPLY APPROX_COUNT APPROX_COUNT_DISTINCT
        APPROX_COUNT_DISTINCT_AGG APPROX_COUNT_DISTINCT_DETAIL APPROX_MEDIAN APPROX_PERCENTILE
        APPROX_PERCENTILE_AGG APPROX_PERCENTILE_DETAIL APPROX_RANK APPROX_SUM ARCHIVAL
        ARCHIVE ARCHIVED ARCHIVELOG ARRAY ARRAYS ASCII ASCIISTR ASIN ASIS ASSEMBLY
        ASSIGN ASSOCIATE ASYNC ASYNCHRONOUS AS_JSON AT ATAN ATAN2 ATTRIBUTE ATTRIBUTES
        AUDIT AUTHENTICATED AUTHENTICATION AUTHID AUTHORIZATION AUTO AUTOALLOCATE
        AUTOEXTEND AUTOMATIC AUTO_LOGIN AUTO_REOPTIMIZE AVAILABILITY AVCACHE_OP AVERAGE_RANK
        AVG AVMDX_OP AVRO AV_AGGREGATE AV_CACHE AW BACKGROUND BACKINGFILE BACKUP BAND_JOIN
        BASIC BASICFILE BATCH BATCHSIZE BATCH_TABLE_ACCESS_BY_ROWID BECOME BEFORE
        BEGIN BEGINNING BEGIN_OUTLINE_DATA BEHALF BEQUEATH BFILENAME BIGFILE BINARY
        BINARY_DOUBLE_INFINITY BINARY_DOUBLE_NAN BINARY_FLOAT_INFINITY BINARY_FLOAT_NAN
        BINDING BIND_AWARE BIN_TO_NUM BITAND BITMAP BITMAPS BITMAP_AND BITMAP_BIT_POSITION
        BITMAP_BUCKET_NUMBER BITMAP_CONSTRUCT_AGG BITMAP_COUNT BITMAP_OR_AGG BITMAP_TREE
        BITOR BITS BITXOR BIT_AND_AGG BIT_OR_AGG BIT_XOR_AGG BLOCK BLOCKCHAIN BLOCKING
        BLOCKS BLOCKSIZE BLOCK_RANGE BODY BOOL BOOTSTRAP BOTH BOUND BRANCH BREADTH
        BROADCAST BSON BUFFER BUFFER_CACHE BUFFER_POOL BUILD BULK BUSHY_JOIN BYPASS_RECURSIVE_CHECK
        BYPASS_UJVC CACHE CACHE_CB CACHE_INSTANCES CACHE_TEMP_TABLE CACHING CALCULATED
        CALL CALLBACK CANCEL CAPACITY CAPTION CAPTURE CARDINALITY CASCADE CASE CAST
        CATALOG_DBLINK CATEGORY CDB$DEFAULT CDB_HTTP_HANDLER CEIL CELLMEMORY CELL_FLASH_CACHE
        CERTIFICATE CFILE CHAINED CHANGE CHANGE_DUPKEY_ERROR_INDEX CHARTOROWID CHAR_CS
        CHECKPOINT CHECKSUM CHECK_ACL_REWRITE CHILD CHOOSE CHR CHUNK CLASS CLASSIFICATION
        CLASSIFIER CLAUSE CLEAN CLEANUP CLEAR CLIENT CLONE CLOSE CLOSEST CLOSE_CACHED_OPEN_CURSORS
        CLOUD_IDENTITY CLUSTERING CLUSTERING_FACTOR CLUSTERS CLUSTER_BY_ROWID CLUSTER_DETAILS
        CLUSTER_DISTANCE CLUSTER_ID CLUSTER_PROBABILITY CLUSTER_SET COALESCE COALESCE_SQ
        COARSE COLAUTH COLD COLLATE COLLATION COLLECT COLUMN COLUMNAR COLUMNS COLUMN_AUTHORIZATION_INDICATOR
        COLUMN_AUTH_INDICATOR COLUMN_STATS COLUMN_VALUE COMMENT COMMIT COMMITTED COMMON
        COMMON_DATA_MAP COMPACT COMPATIBILITY COMPILE COMPLETE COMPLIANCE COMPONENT
        COMPONENTS COMPOSE COMPOSITE COMPOSITE_LIMIT COMPOUND COMPUTATION COMPUTE
        CONCAT CONDITION CONDITIONAL CONFIRM CONFORMING CONNECT_BY_CB_WHR_ONLY CONNECT_BY_COMBINE_SW
        CONNECT_BY_COST_BASED CONNECT_BY_ELIM_DUPS CONNECT_BY_FILTERING CONNECT_BY_ISCYCLE
        CONNECT_BY_ISLEAF CONNECT_BY_ROOT CONNECT_TIME CONSENSUS CONSIDER CONSISTENT
        CONST CONSTANT CONSTRAINT CONSTRAINTS CONTAINER CONTAINERS CONTAINERS_DEFAULT
        CONTAINER_DATA CONTAINER_DATA_ADMIT_NULL CONTAINER_MAP CONTAINER_MAP_OBJECT
        CONTENT CONTENTS CONTEXT CONTINUE CONTROLFILE CONVERSION CONVERT CON_DBID_TO_ID
        CON_GUID_TO_ID CON_ID CON_ID_FILTER CON_ID_TO_CON_NAME CON_ID_TO_DBID CON_ID_TO_GUID
        CON_ID_TO_UID CON_NAME_TO_ID CON_UID_TO_ID COOKIE COPY CORR CORRUPTION CORRUPT_XID
        CORRUPT_XID_ALL CORR_K CORR_S COS COSH COST COST_XML_QUERY_REWRITE COUNT COVAR_POP
        COVAR_SAMP CO_AUTH_IND CPU_COSTING CPU_COUNT CPU_PER_CALL CPU_PER_SESSION
        CPU_TIME CRASH CREATE_FILE_DEST CREATE_STORED_OUTLINES CREATION CREDENTIAL
        CREDENTIALS CRITICAL CROSS CROSSEDITION CSCONVERT CUBE CUBE_AJ CUBE_GB CUBE_SJ
        CUME_DIST CUME_DISTM CURRENT CURRENTV CURRENT_DATE CURRENT_INSTANCE CURRENT_PARTSET_KEY
        CURRENT_SCHEMA CURRENT_SHARD_KEY CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER
        CURSOR CURSOR_SHARING_EXACT CURSOR_SPECIFIC_SEGMENT CV CYCLE DAGG_OPTIM_GSETS
        DANGLING DATA DATABASE DATABASES DATAFILE DATAFILES DATAMOVEMENT DATAOBJNO
        DATAOBJ_TO_MAT_PARTITION DATAOBJ_TO_PARTITION DATAPUMP DATASTORE DATA_LINK_DML
        DATA_SECURITY_REWRITE_LIMIT DATA_VALIDATE DATE_MODE DAYS DBA DBA_RECYCLEBIN
        DBMS_STATS DBSTR2UTF8 DBTIMEZONE DB_ROLE_CHANGE DB_UNIQUE_NAME DB_VERSION
        DDL DEALLOCATE DEBUG DEBUGGER DECLARE DECODE DECOMPOSE DECOMPRESS DECORRELATE
        DECR DECREMENT DECRYPT DEDUPLICATE DEFAULTS DEFAULT_COLLATION DEFAULT_PDB_HINT
        DEFERRABLE DEFERRED DEFINE DEFINED DEFINER DEFINITION DEGREE DELAY DELEGATE
        DELETEXML DELETE_ALL DEMAND DENORM_AV DENSE_RANK DENSE_RANKM DEPENDENT DEPTH
        DEQUEUE DEREF DEREF_NO_REWRITE DESCENDANT DESCRIPTION DESTROY DETACHED DETERMINED
        DETERMINES DETERMINISTIC DG_GATHER_STATS DIAGNOSTICS DICTIONARY DIGEST DIMENSION
        DIMENSIONS DIRECT DIRECTORY DIRECT_LOAD DIRECT_PATH DISABLE DISABLE_ALL DISABLE_CONTAINERS_DEFAULT
        DISABLE_CONTAINER_MAP DISABLE_PARALLEL_DML DISABLE_PRESET DISABLE_RPKE DISALLOW
        DISASSOCIATE DISCARD DISCONNECT DISK DISKGROUP DISKS DISMOUNT DISTINGUISHED
        DISTRIBUTE DISTRIBUTED DIST_AGG_PROLLUP_PUSHDOWN DML DML_UPDATE DOCFIDELITY
        DOCUMENT DOMAIN_INDEX_FILTER DOMAIN_INDEX_NO_SORT DOMAIN_INDEX_SORT DOWNGRADE
        DRAIN_TIMEOUT DRIVING_SITE DROP_COLUMN DROP_GROUP DST_UPGRADE_INSERT_CONV
        DUMP DUPLICATE DUPLICATED DV DYNAMIC DYNAMIC_SAMPLING DYNAMIC_SAMPLING_EST_CDN
        EACH EDITION EDITIONABLE EDITIONING EDITIONS ELAPSED_TIME ELEMENT ELIMINATE_JOIN
        ELIMINATE_OBY ELIMINATE_OUTER_JOIN ELIMINATE_SQ ELIM_GROUPBY EM EMPTY EMPTY_BLOB
        EMPTY_CLOB ENABLE ENABLE_ALL ENABLE_PARALLEL_DML ENABLE_PRESET ENCODE ENCODING
        ENCRYPT ENCRYPTION END END_OUTLINE_DATA ENFORCE ENFORCED ENQUEUE ENTERPRISE
        ENTITYESCAPING ENTRY EQUIPART EQUIVALENT ERROR ERRORS ERROR_ARGUMENT ERROR_ON_OVERLAP_TIME
        ESCAPE ESTIMATE EVAL EVALNAME EVALUATE EVALUATION EVEN EVENTS EVERY EXCEPTION
        EXCEPTIONS EXCHANGE EXCLUDE EXCLUDING EXECUTE EXEMPT EXISTING EXISTSNODE EXP
        EXPAND EXPAND_GSET_TO_UNION EXPAND_TABLE EXPIRE EXPLAIN EXPLOSION EXPORT EXPRESS
        EXPR_CORR_CHECK EXTEND EXTENDED EXTENDS EXTENT EXTENTS EXTERNAL EXTERNALLY
        EXTRA EXTRACT EXTRACTCLOBXML EXTRACTVALUE FACILITY FACT FACTOR FACTORIZE_JOIN
        FAILED FAILED_LOGIN_ATTEMPTS FAILGROUP FAILOVER FAILURE FALSE FAMILY FAR FAST
        FBTSCAN FEATURE FEATURE_COMPARE FEATURE_DETAILS FEATURE_ID FEATURE_SET FEATURE_VALUE
        FEDERATION FETCH FILE FILEGROUP FILESTORE FILESYSTEM_LIKE_LOGGING FILE_NAME_CONVERT
        FILTER FINAL FINE FINISH FIRST FIRSTM FIRST_ROWS FIRST_VALUE FIXED_VIEW_DATA
        FLAGGER FLASHBACK FLASH_CACHE FLEX FLOB FLOOR FLUSH FOLDER FOLLOWING FOLLOWS
        FORCE FORCE_JSON_TABLE_TRANSFORM FORCE_SPATIAL FORCE_XML_QUERY_REWRITE FOREIGN
        FOREVER FORMAT FORWARD FRAGMENT_NUMBER FREE FREELIST FREELISTS FREEPOOLS FRESH
        FRESH_MV FROM_TZ FTP FULL FULL_OUTER_JOIN_TO_OUTER FUNCTION FUNCTIONS GATHER_OPTIMIZER_STATISTICS
        GATHER_PLAN_STATISTICS GBY_CONC_ROLLUP GBY_PUSHDOWN GENERATED GET GLOBAL GLOBALLY
        GLOBAL_NAME GLOBAL_TOPIC_ENABLED GOLDENGATE GOTO GRANTED GRANULAR GREATEST
        GROUPING GROUPING_ID GROUPS GROUP_BY GROUP_ID GUARANTEE GUARANTEED GUARD H
        HALF_YEARS HASH HASHING HASHKEYS HASHSET_BUILD HASH_AJ HASH_SJ HEADER HEAP
        HELP HEXTORAW HEXTOREF HIDDEN HIDE HIERARCHICAL HIERARCHIES HIERARCHY HIER_ANCESTOR
        HIER_CAPTION HIER_CHILDREN HIER_CHILD_COUNT HIER_COLUMN HIER_CONDITION HIER_DEPTH
        HIER_DESCRIPTION HIER_HAS_CHILDREN HIER_LAG HIER_LEAD HIER_LEVEL HIER_MEMBER_NAME
        HIER_MEMBER_UNIQUE_NAME HIER_ORDER HIER_PARENT HIER_WINDOW HIGH HINTSET_BEGIN
        HINTSET_END HOST HOT HOUR HOURS HTTP HWM_BROKERED HYBRID ID IDENTIFIER IDENTITY
        IDGENERATORS IDLE IDLE_TIME IF IGNORE IGNORE_OPTIM_EMBEDDED_HINTS IGNORE_ROW_ON_DUPKEY_INDEX
        IGNORE_WHERE_CLAUSE ILM IMMEDIATE IMMUTABLE IMPACT IMPORT INACTIVE INACTIVE_ACCOUNT_TIME
        INCLUDE INCLUDES INCLUDE_VERSION INCLUDING INCOMING INCR INCREMENT INCREMENTAL
        INDENT INDEXED INDEXES INDEXING INDEXTYPE INDEXTYPES INDEX_ASC INDEX_COMBINE
        INDEX_DESC INDEX_FFS INDEX_FILTER INDEX_JOIN INDEX_ROWS INDEX_RRS INDEX_RS
        INDEX_RS_ASC INDEX_RS_DESC INDEX_SCAN INDEX_SKIP_SCAN INDEX_SS INDEX_SS_ASC
        INDEX_SS_DESC INDEX_STATS INDICATOR INFINITE INFORMATIONAL INHERIT INITCAP
        INITIAL INITIALIZED INITIALLY INITRANS INLINE INLINE_XMLTYPE_NT INLINE_XT
        INMEMORY INMEMORY_PRUNING INNER INPLACE INSENSITIVE INSERTCHILDXML INSERTCHILDXMLAFTER
        INSERTCHILDXMLBEFORE INSERTXMLAFTER INSERTXMLBEFORE INSTALL INSTANCE INSTANCES
        INSTANTIABLE INSTANTLY INSTEAD INSTR INSTR2 INSTR4 INSTRB INSTRC INTERLEAVED
        INTERMEDIATE INTERNAL_CONVERT INTERNAL_USE INTERPRETED INTRA_CDB INVALIDATE
        INVALIDATION INVISIBLE IN_MEMORY_METADATA IN_XQUERY IOSEEKTIM IOTFRSPEED IO_LOGICAL
        IO_MEGABYTES IO_REQUESTS ISOLATE ISOLATION ISOLATION_LEVEL ITERATE ITERATION_NUMBER
        JAVA JOB JOIN JSON JSONGET JSONPARSE JSONTOXML JSON_ARRAY JSON_ARRAYAGG JSON_EQUAL
        JSON_EQUAL2 JSON_EXISTS JSON_EXISTS2 JSON_HASH JSON_LENGTH JSON_MERGEPATCH
        JSON_MKMVI JSON_OBJECT JSON_OBJECTAGG JSON_PATCH JSON_QUERY JSON_SCALAR JSON_SERIALIZE
        JSON_TABLE JSON_TEXTCONTAINS JSON_TEXTCONTAINS2 JSON_TRANSFORM JSON_VALUE
        KEEP KEEP_DUPLICATES KERBEROS KEY KEYS KEYSIZE KEYSTORE KEY_LENGTH KILL
        KURTOSIS_POP KURTOSIS_SAMP LABEL LAG LAG_DIFF LAG_DIFF_PERCENT LANGUAGE LAST
        LAST_DAY LAST_VALUE LATERAL LAX LAYER LDAP_REGISTRATION LDAP_REGISTRATION_ENABLED
        LDAP_REG_SYNC_INTERVAL LEAD LEADING LEAD_CDB LEAD_CDB_URI LEAD_DIFF LEAD_DIFF_PERCENT
        LEAF LEAST LEAVES LEDGER LEFT LENGTH LENGTH2 LENGTH4 LENGTHB LENGTHC LESS
        LEVEL LEVELS LIBRARY LIFE LIFECYCLE LIFETIME LIKE2 LIKE4 LIKEC LIMIT LINEAR
        LINK LIST LISTAGG LN LNNVL LOAD LOB LOBNVL LOBS LOB_VALUE LOCALTIME LOCALTIMESTAMP
        LOCAL_INDEXES LOCATION LOCATOR LOCKDOWN LOCKED LOCKING LOG LOGFILE LOGFILES
        LOGGING LOGICAL LOGICAL_READS_PER_CALL LOGICAL_READS_PER_SESSION LOGMINING
        LOGOFF LOGON LOG_READ_ONLY_VIOLATIONS LOST LOW LOWER LPAD LTRIM MAIN MAKE_REF
        MANAGE MANAGED MANAGEMENT MANAGER MANDATORY MANUAL MAP MAPPER MAPPING MASTER
        MATCH MATCHED MATCHES MATCH_NUMBER MATCH_RECOGNIZE MATERIALIZE MATERIALIZED
        MATRIX MAX MAXARCHLOGS MAXDATAFILES MAXEXTENTS MAXIMIZE MAXINSTANCES MAXLOGFILES
        MAXLOGHISTORY MAXLOGMEMBERS MAXSIZE MAXTRANS MAXVALUE MAX_AUDIT_SIZE MAX_DIAG_SIZE
        MAX_PDB_SNAPSHOTS MAX_SHARED_TEMP_SIZE MBRC MEASURE MEASURES MEDIAN MEDIUM
        MEMBER MEMCOMPRESS MEMOPTIMIZE MEMOPTIMIZE_WRITE MEMORY MERGE MERGE$ACTIONS
        MERGE_AJ MERGE_CONST_ON MERGE_SJ METADATA METADATA_SOURCE_PDB METHOD MIGRATE
        MIGRATE_CROSS_CON MIGRATION MIN MINEXTENTS MINIMIZE MINIMUM MINING MINUS_NULL
        MINUTE MINUTES MINVALUE MIRROR MIRRORCOLD MIRRORHOT MISMATCH MISSING MLE MLSLABEL
        MOD MODEL MODEL_COMPILE_SUBQUERY MODEL_DONTVERIFY_UNIQUENESS MODEL_DYNAMIC_SUBQUERY
        MODEL_MIN_ANALYSIS MODEL_NB MODEL_NO_ANALYSIS MODEL_PBY MODEL_PUSH_REF MODEL_SV
        MODIFICATION MODIFY MODIFY_COLUMN_TYPE MODULE MONITOR MONITORING MONTHS MONTHS_BETWEEN
        MOUNT MOUNTPATH MOUNTPOINT MOVE MOVEMENT MULTIDIMENSIONAL MULTISET MULTIVALUE
        MV_MERGE NAME NAMED NAMES NAMESPACE NAN NANVL NATIVE NATIVE_FULL_OUTER_JOIN
        NATURAL NAV NCHAR_CS NCHR NEEDED NEG NESTED NESTED_ROLLUP_TOP NESTED_TABLE_FAST_INSERT
        NESTED_TABLE_GET_REFS NESTED_TABLE_ID NESTED_TABLE_SET_REFS NESTED_TABLE_SET_SETID
        NETWORK NEVER NEW NEW_TIME NEXT NEXT_DAY NLJ_BATCHING NLJ_INDEX_FILTER NLJ_INDEX_SCAN
        NLJ_PREFETCH NLSSORT NLS_CALENDAR NLS_CHARACTERSET NLS_CHARSET_DECL_LEN NLS_CHARSET_ID
        NLS_CHARSET_NAME NLS_COLLATION_ID NLS_COLLATION_NAME NLS_COMP NLS_CURRENCY
        NLS_DATE_FORMAT NLS_DATE_LANGUAGE NLS_INITCAP NLS_ISO_CURRENCY NLS_LANG NLS_LANGUAGE
        NLS_LENGTH_SEMANTICS NLS_LOWER NLS_NCHAR_CONV_EXCP NLS_NUMERIC_CHARACTERS
        NLS_SORT NLS_SPECIAL_CHARS NLS_TERRITORY NLS_UPPER NL_AJ NL_SJ NO NOAPPEND
        NOARCHIVELOG NOAUDIT NOCACHE NOCOPY NOCPU_COSTING NOCYCLE NODELAY NOENTITYESCAPING
        NOEXTEND NOFORCE NOGUARANTEE NOKEEP NOLOCAL NOLOGGING NOMAPPING NOMAXVALUE
        NOMINIMIZE NOMINVALUE NOMONITORING NONBLOCKING NONE NONEDITIONABLE NONPARTITIONED
        NONSCHEMA NOORDER NOOVERRIDE NOPARALLEL NOPARALLEL_INDEX NORELOCATE NORELY
        NOREPAIR NOREPLAY NORESETLOGS NOREVERSE NOREWRITE NORMAL NOROWDEPENDENCIES
        NOSCALE NOSCHEMACHECK NOSEGMENT NOSHARD NOSORT NOSTRICT NOSWITCH NOTHING NOTIFICATION
        NOVALIDATE NOW NO_ACCESS NO_ADAPTIVE_PLAN NO_ANSI_REARCH NO_ANSWER_QUERY_USING_STATS
        NO_AUTO_REOPTIMIZE NO_BAND_JOIN NO_BASETABLE_MULTIMV_REWRITE NO_BATCH_TABLE_ACCESS_BY_ROWID
        NO_BIND_AWARE NO_BUFFER NO_BUSHY_JOIN NO_CARTESIAN NO_CHECK_ACL_REWRITE NO_CLUSTERING
        NO_CLUSTER_BY_ROWID NO_COALESCE_SQ NO_COMMON_DATA NO_CONNECT_BY_CB_WHR_ONLY
        NO_CONNECT_BY_COMBINE_SW NO_CONNECT_BY_COST_BASED NO_CONNECT_BY_ELIM_DUPS
        NO_CONNECT_BY_FILTERING NO_CONTAINERS NO_COST_XML_QUERY_REWRITE NO_CPU_COSTING
        NO_CROSS_CONTAINER NO_DAGG_OPTIM_GSETS NO_DATA_SECURITY_REWRITE NO_DECORRELATE
        NO_DIST_AGG_PROLLUP_PUSHDOWN NO_DOMAIN_INDEX_FILTER NO_DST_UPGRADE_INSERT_CONV
        NO_ELIMINATE_JOIN NO_ELIMINATE_OBY NO_ELIMINATE_OUTER_JOIN NO_ELIMINATE_SQ
        NO_ELIM_GROUPBY NO_EXPAND NO_EXPAND_GSET_TO_UNION NO_EXPAND_TABLE NO_FACT
        NO_FACTORIZE_JOIN NO_FILTERING NO_FULL_OUTER_JOIN_TO_OUTER NO_GATHER_OPTIMIZER_STATISTICS
        NO_GBY_PUSHDOWN NO_INDEX NO_INDEX_FFS NO_INDEX_SS NO_INMEMORY NO_INMEMORY_PRUNING
        NO_JSON_TABLE_TRANSFORM NO_LOAD NO_MERGE NO_MODEL_PUSH_REF NO_MONITOR NO_MONITORING
        NO_MULTIMV_REWRITE NO_NATIVE_FULL_OUTER_JOIN NO_NLJ_BATCHING NO_NLJ_PREFETCH
        NO_OBJECT_LINK NO_OBY_GBYPD_SEPARATE NO_ORDER_ROLLUPS NO_OR_EXPAND NO_OUTER_JOIN_TO_ANTI
        NO_OUTER_JOIN_TO_INNER NO_PARALLEL NO_PARALLEL_INDEX NO_PARTIAL_COMMIT NO_PARTIAL_JOIN
        NO_PARTIAL_OSON_UPDATE NO_PARTIAL_ROLLUP_PUSHDOWN NO_PLACE_DISTINCT NO_PLACE_GROUP_BY
        NO_PQ_CONCURRENT_UNION NO_PQ_EXPAND_TABLE NO_PQ_MAP NO_PQ_NONLEAF_SKEW NO_PQ_REPLICATE
        NO_PQ_SKEW NO_PRUNE_GSETS NO_PULL_PRED NO_PUSH_HAVING_TO_GBY NO_PUSH_PRED
        NO_PUSH_SUBQ NO_PX_FAULT_TOLERANCE NO_PX_JOIN_FILTER NO_QKN_BUFF NO_QUERY_TRANSFORMATION
        NO_REF_CASCADE NO_REORDER_WIF NO_RESULT_CACHE NO_REWRITE NO_ROOT_SW_FOR_LOCAL
        NO_SEMIJOIN NO_SEMI_TO_INNER NO_SET_GBY_PUSHDOWN NO_SET_TO_JOIN NO_SQL_TRANSLATION
        NO_SQL_TUNE NO_STAR_TRANSFORMATION NO_STATEMENT_QUEUING NO_STATS_GSETS NO_SUBQUERY_PRUNING
        NO_SUBSTRB_PAD NO_SWAP_JOIN_INPUTS NO_TABLE_LOOKUP_BY_NL NO_TEMP_TABLE NO_TRANSFORM_DISTINCT_AGG
        NO_UNNEST NO_USE_CUBE NO_USE_DAGG_UNION_ALL_GSETS NO_USE_HASH NO_USE_HASH_AGGREGATION
        NO_USE_HASH_GBY_FOR_DAGGPSHD NO_USE_HASH_GBY_FOR_PUSHDOWN NO_USE_INVISIBLE_INDEXES
        NO_USE_MERGE NO_USE_NL NO_USE_PARTITION_WISE_DISTINCT NO_USE_PARTITION_WISE_GBY
        NO_USE_PARTITION_WISE_WIF NO_USE_SCALABLE_GBY_INVDIST NO_USE_VECTOR_AGGREGATION
        NO_VECTOR_TRANSFORM NO_VECTOR_TRANSFORM_DIMS NO_VECTOR_TRANSFORM_FACT NO_XDB_FASTPATH_INSERT
        NO_XMLINDEX_REWRITE NO_XMLINDEX_REWRITE_IN_SELECT NO_XML_DML_REWRITE NO_XML_QUERY_REWRITE
        NO_ZONEMAP NTH_VALUE NTILE NULLIF NULLS NUMTODSINTERVAL NUMTOYMINTERVAL NUM_INDEX_KEYS
        NVL NVL2 OBJECT OBJECT2XML OBJNO OBJNO_REUSE OBJ_ID OBY_GBYPD_SEPARATE OCCURENCES
        OCCURRENCES ODD OFF OFFLINE OFFSET OID OIDINDEX OLAP OLD OLD_PUSH_PRED OLS
        OLTP OMIT ONE ONLINE ONLY OPAQUE OPAQUE_TRANSFORM OPAQUE_XCANONICAL OPCODE
        OPEN OPERATIONS OPERATOR OPTIMAL OPTIMIZE OPTIMIZER_FEATURES_ENABLE OPTIMIZER_GOAL
        OPT_ESTIMATE OPT_PARAM ORADEBUG ORA_BRANCH ORA_CHECK_ACL ORA_CHECK_PRIVILEGE
        ORA_CHECK_SYS_PRIVILEGE ORA_CLUSTERING ORA_CONCAT_RWKEY ORA_DM_PARTITION_NAME
        ORA_DST_AFFECTED ORA_DST_CONVERT ORA_DST_ERROR ORA_GET_ACLIDS ORA_GET_PRIVILEGES
        ORA_HASH ORA_INVOKING_USER ORA_INVOKING_USERID ORA_INVOKING_XS_USER ORA_INVOKING_XS_USER_GUID
        ORA_NORMALIZE ORA_PARTITION_VALIDATION ORA_RAWCOMPARE ORA_RAWCONCAT ORA_ROWSCN
        ORA_ROWSCN_RAW ORA_ROWVERSION ORA_SEARCH_RWKEY ORA_SHARDSPACE_NAME ORA_SHARD_ID
        ORA_TABVERSION ORA_WRITE_TIME ORDERED ORDERED_PREDICATES ORDER_KEY_VECTOR_USE
        ORDER_SUBQ ORDINALITY ORGANIZATION OR_EXPAND OR_PREDICATES OSON OSON_DIAG
        OSON_GET_CONTENT OTHER OTHERS OUTER OUTER_JOIN_TO_ANTI OUTER_JOIN_TO_INNER
        OUTLINE OUTLINE_LEAF OUT_OF_LINE OVER OVERFLOW OVERFLOW_NOMOVE OVERLAPS OWN
        OWNER OWNERSHIP PACKAGE PACKAGES PARALLEL PARALLEL_INDEX PARAM PARAMETERS
        PARENT PARITY PART$NUM$INST PARTIAL PARTIALLY PARTIAL_JOIN PARTIAL_ROLLUP_PUSHDOWN
        PARTITION PARTITIONING PARTITIONS PARTITIONSET PARTITION_CONTAINED PARTITION_HASH
        PARTITION_LIST PARTITION_RANGE PASSING PASSIVE PASSWORD PASSWORDFILE_METADATA_CACHE
        PASSWORD_GRACE_TIME PASSWORD_LIFE_TIME PASSWORD_LOCK_TIME PASSWORD_REUSE_MAX
        PASSWORD_REUSE_TIME PASSWORD_ROLLOVER_TIME PASSWORD_VERIFY_FUNCTION PAST PATCH
        PATH PATHS PATH_PREFIX PATTERN PBL_HS_BEGIN PBL_HS_END PCTINCREASE PCTTHRESHOLD
        PCTUSED PCTVERSION PDB_LOCAL_ONLY PEER PEERS PENDING PER PERCENT PERCENTAGE
        PERCENTILE_CONT PERCENTILE_DISC PERCENT_RANK PERCENT_RANKM PERFORMANCE PERIOD
        PERMANENT PERMISSION PERMUTE PERSISTABLE PFILE PHV PHYSICAL PIKEY PIVOT PIV_GB
        PIV_SSF PLACE_DISTINCT PLACE_GROUP_BY PLAN PLSCOPE_SETTINGS PLSQL_CCFLAGS
        PLSQL_CODE_TYPE PLSQL_DEBUG PLSQL_OPTIMIZE_LEVEL PLSQL_WARNINGS PLUGGABLE
        PMEM POINT POLICY POOL_16K POOL_2K POOL_32K POOL_4K POOL_8K PORT POSITION
        POST_TRANSACTION POWER POWERMULTISET POWERMULTISET_BY_CARDINALITY PQ_CONCURRENT_UNION
        PQ_DISTRIBUTE PQ_DISTRIBUTE_WINDOW PQ_EXPAND_TABLE PQ_FILTER PQ_MAP PQ_NOMAP
        PQ_NONLEAF_SKEW PQ_REPLICATE PQ_SKEW PREBUILT PRECEDES PRECEDING PRECOMPUTE_SUBQUERY
        PREDICATE_REORDERS PREDICTION PREDICTION_BOUNDS PREDICTION_COST PREDICTION_DETAILS
        PREDICTION_PROBABILITY PREDICTION_SET PRELOAD PREPARE PRESENT PRESENTNNV PRESENTV
        PRESERVE PRESERVE_OID PRETTY PREV PREVIOUS PRIMARY PRINTBLOBTOCLOB PRIORITY
        PRIVATE PRIVATE_SGA PRIVILEGE PRIVILEGED PRIVILEGES PROCEDURAL PROCEDURE PROCESS
        PROFILE PROGRAM PROJECT PROPAGATE PROPAGATION PROPERTY PROTECTED PROTECTION
        PROTOCOL PROXY PRUNING PULL_PRED PURGE PUSH_HAVING_TO_GBY PUSH_PRED PUSH_SUBQ
        PX_FAULT_TOLERANCE PX_GRANULE PX_JOIN_FILTER QB_NAME QUALIFY QUARANTINE QUARTERS
        QUERY QUERY_BLOCK QUEUE QUEUE_CURR QUEUE_ROWP QUIESCE QUORUM QUOTA QUOTAGROUP
        QUOTES RANDOM RANDOM_LOCAL RANGE RANK RANKM RAPIDLY RATIO_TO_REPORT RAWTOHEX
        RAWTONHEX RAWTOREF RBA RBO_OUTLINE RDBA READ READS READ_OR_WRITE REALM REBALANCE
        REBUILD RECONNECT RECORDS_PER_BLOCK RECOVER RECOVERABLE RECOVERY RECYCLE RECYCLEBIN
        REDACTION REDEFINE REDO REDUCED REDUNDANCY REFERENCE REFERENCED REFERENCES
        REFERENCING REFRESH REFTOHEX REFTORAW REF_CASCADE_CURSOR REGEXP_COUNT REGEXP_INSTR
        REGEXP_LIKE REGEXP_REPLACE REGEXP_SUBSTR REGISTER REGR_AVGX REGR_AVGY REGR_COUNT
        REGR_INTERCEPT REGR_R2 REGR_SLOPE REGR_SXX REGR_SXY REGR_SYY REGULAR REJECT
        REKEY RELATIONAL RELOCATE RELY REMAINDER REMOTE REMOTE_MAPPED REMOVE REORDER_WIF
        REPAIR REPEAT REPLACE REPLICATION REQUIRED RESERVOIR_SAMPLING RESET RESETLOGS
        RESIZE RESOLVE RESOLVER RESPECT RESTART RESTORE RESTORE_AS_INTERVALS RESTRICT
        RESTRICTED RESTRICT_ALL_REF_CONS RESULT_CACHE RESUMABLE RESUME RETENTION RETRY_ON_ROW_CHANGE
        RETURN RETURNING REUSE REVERSE REWRITE REWRITE_OR_ERROR RIGHT RLS_FORCE ROLE
        ROLES ROLESET ROLLBACK ROLLING ROLLOVER ROLLUP ROOT ROUND ROUND_TIES_TO_EVEN
        ROW ROWDEPENDENCIES ROWIDTOCHAR ROWIDTONCHAR ROWID_MAPPING_TABLE ROWNUM ROWS
        ROW_LENGTH ROW_NUMBER RPAD RTRIM RULE RULES RUNNING SALT SAMPLE SAVE SAVEPOINT
        SAVE_AS_INTERVALS SB4 SCALAR SCALARS SCALE SCALE_ROWS SCAN SCAN_INSTANCES
        SCHEDULER SCHEMA SCHEMACHECK SCN SCN_ASCENDING SCOPE SCRUB SDO_GEOM_KEY SDO_GEOM_MAX_X
        SDO_GEOM_MAX_Y SDO_GEOM_MAX_Z SDO_GEOM_MBB SDO_GEOM_MBR SDO_GEOM_MIN_X SDO_GEOM_MIN_Y
        SDO_GEOM_MIN_Z SDO_TOLERANCE SD_ALL SD_INHIBIT SD_SHOW SEARCH SECONDS SECRET
        SECUREFILE SECUREFILE_DBA SECURITY SEED SEGMENT SEG_BLOCK SEG_FILE SELECTIVITY
        SELF SEMIJOIN SEMIJOIN_DRIVER SEMI_TO_INNER SENSITIVE SEQUENCE SEQUENCED SEQUENTIAL
        SERIAL SERIALIZABLE SERVERERROR SERVICE SERVICES SERVICE_NAME_CONVERT SESSION
        SESSIONS_PER_USER SESSIONTIMEZONE SESSIONTZNAME SESSION_CACHED_CURSORS SETS
        SETTINGS SET_GBY_PUSHDOWN SET_TO_JOIN SEVERE SHARD SHARDED SHARDS SHARDSPACE
        SHARD_CHUNK_ID SHARED SHARED_POOL SHARE_OF SHARING SHD$COL$MAP SHELFLIFE SHOW
        SHRINK SHUTDOWN SIBLING SIBLINGS SID SIGN SIGNAL_COMPONENT SIGNAL_FUNCTION
        SIGNATURE SIMPLE SIN SINGLE SINGLETASK SINH SITE SKEWNESS_POP SKEWNESS_SAMP
        SKIP SKIP_EXT_OPTIMIZER SKIP_PROXY SKIP_UNQ_UNUSABLE_IDX SKIP_UNUSABLE_INDEXES
        SMALLFILE SNAPSHOT SOME SORT SOUNDEX SOURCE SOURCE_FILE_DIRECTORY SOURCE_FILE_NAME_CONVERT
        SPACE SPATIAL SPECIFICATION SPFILE SPLIT SPREADSHEET SQL SQLLDR SQL_SCOPE
        SQL_TRACE SQL_TRANSLATION_PROFILE SQRT STALE STANDALONE STANDARD_HASH STANDBY
        STANDBYS STANDBY_MAX_DATA_DELAY STAR STARTUP STAR_TRANSFORMATION STATE STATEMENT
        STATEMENTS STATEMENT_ID STATEMENT_QUEUING STATIC STATISTICS STATS_BINOMIAL_TEST
        STATS_CROSSTAB STATS_F_TEST STATS_KS_TEST STATS_MODE STATS_MW_TEST STATS_ONE_WAY_ANOVA
        STATS_T_TEST_INDEP STATS_T_TEST_INDEPU STATS_T_TEST_ONE STATS_T_TEST_PAIRED
        STATS_WSR_TEST STDDEV STDDEV_POP STDDEV_SAMP STOP STORAGE STORAGE_INDEX STORE
        STREAM STREAMS STRICT STRING STRINGS STRIP STRIPE_COLUMNS STRIPE_WIDTH STRUCTURE
        SUBMULTISET SUBPARTITION SUBPARTITIONING SUBPARTITIONS SUBPARTITION_REL SUBQUERIES
        SUBQUERY_PRUNING SUBSCRIBE SUBSET SUBSTITUTABLE SUBSTR SUBSTR2 SUBSTR4 SUBSTRB
        SUBSTRC SUBTYPE SUCCESS SUCCESSFUL SUM SUMMARY SUPPLEMENTAL SUPPRESS_LOAD
        SUSPEND SWAP_JOIN_INPUTS SWITCH SWITCHOVER SYNC SYNCHRONOUS SYSASM SYSAUX
        SYSBACKUP SYSDATE SYSDBA SYSDG SYSGUID SYSKM SYSOBJ SYSOPER SYSRAC SYSTEM
        SYSTEM_DEFINED SYSTEM_STATS SYSTIMESTAMP SYS_AUDIT SYS_CHECKACL SYS_CHECK_PRIVILEGE
        SYS_CONNECT_BY_PATH SYS_CONS_ANY_SCALAR SYS_CONTEXT SYS_CTXINFOPK SYS_CTX_CONTAINS2
        SYS_CTX_MKIVIDX SYS_DBURIGEN SYS_DL_CURSOR SYS_DM_RXFORM_CHR SYS_DM_RXFORM_LAB
        SYS_DM_RXFORM_NUM SYS_DOM_COMPARE SYS_DST_PRIM2SEC SYS_DST_SEC2PRIM SYS_ET_BFILE_TO_RAW
        SYS_ET_BLOB_TO_IMAGE SYS_ET_IMAGE_TO_BLOB SYS_ET_RAW_TO_BFILE SYS_EXTPDTXT
        SYS_EXTRACT_UTC SYS_FBT_INSDEL SYS_FILTER_ACLS SYS_FNMATCHES SYS_FNREPLACE
        SYS_GETTOKENID SYS_GETXTIVAL SYS_GET_ACLIDS SYS_GET_ANY_SCALAR SYS_GET_COL_ACLIDS
        SYS_GET_PRIVILEGES SYS_GUID SYS_MAKEXML SYS_MAKE_XMLNODEID SYS_MKXMLATTR SYS_MKXTI
        SYS_OPTLOBPRBSC SYS_OPTXICMP SYS_OPTXQCASTASNQ SYS_OP_ADT2BIN SYS_OP_ADTCONS
        SYS_OP_ALSCRVAL SYS_OP_ATG SYS_OP_BIN2ADT SYS_OP_BITVEC SYS_OP_BL2R SYS_OP_BLOOM_FILTER
        SYS_OP_BLOOM_FILTER_LIST SYS_OP_C2C SYS_OP_CAST SYS_OP_CEG SYS_OP_CL2C SYS_OP_COMBINED_HASH
        SYS_OP_COMP SYS_OP_CONVERT SYS_OP_COUNTCHG SYS_OP_CSCONV SYS_OP_CSCONVTEST
        SYS_OP_CSR SYS_OP_CSX_PATCH SYS_OP_CYCLED_SEQ SYS_OP_DECOMP SYS_OP_DESCEND
        SYS_OP_DISTINCT SYS_OP_DRA SYS_OP_DSB_DESERIALIZE SYS_OP_DSB_SERIALIZE SYS_OP_DUMP
        SYS_OP_DV_CHECK SYS_OP_ENFORCE_NOT_NULL$ SYS_OP_EXTRACT SYS_OP_GROUPING SYS_OP_GUID
        SYS_OP_HASH SYS_OP_HCS_TABLE SYS_OP_IIX SYS_OP_INTERVAL_HIGH_BOUND SYS_OP_ITR
        SYS_OP_KEY_VECTOR_CREATE SYS_OP_KEY_VECTOR_FILTER SYS_OP_KEY_VECTOR_FILTER_LIST
        SYS_OP_KEY_VECTOR_PAYLOAD SYS_OP_KEY_VECTOR_SUCCEEDED SYS_OP_KEY_VECTOR_USE
        SYS_OP_LBID SYS_OP_LOBLOC2BLOB SYS_OP_LOBLOC2CLOB SYS_OP_LOBLOC2ID SYS_OP_LOBLOC2NCLOB
        SYS_OP_LOBLOC2TYP SYS_OP_LSVI SYS_OP_LVL SYS_OP_MAKEOID SYS_OP_MAP_NONNULL
        SYS_OP_MSR SYS_OP_NICOMBINE SYS_OP_NIEXTRACT SYS_OP_NII SYS_OP_NIX SYS_OP_NOEXPAND
        SYS_OP_NTCIMG$ SYS_OP_NUMTORAW SYS_OP_OBJ_UPD_IN_TXN SYS_OP_OIDVALUE SYS_OP_OPNSIZE
        SYS_OP_PAR SYS_OP_PARGID SYS_OP_PARGID_1 SYS_OP_PART_ID SYS_OP_PAR_1 SYS_OP_PIVOT
        SYS_OP_R2O SYS_OP_RAWTONUM SYS_OP_RDTM SYS_OP_REF SYS_OP_RMTD SYS_OP_ROWIDTOOBJ
        SYS_OP_RPB SYS_OP_TOSETID SYS_OP_TPR SYS_OP_TRTB SYS_OP_UNDESCEND SYS_OP_VECAND
        SYS_OP_VECBIT SYS_OP_VECOR SYS_OP_VECTOR_GROUP_BY SYS_OP_VECXOR SYS_OP_VERSION
        SYS_OP_VREF SYS_OP_VVD SYS_OP_XMLCONS_FOR_CSX SYS_OP_XPTHATG SYS_OP_XPTHIDX
        SYS_OP_XPTHOP SYS_OP_XTNN SYS_OP_XTXT2SQLT SYS_OP_ZONE_ID SYS_ORDERKEY_DEPTH
        SYS_ORDERKEY_MAXCHILD SYS_ORDERKEY_PARENT SYS_PARALLEL_TXN SYS_PATHID_IS_ATTR
        SYS_PATHID_IS_NMSPC SYS_PATHID_LASTNAME SYS_PATHID_LASTNMSPC SYS_PATH_REVERSE
        SYS_PLSQL_COUNT SYS_PLSQL_CPU SYS_PLSQL_IO SYS_PXQEXTRACT SYS_RAW_TO_XSID
        SYS_REMAP_XMLTYPE SYS_RID_ORDER SYS_ROW_DELTA SYS_SC_2_XMLT SYS_SYNRCIREDO
        SYS_TYPEID SYS_UMAKEXML SYS_XMLANALYZE SYS_XMLCONTAINS SYS_XMLCONV SYS_XMLEXNSURI
        SYS_XMLGEN SYS_XMLINSTR SYS_XMLI_LOC_ISNODE SYS_XMLI_LOC_ISTEXT SYS_XMLLOCATOR_GETSVAL
        SYS_XMLNODEID SYS_XMLNODEID_GETCID SYS_XMLNODEID_GETLOCATOR SYS_XMLNODEID_GETOKEY
        SYS_XMLNODEID_GETPATHID SYS_XMLNODEID_GETPTRID SYS_XMLNODEID_GETRID SYS_XMLNODEID_GETSVAL
        SYS_XMLNODEID_GETTID SYS_XMLTRANSLATE SYS_XMLTYPE2SQL SYS_XMLT_2_SC SYS_XQBASEURI
        SYS_XQCASTABLEERRH SYS_XQCODEP2STR SYS_XQCODEPEQ SYS_XQCON2SEQ SYS_XQCONCAT
        SYS_XQDELETE SYS_XQDFLTCOLATION SYS_XQDOC SYS_XQDOCURI SYS_XQDURDIV SYS_XQED4URI
        SYS_XQENDSWITH SYS_XQERR SYS_XQERRH SYS_XQESHTMLURI SYS_XQEXLOBVAL SYS_XQEXSTWRP
        SYS_XQEXTRACT SYS_XQEXTRREF SYS_XQEXVAL SYS_XQFB2STR SYS_XQFNBOOL SYS_XQFNCMP
        SYS_XQFNDATIM SYS_XQFNLNAME SYS_XQFNNM SYS_XQFNNSURI SYS_XQFNPREDTRUTH SYS_XQFNQNM
        SYS_XQFNROOT SYS_XQFORMATNUM SYS_XQFTCONTAIN SYS_XQFUNCR SYS_XQGETCONTENT
        SYS_XQINDXOF SYS_XQINSERT SYS_XQINSPFX SYS_XQIRI2URI SYS_XQLANG SYS_XQLLNMFRMQNM
        SYS_XQMKNODEREF SYS_XQNILLED SYS_XQNODENAME SYS_XQNORMSPACE SYS_XQNORMUCODE
        SYS_XQNSP4PFX SYS_XQNSPFRMQNM SYS_XQPFXFRMQNM SYS_XQPOLYABS SYS_XQPOLYADD
        SYS_XQPOLYCEL SYS_XQPOLYCST SYS_XQPOLYCSTBL SYS_XQPOLYDIV SYS_XQPOLYFLR SYS_XQPOLYMOD
        SYS_XQPOLYMUL SYS_XQPOLYRND SYS_XQPOLYSQRT SYS_XQPOLYSUB SYS_XQPOLYUMUS SYS_XQPOLYUPLS
        SYS_XQPOLYVEQ SYS_XQPOLYVGE SYS_XQPOLYVGT SYS_XQPOLYVLE SYS_XQPOLYVLT SYS_XQPOLYVNE
        SYS_XQREF2VAL SYS_XQRENAME SYS_XQREPLACE SYS_XQRESVURI SYS_XQRNDHALF2EVN SYS_XQRSLVQNM
        SYS_XQRYENVPGET SYS_XQRYVARGET SYS_XQRYWRP SYS_XQSEQ2CON SYS_XQSEQ2CON4XC
        SYS_XQSEQDEEPEQ SYS_XQSEQINSB SYS_XQSEQRM SYS_XQSEQRVS SYS_XQSEQSUB SYS_XQSEQTYPMATCH
        SYS_XQSTARTSWITH SYS_XQSTATBURI SYS_XQSTR2CODEP SYS_XQSTRJOIN SYS_XQSUBSTRAFT
        SYS_XQSUBSTRBEF SYS_XQTOKENIZE SYS_XQTREATAS SYS_XQXFORM SYS_XQ_ASQLCNV SYS_XQ_ATOMCNVCHK
        SYS_XQ_NRNG SYS_XQ_PKSQL2XML SYS_XQ_UPKXML2SQL SYS_XSID_TO_RAW SYS_ZMAP_FILTER
        SYS_ZMAP_REFRESH TABAUTH TABLES TABLESPACE TABLESPACE_NO TABLE_LOOKUP_BY_NL
        TABLE_STATS TABNO TAG TAN TANH TARGET TBL$OR$IDX$PART$NUM TEMP TEMPFILE TEMPLATE
        TEMPORARY TEMP_TABLE TENANT_ID TEST TEXT THAN THE THREAD THROUGH TIER TIES
        TIMEOUT TIMES TIMESTAMP_TO_NUMBER TIMEZONE_ABBR TIMEZONE_HOUR TIMEZONE_MINUTE
        TIMEZONE_OFFSET TIMEZONE_REGION TIME_ZONE TIV_GB TIV_SSF TOKEN TOPLEVEL TO_ACLID
        TO_APPROX_COUNT_DISTINCT TO_APPROX_PERCENTILE TO_BINARY_DOUBLE TO_BINARY_FLOAT
        TO_BLOB TO_CHAR TO_CLOB TO_DATE TO_DSINTERVAL TO_ISO_STRING TO_LOB TO_MULTI_BYTE
        TO_NCHAR TO_NCLOB TO_NUMBER TO_SINGLE_BYTE TO_TIME TO_TIMESTAMP TO_TIMESTAMP_TZ
        TO_TIME_TZ TO_UTC_TIMESTAMP_TZ TO_YMINTERVAL TRACE TRACING TRACKING TRAILING
        TRANSACTION TRANSFORM TRANSFORM_DISTINCT_AGG TRANSITION TRANSITIONAL TRANSLATE
        TRANSLATION TRANSPORTABLE TREAT TRIGGERS TRIM TRUE TRUNC TRUNCATE TRUST TRUSTED
        TUNING TX TYPE TYPENAME TYPES TZ_OFFSET UB2 UBA UCS2 UID UNARCHIVED UNBOUND
        UNBOUNDED UNCONDITIONAL UNDER UNDO UNDROP UNIFORM UNINSTALL UNION_ALL UNISTR
        UNITE UNIXTIME UNLIMITED UNLOAD UNLOCK UNMATCHED UNNEST UNNEST_INNERJ_DISTINCT_VIEW
        UNNEST_NOSEMIJ_NODISTINCTVIEW UNNEST_SEMIJ_VIEW UNPACKED UNPIVOT UNPLUG UNPROTECTED
        UNQUIESCE UNRECOVERABLE UNRESTRICTED UNSUBSCRIBE UNTIL UNUSABLE UNUSED UPDATABLE
        UPDATED UPDATEXML UPD_INDEXES UPD_JOININDEX UPGRADE UPPER UPSERT USABLE USAGE
        USE USER USERENV USERGROUP USERS USER_DATA USER_DEFINED USER_RECYCLEBIN USER_TABLESPACES
        USE_ANTI USE_CONCAT USE_CUBE USE_DAGG_UNION_ALL_GSETS USE_HASH USE_HASH_AGGREGATION
        USE_HASH_GBY_FOR_DAGGPSHD USE_HASH_GBY_FOR_PUSHDOWN USE_HIDDEN_PARTITIONS
        USE_INVISIBLE_INDEXES USE_MERGE USE_MERGE_CARTESIAN USE_NL USE_NL_WITH_INDEX
        USE_PARTITION_WISE_DISTINCT USE_PARTITION_WISE_GBY USE_PARTITION_WISE_WIF
        USE_PRIVATE_OUTLINES USE_SCALABLE_GBY_INVDIST USE_SEMI USE_STORED_OUTLINES
        USE_TTT_FOR_GSETS USE_VECTOR_AGGREGATION USE_WEAK_NAME_RESL USING USING_NO_EXPAND
        UTF16BE UTF16LE UTF32 UTF8 V1 V2 VALIDATE VALIDATE_CONVERSION VALIDATION VALID_TIME_END
        VALUE VARIANCE VARRAY VARRAYS VAR_POP VAR_SAMP VECTOR VECTOR_ENCODE VECTOR_READ
        VECTOR_READ_TRACE VECTOR_TRANSFORM VECTOR_TRANSFORM_DIMS VECTOR_TRANSFORM_FACT
        VERIFIER VERIFY VERSION VERSIONING VERSIONS VERSIONS_ENDSCN VERSIONS_ENDTIME
        VERSIONS_OPERATION VERSIONS_STARTSCN VERSIONS_STARTTIME VERSIONS_XID VIEWS
        VIOLATION VIRTUAL VISIBILITY VISIBLE VOLUME VSIZE WAIT WALLET WEEK WEEKS WELLFORMED
        WHEN WHENEVER WHITESPACE WIDTH_BUCKET WINDOW WITHIN WITHOUT WITH_EXPRESSION
        WITH_PLSQL WORK WRAPPED WRAPPER WRITE XDB_FASTPATH_INSERT XID XML XML2OBJECT
        XMLATTRIBUTES XMLCAST XMLCDATA XMLCOLATTVAL XMLCOMMENT XMLCONCAT XMLDIFF XMLELEMENT
        XMLEXISTS XMLEXISTS2 XMLFOREST XMLINDEX_REWRITE XMLINDEX_REWRITE_IN_SELECT
        XMLINDEX_SEL_IDX_TBL XMLISNODE XMLISVALID XMLNAMESPACES XMLPARSE XMLPATCH
        XMLPI XMLQUERY XMLQUERYVAL XMLROOT XMLSCHEMA XMLSERIALIZE XMLTABLE XMLTOJSON
        XMLTOKENSET XMLTRANSFORM XMLTRANSFORMBLOB XMLTSET_DML_ENABLE XML_DIAG XML_DML_RWT_STMT
        XPATHTABLE XS XS_SYS_CONTEXT X_DYN_PRUNE YEARS YES ZONEMAP
        )&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keywords_func&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@keywords_func&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%w(
        ABS ACOS ADD_MONTHS APPROX_COUNT APPROX_COUNT_DISTINCT APPROX_COUNT_DISTINCT_AGG
        APPROX_COUNT_DISTINCT_DETAIL APPROX_MEDIAN APPROX_PERCENTILE APPROX_PERCENTILE_AGG
        APPROX_PERCENTILE_DETAIL APPROX_RANK APPROX_SUM ASCII ASCIISTR ASIN ATAN ATAN2
        AVG BFILENAME BIN_TO_NUM BITAND CARDINALITY CAST CEIL CHARTOROWID CHR CLUSTER_DETAILS
        CLUSTER_DISTANCE CLUSTER_ID CLUSTER_PROBABILITY CLUSTER_SET COALESCE COLLATION
        COLLECT COMPOSE CONCAT CONVERT CON_DBID_TO_ID CON_GUID_TO_ID CON_NAME_TO_ID
        CON_UID_TO_ID CORR COS COSH COUNT COVAR_POP COVAR_SAMP CUME_DIST CURRENT_DATE
        CURRENT_TIMESTAMP CV DATAOBJ_TO_MAT_PARTITION DATAOBJ_TO_PARTITION DBTIMEZONE
        DECODE DECOMPOSE DENSE_RANK DEPTH DEREF DUMP EMPTY_BLOB EMPTY_CLOB EXISTSNODE
        EXP EXTRACT EXTRACTVALUE FEATURE_COMPARE FEATURE_DETAILS FEATURE_ID FEATURE_SET
        FEATURE_VALUE FIRST FIRST_VALUE FLOOR FROM_TZ GREATEST GROUPING GROUPING_ID
        GROUP_ID HEXTORAW INITCAP INSTR ITERATION_NUMBER JSON_ARRAY JSON_ARRAYAGG
        JSON_OBJECT JSON_OBJECTAGG JSON_QUERY JSON_TABLE JSON_VALUE LAG LAST LAST_DAY
        LAST_VALUE LEAD LEAST LENGTH LISTAGG LN LNNVL LOCALTIMESTAMP LOG LOWER LPAD
        LTRIM MAKE_REF MAX MEDIAN MIN MOD MONTHS_BETWEEN NANVL NCHR NEW_TIME NEXT_DAY
        NLSSORT NLS_CHARSET_DECL_LEN NLS_CHARSET_ID NLS_CHARSET_NAME NLS_COLLATION_ID
        NLS_COLLATION_NAME NLS_INITCAP NLS_LOWER NLS_UPPER NTH_VALUE NTILE NULLIF
        NUMTODSINTERVAL NUMTOYMINTERVAL NVL NVL2 ORA_DM_PARTITION_NAME ORA_DST_AFFECTED
        ORA_DST_CONVERT ORA_DST_ERROR ORA_HASH ORA_INVOKING_USER ORA_INVOKING_USERID
        PATH PERCENTILE_CONT PERCENTILE_DISC PERCENT_RANK POWER POWERMULTISET POWERMULTISET_BY_CARDINALITY
        PREDICTION PREDICTION_BOUNDS PREDICTION_COST PREDICTION_DETAILS PREDICTION_PROBABILITY
        PREDICTION_SET PRESENTNNV PRESENTV PREVIOUS RANK RATIO_TO_REPORT RAWTOHEX
        RAWTONHEX REFTOHEX REGEXP_COUNT REGEXP_INSTR REGEXP_REPLACE REGEXP_SUBSTR
        REMAINDER REPLACE ROUND ROUND ROWIDTOCHAR ROWIDTONCHAR ROW_NUMBER RPAD RTRIM
        SCN_TO_TIMESTAMP SESSIONTIMEZONE SET SIGN SIN SINH SOUNDEX SQRT STANDARD_HASH
        STATS_BINOMIAL_TEST STATS_CROSSTAB STATS_F_TEST STATS_KS_TEST STATS_MODE STATS_MW_TEST
        STATS_ONE_WAY_ANOVA STATS_WSR_TEST STDDEV STDDEV_POP STDDEV_SAMP SUBSTR SUM
        SYSDATE SYSTIMESTAMP SYS_CONNECT_BY_PATH SYS_CONTEXT SYS_DBURIGEN SYS_EXTRACT_UTC
        SYS_GUID SYS_OP_ZONE_ID SYS_TYPEID SYS_XMLAGG SYS_XMLGEN TAN TANH TIMESTAMP_TO_SCN
        TO_APPROX_COUNT_DISTINCT TO_APPROX_PERCENTILE TO_BINARY_DOUBLE TO_BINARY_FLOAT
        TO_BLOB TO_CHAR TO_CLOB TO_DATE TO_DSINTERVAL TO_LOB TO_MULTI_BYTE TO_NCHAR
        TO_NCLOB TO_NUMBER TO_SINGLE_BYTE TO_TIMESTAMP TO_TIMESTAMP_TZ TO_YMINTERVAL
        TRANSLATE TREAT TRIM TRUNC TZ_OFFSET UID UNISTR UPPER USER USERENV VALIDATE_CONVERSION
        VALUE VARIANCE VAR_POP VAR_SAMP VSIZE WIDTH_BUCKET XMLAGG XMLCAST XMLCDATA
        XMLCOLATTVAL XMLCOMMENT XMLCONCAT XMLDIFF XMLELEMENT XMLEXISTS XMLFOREST XMLISVALID
        XMLPARSE XMLPATCH XMLPI XMLQUERY XMLROOT XMLSEQUENCE XMLSERIALIZE XMLTABLE
        XMLTRANSFORM
        )&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keywords_type&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# sources:&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@keywords_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%w(
        CHAR BYTE VARCHAR2 NCHAR NVARCHAR2
        NUMBER FLOAT BINARY_FLOAT BINARY_DOUBLE
        LONG RAW
        DATE TIMESTAMP INTERVAL LOCAL TIME ZONE TO MONTH SECOND YEAR DAY
        BLOB CLOB NCLOB BFILE
        UROWID
        CHARACTER VARYING VARCHAR NATIONAL CHARACTER
        NUMERIC DECIMAL DEC INTEGER INT SMALLINT
        FLOAT DOUBLE PRECISION REAL
        SDO_GEOMETRY SDO_TOPO_GEOMETRY SDO_GEORASTER
        REF ANYTYPE ANYDATA ANYDATASET XMLTYPE HTTPURITYPE XDBURITYPE DUBRITYPE
        BOOLEAN PLS_INTEGER BINARY_INTEGER SIMPLE_FLOAT SIMPLE_INTEGER SIMPLE_DOUBLE SYS_REFCURSOR
        )&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


      &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;delimiter_map&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'['&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;']'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'('&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;')'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&amp;lt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&amp;gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+/m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Text&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/--.*/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Single&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r(/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\*&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Multiline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:multiline_comments&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/q'(.)/i&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;#open = Regexp.escape(m[1])&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;close&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;escape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delimiter_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# the opening q'X&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/(?:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;[^']|[^&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;]'|[^&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;'])+/m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Other&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;'/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pop!&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/'/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:single_string&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# A double-quoted string refers to a database object in our default SQL&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/&quot;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:double_string&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;### we do not use backticks in Oracle. I do not know the rules for other sql engines&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;###rule %r/`/, Name::Variable, :backtick&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/[+-]?(?:(?:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.\d&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+(?:[eE][+-]?&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+)?)|&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;(?:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+(?:[eE][+-]?&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+)?)?)/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Float&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/[+-]?&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Integer&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/%(?:TYPE|ROWTYPE)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\b&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Attribute&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# longer ones come first on purpose!&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/=&amp;gt;|&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\|\|&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\*\*&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;|&amp;lt;&amp;lt;|&amp;gt;&amp;gt;|&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;|&amp;lt;&amp;gt;|[:!~^&amp;lt;&amp;gt;]=|[-+%&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\/&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;*=&amp;lt;&amp;gt;@&amp;amp;!^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\[\]&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;]/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/(NOT|AND|OR|LIKE|BETWEEN|IN)(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;)/im&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;groups&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Text&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/(IS)(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+)(?:(NOT)(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+))?(NULL&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\b&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;)/im&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;groups&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/[;:()&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\[\]&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;,.]/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Punctuation&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# this madness is to keep the word &quot;replace&quot; from being treated as a builtin function in this context&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/(?:(replace)(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+))?(package|function|procedure|type)(?:(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+)(body))?(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+)(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\w&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\w\d\$&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;]*)/im&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;groups&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Reserved&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Reserved&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Reserved&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;(?:IF|THEN|ELSE|ELSIF|ERROR|END|(?:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$\w&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+)))(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+)/im&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;groups&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Preproc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Text&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\w&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\w\d\$&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;]*/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keywords_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;upcase&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt; 
            &lt;span class=&quot;c1&quot;&gt;#Name::Builtin&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keywords_func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;upcase&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Function&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keywords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;upcase&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Reserved&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keywords_nresvd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;upcase&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Keyword&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:multiline_comments&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/([*][^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\/&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;]|[^*])+/m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Multiline&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r([*]&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\/&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Multiline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pop!&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


      &lt;span class=&quot;c1&quot;&gt;#state :backtick do&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#  rule %r/\\./, Str::Escape&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#  rule %r/``/, Str::Escape&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#  rule %r/`/, Name::Variable, :pop!&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#  rule %r/[^\\`]+/, Name::Variable&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#end&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:single_string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;./&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Escape&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/''/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Escape&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/'/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pop!&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;']+/m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Single&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:double_string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;./&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Escape&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/&quot;&quot;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Escape&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/&quot;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pop!&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r/[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;&quot;]+/m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Variable&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 20 Mar 2022 12:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/plsql/sql/2022/03/20/Ruby-Rouge-Lexer-PLSQL.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/plsql/sql/2022/03/20/Ruby-Rouge-Lexer-PLSQL.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>ruby</category>
        
        <category>rouge</category>
        
        <category>lexer</category>
        
        <category>syntax-highlighting</category>
        
        <category>github-pages</category>
        
        <category>github</category>
        
        <category>syntax</category>
        
        <category>jekyll</category>
        
        
        <category>plsql</category>
        
        <category>sql</category>
        
      </item>
    
      <item>
        <title>Staging Tables vs Single SQL</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;A batch technique I often encounter creates staging tables for intermediate results, then breaks a problem
down into multiple smaller steps, each one populating (and sometimes updating!) staging tables before
consolidating into a final result. The argument is that breaking down the problem into smaller,
more easily understood steps is a good programming practice,
which on its face is true. There is also an advantage in being able to 
inspect the intermediate results while doing development and QA (and perhaps in production support).&lt;/p&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;Small Steps Using Staging Tables&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/with2_staging_tables.gif&quot; alt=&quot;with2_staging_tables&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Assuming the developer’s code is optimized as
well as the Oracle optimizer would if given the entire problem, the only extra cost is the instantiation
of intermediate results. Generally, I have not found the assumption to be true, but let’s give practitioners
of this pattern the benefit of the doubt. Most of the time one can achieve reasonable performance
using this technique.&lt;/p&gt;

&lt;p&gt;Yet the overhead in &lt;em&gt;undo/redo&lt;/em&gt; logging for these staging tables is not free. The total run-time
will be longer using this technique, sometimes substantially. The DBA
is probably gathering statistics in a job that will process your staging tables needlessly. In addition,
we are bloating the undo and redo logs, plus these tables get backed up and take up room on “disk”.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you would like
a refresher course on &lt;em&gt;undo&lt;/em&gt; and &lt;em&gt;redo&lt;/em&gt;, my personal favorite is from Tom Kyte’s 
book &lt;em&gt;Expert Oracle Architecture&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For our purposes just know those staging tables are not free, even if you use direct path load.&lt;/p&gt;

&lt;p&gt;Furthermore, practitioners often employ UPDATEs and DELETEs which are much more expensive 
in Oracle than INSERTs. If you can gather the information you need while doing the INSERT, it is
difficult to justify doing so after the fact with an UPDATE even to simplify your understanding
of the solution. If you find yourself adding indexes to your staging table to facilitate subsequent
operations, you might want to rethink
your approach. (Of course, there are exceptions where the cost of creating an index on a staging table
and using it to perform updates/deletes is the best answer, but it is extremely rare.)&lt;/p&gt;

&lt;h1 id=&quot;using-with-subquery-factoring-alternative&quot;&gt;Using WITH Subquery Factoring Alternative&lt;/h1&gt;

&lt;h2 id=&quot;multiple-small-steps&quot;&gt;Multiple Small Steps&lt;/h2&gt;

&lt;p&gt;We can write the same task in a single SQL statement using the &lt;em&gt;WITH&lt;/em&gt; syntax to create
multiple sequential views, aka Common Table Expressions (CTE).
This retains the advantage
of breaking the problem into multiple smaller steps while not paying the undo/redo cost of the staging tables.
Instead, the execution plan allows the database engine to merrily pipeline results 
from join to join without ever writing out the intermediate
resultsets. (Well, technically it can buffer intermediate resultsets in the temp tablespace 
between operations, and some of that may write
to disk if there is not enough memory, but from a logical
standpoint nothing is written to the database).&lt;/p&gt;

&lt;p&gt;Not only that, but the optimizer can merge the views into the main query when it finds 
a better plan than the one you envisioned when you designed the program and broke it down into parts.
Letting Oracle do the entire set of work in a single statement is almost always the most
efficient and fastest solution.&lt;/p&gt;

&lt;p&gt;From the developer’s point of view, we have broken down the problem into
smaller, understandable chunks as CTE’s. It is the Oracle optimizer that is taking on the complexity, not the developer.
Granted, your explain plan is going to be bigger and take some effort to relate to your code, but
it should be in the wheelhouse for any serious Oracle practitioner to understand what
the Optimizer does with your query.&lt;/p&gt;

&lt;h2 id=&quot;intermediate-resultsets&quot;&gt;Intermediate Resultsets&lt;/h2&gt;

&lt;p&gt;As for being able to see intermediate resultsets, at least during development and performance testing you can
easily alter the query to stop after any given CTE, then select from that CTE. You can select the entire resultset
or use a where clause or even analytic.  For example:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step1&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step3&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You make a copy and interject your debug select like so:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step1&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;) select mycollist from step2 where xyz = ‘mykey’;&lt;br /&gt;
– remainder of quuery does not run&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step3&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It can also be helpful to do this if you want to look at the Explain Plan for the smaller subset of the
query.&lt;/p&gt;

&lt;p&gt;When you want to resume going through the query and looking at parts, you can put dash-dash in front of that line
and continue going down the query testing each part. That way you can come back to it for further analysis.&lt;/p&gt;

&lt;h2 id=&quot;cte-used-multiple-times&quot;&gt;CTE Used Multiple Times&lt;/h2&gt;

&lt;p&gt;If you use a CTE within the full query more than once, then Oracle must instantiate it. This will appear as
&lt;em&gt;Temp Table Transformation&lt;/em&gt; with a &lt;em&gt;Load As Select&lt;/em&gt; below it near the start of the Explain Plan. 
For all practical purposes this is the same as populating a global temporary table. It uses
the default temporary tablespace. As far as I can tell, this is effectively a direct path load of the 
transient global temporary table that does not generate much undo (and by default creating undo also
generates redo for that undo). This addresses another stated purpose for using
staging tables – to be able to use them more than once.&lt;/p&gt;

&lt;p&gt;In this scenario when you have a large resultset, it is still going to be almost as expensive to instantiate
it in Temporary space as it would be to do a direct path load into a staging table (except for the redo logging).
The only way around that is to figure out how to avoid reusing the CTE. You may
surprise yourself and find that you can.&lt;/p&gt;

&lt;h2 id=&quot;cardinality-and-statistics&quot;&gt;Cardinality and Statistics&lt;/h2&gt;

&lt;p&gt;Using WITH/CTE technique rather than staging tables, the optimizer has an advantage of knowing how each 
intermediate result was gathered. It can estimate
the cardinality. When using staging tables, you must either gather statistics on the staging table or provide
a cardinality hint to the using query. Gathering statistics can be even more expensive than the redo logging.&lt;/p&gt;

&lt;h1 id=&quot;discoveries-from-refactoring-staging-tables-to-a-single-sql&quot;&gt;Discoveries from Refactoring Staging Tables to a Single SQL&lt;/h1&gt;

&lt;h2 id=&quot;no_merge-hint&quot;&gt;NO_MERGE Hint&lt;/h2&gt;

&lt;p&gt;The optimizer will sometime merge subqueries together that you can prove via experimentation is a bad idea.
Purists will tell you that you just don’t have your statistics right. I don’t argue. I just hint the damn thing.&lt;/p&gt;

&lt;p&gt;For the example below,
Oracle will almost always merge the view named &lt;em&gt;keys&lt;/em&gt; into the main select and do a single HASH UNIQUE.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DISTINCT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_source&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DISTINCT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultfieldlist&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourcetable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I can prove that in case of the query I was tuning, we are better off gathering a DISTINCT set of keys 
before joining to &lt;em&gt;sourcetable&lt;/em&gt;.
The optimizer flat out refuses and merges the “set of keys” view into the join query so that it only has to do a single
HASH UNIQUE after the join. Yet the duplication of the keys between those two sources is multiplicative. If we let Oracle have
its way, the result set going into the single HASH UNIQUE the optimizer is so proud of is ginormous.
I know from trial and error for this
particular set of inputs, we are better off with two separate HASH UNIQUE operations.&lt;/p&gt;

&lt;p&gt;In this scenario you can force Oracle to do two separate HASH UNIQUE operations by using the NO_MERGE hint:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*+ NO_MERGE */&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DISTINCT&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_source&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DISTINCT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultfieldlist&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourcetable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve also seen cases where Oracle chooses to merge in a subquery containing an analytic, doing the join first
and then the analytic and the filter using the analytic column that was supposed to be applied before doing the join.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OVER&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;PARTITION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xyz&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_modified_dt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_source&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultfieldlist&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourcetable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The optimizer has determined that it can reduce the resultset better by doing the join first, shrinking the number
of rows from key_source, then doing the analytic sort and filter. 
Maybe the optimizer is right. 
When it is wrong, you will notice a humongous hash join taking forever in order to save a little
bit on that sort. That is when you can try making it do the sort first using NO_MERGE:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*+ NO_MERGE */&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OVER&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;PARTITION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xyz&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_modified_dt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_source&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resultfieldlist&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourcetable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key3&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I do not advise reaching for the NO_MERGE hint until you suspect a problem, and even then you should try
it both ways. Need for the hint in these scenarios is the exception, as most of the time the optimizer is right.
Yet the optimizer relies on rules of thumb and guesses that do not necessarily match your data. Even with
good statistics with histograms available, much of the optimizer logic is based on probabilities and rules of thumb.
It is not always right. Figuring out how to give the optimizer enough information to come up with the right answer,
as one school of purists advocate, is not practical.&lt;/p&gt;

&lt;h2 id=&quot;fat-resultset-and-big-joins&quot;&gt;Fat Resultset and Big Joins&lt;/h2&gt;

&lt;p&gt;When we get into very large joins when one or both of the input datasets are very fat (lots of columns/big fat records),
there is a technique from the staging table school of thought you might want to employ.
The technique is to grab only the join keys from one of your sources (likely your target record that you are
joining additional fields into) and use those keys to gather the data from the other table, then once
you have the values you need, join them back into the main query.&lt;/p&gt;

&lt;p&gt;Why do the join twice? Consider that the table you are reading is very large with many more rows than the one
you are joining into. The proper way to do this is hash the one with less rows and probe it from the new
table; yet, because it is so fat, hashing the entire thing overflows our available PGA memory pushing us off
into a onepass or multipass hash join. In this scenario we could be better off just hashing the distinct set
of join keys while we probe it from the large source table until we have only the data we need.&lt;/p&gt;

&lt;p&gt;Eventually we still have to join to our big fat target resultset, but now we have a smaller footprint of
data to do it with. We can reverse which resultset is hashed to the one that fits in memory.&lt;/p&gt;

&lt;p&gt;I still am likely to use WITH subqueries to do it but have at times found a separate staging table
is a faster answer. I have not completely grasped the reason, but suspect it revolves around the PGA
size and the amount of memory paged to disk for our reuse
of our main resultset.&lt;/p&gt;

&lt;h2 id=&quot;sometimes-an-update-really-is-faster&quot;&gt;Sometimes an Update Really is Faster&lt;/h2&gt;

&lt;p&gt;We have a very large and fat resultest,
and need to update values in a very small number of rows.
We need to make a full table scan pass through our data to select candidate rows (or the keys
from candidate rows) and join them to our source. We cannot avoid that first full scan.
If we have a staging table, we can bring the &lt;em&gt;ROWID&lt;/em&gt; along, 
then use that for the MERGE ON clause join back into the staging table.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;MERGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;staging_table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lkup_value&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowid&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s_rowid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OVER&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;PARTITION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effective_date&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;staging_table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lookup_table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
                &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_dt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effective_date&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s_rowid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lkup_value&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s_rowid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;MATCHED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SET&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Consider how we would do this in a series of steps using WITH clause CTEs in a single INSERT without the staging
table:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;staging_table&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lkup_value_1&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OVER&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;PARTITION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effective_date&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lookup_table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
                &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_dt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effective_date&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lkup_value&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lkup_value_1&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_value&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OUTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lkup_value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even though we are only changing a few rows with clause ‘y’, we must run all of the rows from ‘x’ through
this join with lkup_value clause. I have observed situations where keeping a staging table and
doing the MERGE as shown above is superior to doing everything in a single query. This surprised me. I started
the task with the expectation that I could eliminate all of the staging tables.
It was not the answer I wanted, but it was the answer I found.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;To break a database problem into small, understandable chunks you can use the WITH clause Common Table Expressions (CTE)
instead of staging tables. There are more benefits than downside to this technique, but there are some
exceptional circumstances where using intermediate staging tables is the best approach. Understand how the optimizer
processes what you have designed as sequential steps (most of which are pipelined), and use hints when absolutely 
necessary to guide the
optimizer away from suboptimal plans. A detailed understanding of the memory involved with HASH joins can
help you design non-obvious solutions for very large dataset operations.&lt;/p&gt;

</description>
        <pubDate>Sun, 06 Mar 2022 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/2022/03/06/Staging_Tables_vs_With.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/2022/03/06/Staging_Tables_vs_With.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>tuning</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
      </item>
    
      <item>
        <title>Inline External Tables for CSV Load</title>
        <description>&lt;h1 id=&quot;yet-another-csv-load-option---inline-external-tables&quot;&gt;Yet Another CSV Load Option - &lt;em&gt;Inline External Tables&lt;/em&gt;&lt;/h1&gt;

&lt;p&gt;While reviewing the Oracle What’s New documentation for 18c I found &lt;em&gt;Private Temporary Tables&lt;/em&gt; which
I wrote about in &lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/23/CSV-Clob-PTT.html&quot;&gt;my last post&lt;/a&gt;
on the neverending saga of loading CSV data. Also found in What’s New for 18c
is &lt;em&gt;Inline External Tables&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Just like with PTTs, &lt;em&gt;Inline External Tables&lt;/em&gt; are DDL without the downside. It is ad-hoc, and can be dynamically
generated. In order to use it you must have an Oracle DIRECTORY object granted to you with both READ and WRITE,
plus EXECUTE privilege on &lt;em&gt;DBMS_LOB&lt;/em&gt; (and &lt;em&gt;UTL_FILE&lt;/em&gt; if you want to clean up after yourself).&lt;/p&gt;

&lt;h1 id=&quot;example&quot;&gt;Example&lt;/h1&gt;

&lt;p&gt;From our last post we had an example of CSV data with a header row that looked as such:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Employee ID, Last Name, First Name, nickname
999, &quot;Baggins&quot;, &quot;Bilbo&quot;, &quot;badboy, ringbearer&quot;
998, &quot;Baggins&quot;, &quot;Frodo&quot;,
997, &quot;Orc&quot;, &quot;Ogg&quot;, &quot;i kill you&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We could get fancy and try to parse the header row like we did last time, but for this effort I’m going the cheap route
and assume you, the developer who wants to load the CSV data, will hand craft the code.&lt;/p&gt;

&lt;h1 id=&quot;create-file-on-oracle-server&quot;&gt;Create File on Oracle Server&lt;/h1&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clob2file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;999, &quot;Baggins&quot;, &quot;Bilbo&quot;, &quot;badboy, ringbearer&quot;
998, &quot;Baggins&quot;, &quot;Frodo&quot;,
997, &quot;Orc&quot;, &quot;Ogg&quot;, &quot;i kill you&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TMP_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;temp_csv_load.csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h1 id=&quot;read-from-the-inline-external-table&quot;&gt;Read from the Inline External Table&lt;/h1&gt;

&lt;p&gt;Now we read from the file:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXTERNAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ORACLE_LOADER&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DIRECTORY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TMP_DIR&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ACCESS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PARAMETERS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;RECORDS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DELIMITED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NEWLINE&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FIELDS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TERMINATED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;OPTIONALLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ENCLOSED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;MISSING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FIELD&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ARE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;LOCATION&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;temp_csv_load.csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;REJECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;UNLIMITED&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp_csv_load_ext&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The export from SQL Developer is adding the double quotes here. These are not in the data.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;Employee ID&quot;                 &quot;Last Name&quot;                   &quot;First Name&quot;                  &quot;nickname&quot;                    
&quot;999&quot;                         &quot;Baggins&quot;                     &quot;Bilbo&quot;                       &quot;badboy, ringbearer&quot;          
&quot;998&quot;                         &quot;Baggins&quot;                     &quot;Frodo&quot;                       &quot;&quot;                            
&quot;997&quot;                         &quot;Orc&quot;                         &quot;Ogg&quot;                         &quot;i kill you&quot;                  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You could of course have the external table sqlldr driver convert to dates and numbers as needed. As far as I can tell
you have everything at your disposal that is there for a normal external table.&lt;/p&gt;

&lt;p&gt;The line&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;OPTIONALLY ENCLOSED BY '&quot;'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;makes sqlldr parse CSV data in a way that I believe mostly comports with the RFC on CSV data. It will handle
most of the test cases I threw at it and you are unlikely to give it the oddball stuff.&lt;/p&gt;

&lt;p&gt;The line&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MISSING FIELD VALUES ARE NULL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;is because we do not have a trailing comma after the last field (which would be delimited data rather than separated)
and because the sqlldr syntax of ‘TRAILING NULLCOLS’ I would pick is not available in the external table driver. Maddening.
Without it our second record would fail to load.&lt;/p&gt;

&lt;h1 id=&quot;cleanup&quot;&gt;Cleanup&lt;/h1&gt;

&lt;p&gt;Being a good citizen, we remove our trash:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UTL_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fremove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TMP_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;temp_csv_load.csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h1 id=&quot;drawback&quot;&gt;Drawback&lt;/h1&gt;

&lt;p&gt;The big drawback to this technique (aside from how hard it is to get the external table definition right) is
that it requires you have READ/WRITE privs on a directory on the database server. In many organizations this is forbidden.
I retrofitted forty something load jobs
from external table to sqlldr because of that restriction imposed upon us by architecture/DBA teams
a few years back.
We can debate whether that is reasonable or not, but it is what it is.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;There are plenty of client tools that will generate load data for you from CSV files or even directly from Excel.
As nice as those are, you probably cannot use them for Continuous Improvement deployments. I’m trying to come up
with a way to make that better. This is one more technique we might be able to use.&lt;/p&gt;
</description>
        <pubDate>Mon, 24 Jan 2022 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/24/CSV-Clob-Inline-External.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/24/CSV-Clob-Inline-External.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>csv</category>
        
        <category>perl</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>perl</category>
        
      </item>
    
      <item>
        <title>CSV Clob and Private Temporary Table</title>
        <description>&lt;h1 id=&quot;deploying-table-data-using-csv&quot;&gt;Deploying Table Data Using CSV&lt;/h1&gt;

&lt;p&gt;I have been obsessing about parsing CSV data with a focus on the use case of 
code promotion through Continuous Improvement/Devops process. The current method of
deploying a bunch of single INSERT statements rubs me the wrong way. It is ridiculous.&lt;/p&gt;

&lt;p&gt;I get the feeling I’m the only one who cares, but hey, I’m the one who matters!&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/09/More-CSV-Fun.html&quot;&gt;my last post on the topic&lt;/a&gt;
I described how one could use some new tools I created 
in &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#perlish_util_udt&quot;&gt;perlish_util_udt&lt;/a&gt; 
to parse a CSV clob into records and fields, then use that parsed data in a SQL statement,
perhaps including DML.&lt;/p&gt;

&lt;p&gt;That blog post left it still a bit rough though. The user would need to remember or relearn the
syntax for two related functions to do the parsing, and the syntax for using
an object method in a SQL query is easy to forget about or mess up.&lt;/p&gt;

&lt;p&gt;I had also been experimenting with Polymorphic Table Functions for this purpose because you
can determine the resultset structure at run time, which is important for this task. Yet PTFs
are complicated. Maybe too complicated.&lt;/p&gt;

&lt;p&gt;I was rereading the “What’s New” Oracle documentation for the last several releases and
Private Temporary Tables (added in 18c) caught my eye. Much of the issue we face with this use case
is that it needs to be dynamic and DDL is not dynamic. It is resource expensive and
regulated in most production environments. Private Temporary Tables are DDL without the downside.
It does not alter the data dictionary (well, maybe there is something going on as it
take advantage of the TEMPORARY tablespace), it does not leave anything behind after a session
completes, and it is not regulated by our corporate rules.&lt;/p&gt;

&lt;p&gt;Private Temporary Tables let us define our column list on the fly!&lt;/p&gt;

&lt;h1 id=&quot;perlish_util_udtcreate_ptt_csv&quot;&gt;perlish_util_udt.create_ptt_csv&lt;/h1&gt;

&lt;p&gt;This static procedure recently added to 
&lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#perlish_util_udt&quot;&gt;perlish_util_udt&lt;/a&gt; 
combines the operations demonstrated in my prior blog post about CSV data to build
and populate a Private Temporary Table to contain your data from the CSV clob. How cool is that?&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_ptt_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employee ID, Last Name, First Name, nickname
999, &quot;Baggins&quot;, &quot;Bilbo&quot;, &quot;badboy, ringbearer&quot;
998, &quot;Baggins&quot;, &quot;Frodo&quot;,
997, &quot;Orc&quot;, &quot;Ogg&quot;, &quot;i kill you&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ora$ptt_csv&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;Employee ID&quot;                 &quot;Last Name&quot;                   &quot;First Name&quot;                  &quot;nickname&quot;                    
&quot;999&quot;                         &quot;Baggins&quot;                     &quot;Bilbo&quot;                       &quot;badboy, ringbearer&quot;          
&quot;998&quot;                         &quot;Baggins&quot;                     &quot;Frodo&quot;                       &quot;&quot;                            
&quot;997&quot;                         &quot;Orc&quot;                         &quot;Ogg&quot;                         &quot;i kill you&quot;                  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The value of “nickname” in the Fodo record is NULL.&lt;/p&gt;

&lt;p&gt;That is a lot easier than doing the parsing into lines and the parsing into fields and selecting by index number
we were using before. Under the covers that is what it does, but now we have something that is easy to use.&lt;/p&gt;

&lt;h2 id=&quot;the-code&quot;&gt;The Code&lt;/h2&gt;

&lt;p&gt;Given the prior work with &lt;em&gt;perlish_util_udt.split_clobs_to_lines&lt;/em&gt; and &lt;em&gt;perlish_util_udt.split_csv&lt;/em&gt;, this was not
difficult to implement.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;k&quot;&gt;STATIC&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_ptt_csv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;-- creates private temporary table ora$ptt_csv with columns named in first row of data case preserved.&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;-- All fields are varchar2(4000)&lt;/span&gt;
	     &lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
	    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
	    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- also unquotes \&quot; and &quot;&quot; pairs within the field to just &quot;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first step, which is in the variable declarations, splits the clob up into an array of rows. We are
going to parse the first row into &lt;em&gt;v_cols&lt;/em&gt; which we make into an object type so we can take advantage
of our &lt;em&gt;map&lt;/em&gt; and &lt;em&gt;join&lt;/em&gt; methods to build the SQL we need to execute.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- remove the header row so can bind the array to read the data&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DELETE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We parse the first row differently than the rest. We are going to enclose the data in double quotes so we must strip
them if they exist. After we get our data from the first row, we delete it from the collection. We will later
bind the collection array to a SQL statement and do not want the first row to be present.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;DROP TABLE ora$ptt_csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OTHERS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Drop the PTT if it already exists.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CREATE PRIVATE TEMPORARY TABLE ora$ptt_csv(
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;$_&quot;    VARCHAR2(4000)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We use our collection of column names from the first row to create the column defintions for the PTT.
&lt;em&gt;map&lt;/em&gt; and &lt;em&gt;join&lt;/em&gt; are handy for this. As you can see we do not try to figure out what kind of data
each column is. We just stuff each column value into a VARCHAR2(4000) field.&lt;/p&gt;

&lt;p&gt;The serveroutput of this code from running the example above was:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIVATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TEMPORARY&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ora$ptt_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now for the INSERT.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;INSERT INTO ora$ptt_csv 
WITH a AS (
    SELECT perlish_util_udt(
            perlish_util_udt.split_csv(t.column_value, p_separator =&amp;gt; :p_separator, p_strip_dquote =&amp;gt; :p_strip_dquote, p_keep_nulls =&amp;gt; 'Y')
        ) AS p
    FROM TABLE(:bind_array) t
) SELECT &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;x.p.get($##index_val##) AS &quot;$_&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
FROM a x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- must use table alias and fully qualify object name with it to be able to call function or get attribute of object&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- Thus alias x for a.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- crate_ptt_csv&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Building the INSERT statement was a little tricky because we need to use the object function &lt;em&gt;get&lt;/em&gt; with the array
index value for each column. Once again &lt;em&gt;map&lt;/em&gt; and &lt;em&gt;join&lt;/em&gt; are handing for building this. I added the ‘$##index_val##’
substitution to &lt;em&gt;map&lt;/em&gt; for this release.&lt;/p&gt;

&lt;p&gt;The serveroutput of this section from the example:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ora$ptt_csv&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bind_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And of course you have seen the results. I’m starting to like this. Sure, I’ve been seduced by the dark side
and am writing PL/SQL code that looks like Perl, but I am what I am.&lt;/p&gt;
</description>
        <pubDate>Sun, 23 Jan 2022 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/23/CSV-Clob-PTT.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/23/CSV-Clob-PTT.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>csv</category>
        
        <category>perl</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>perl</category>
        
      </item>
    
      <item>
        <title>Protecting/Hiding Data in Oracle</title>
        <description>&lt;h2 id=&quot;the-ask&quot;&gt;The Ask&lt;/h2&gt;

&lt;p&gt;While experimenting with &lt;a href=&quot;https://github.com/antonscheffer/as_sftp&quot;&gt;as_sftp&lt;/a&gt;, a package by Anton Scheffer
that implements a Secure File Transfer Protocol (SFTP) utility in Oracle PL/SQL, I encountered an issue (or want)
of storing SSH private keys in the database. That opens a can of worms because private keys should be secured as
much as possible.&lt;/p&gt;

&lt;p&gt;On a Unix system the private keys are kept in files in ~/.ssh directory with permissions set to be readable only by
the user login associated with that HOME directory. Of course anyone with root or sudo privs can get the file, but
that is the level of security we have come to expect for SSH client implementations. It is also one of the reasons
some corporations have issues with ssh and go to lengths to prevent using it except where absolutely necessary. I digress.&lt;/p&gt;

&lt;p&gt;I need to store private keys in the database with the following requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Not visible to any user except the schema owner (and DBA, but only because we can’t keep them out).&lt;/li&gt;
  &lt;li&gt;Accessible to a particular procedure that calls the &lt;em&gt;login&lt;/em&gt; method of &lt;em&gt;as_sftp&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Not accessible to any other procedures or queries.&lt;/li&gt;
  &lt;li&gt;Not visible to the schema owner if possible. The reason for this odd seeming requirement is that in most corporate systems a schema owner for deployed code is a shared account. Even if it is protected from login, there are multiple developers who deploy code into the schema. We want to control access to the single deployer and the procedure that needs the private key.&lt;/li&gt;
&lt;/ul&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;Hiding Data Use Cases&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/hiding_data_use_case.gif&quot; alt=&quot;Use_Case&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;I can see altering the use case to have separate privileges for the DML operations versus the SFTP Login,
but I’m going to combine them for now.&lt;/p&gt;

&lt;p&gt;As shown in the diagram, the authorized user does not have a use case for obtaining the private key directly
or even to tell that there is a key available. That is by design.&lt;/p&gt;

&lt;h2 id=&quot;options&quot;&gt;Options&lt;/h2&gt;

&lt;p&gt;I considered a package with the source obfuscated via the utility &lt;em&gt;wrap&lt;/em&gt;. From the documentation though&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Although wrapping a compilation unit helps to hide the algorithm and makes reverse-engineering difficult, Oracle Corporation does not recommend it as a secure method for hiding passwords or table names. Obfuscating a PL/SQL unit prevents most users from examining the source code, but might not stop all attempts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It might be good enough for our use case, but there are other issues. This package or function will be executable
by the schema owner, who in our target environment is a shared account. We also do not control all database security
and a surprising number of other accounts may have elevated privliges 
including EXECUTE ANY PROCEDURE (yea, don’t. OK. Not my circus. Not my monkeys.).&lt;/p&gt;

&lt;p&gt;As for storing the key in a table, even more frequently found in the wild is the grant of SELECT ANY TABLE.&lt;/p&gt;

&lt;p&gt;I considered putting some sort of encryption on the text of the key in the table, but that just kicks the can around
because now the key for that encryption needs to be stored somewhere. Not helpful.&lt;/p&gt;

&lt;p&gt;I briefly flirted with using a Context, but it has to be loaded somehow and we are back to protecting that operation.&lt;/p&gt;

&lt;p&gt;I settled on Oracle Fine-Grained Access Control (See Oracle Database Security Guide and &lt;em&gt;DBMS_RLS&lt;/em&gt; for your release).&lt;/p&gt;

&lt;p&gt;The solution won’t stop that person with EXECUTE ANY PROCEDURE from
running our login procedure which uses our private key (shame on the DBA!), but it prevents them from grabbing 
the private key and using it elsewhere. Even this issue might be solved if we add a check for calling &lt;em&gt;user&lt;/em&gt;
in the policy, but I have not gone there yet.&lt;/p&gt;

&lt;p&gt;There is an extensive amount of debug logging available in &lt;em&gt;as_sftp&lt;/em&gt;. I have not taken the time to explore
it all to see if the private keys can be exposed.&lt;/p&gt;

&lt;p&gt;I am not a security expert. It is possible there are more glaring holes in what I have built. Everything I’ve checked
suggests this is good against any but a SYSDBA privileged user account, yet I’m not going to claim
this is a secure solution. I just do not know. This is an exercise in implementing Fine Grained
Access Control.&lt;/p&gt;

&lt;h2 id=&quot;fine-grained-access-control-configuration&quot;&gt;Fine Grained Access Control Configuration&lt;/h2&gt;

&lt;p&gt;The documentation and examples center around use of a context that you populate with a login trigger
to restrict access to particular logins or scenarios as to how the session was entered. Much of that does
not fit my use case.&lt;/p&gt;

&lt;p&gt;What does fit is that we can install functions that control whether or not a SELECT, INSERT, UPDATE or DELETE
operation will or will not work based on logic we implement. In this context “not work” means a SELECT will return 0
rows but no error, while a DML operation will return either 0 rows updated/deleted, or in the case of insert, a policy error
exception. No user or procedure can manipulate the data in this table without passing our policy check.&lt;/p&gt;

&lt;p&gt;The exceptions to this are that the schema owner can perform DDL on the table including TRUNCATE and DROP, and 
anyone with SYSDBA priv can do whatever they want. These conditions are acceptable to me. To take it any further
we would need to create a separate schema for the table and packages. That might be best, but in most corporate environments, it
is expensive to do. That said, if I was installing &lt;em&gt;as_sftp&lt;/em&gt; as a DBA for general use that is where I would go with it.&lt;/p&gt;

&lt;p&gt;My intent is for the policy check functions to look at the call stack and only allow the operation to succeed
if we are called by a specific schema owned package function/procedure. Mostly that means my supplemental &lt;em&gt;login&lt;/em&gt; procedure
that retrieves the private key and calls &lt;em&gt;as_sftp.login&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;

&lt;p&gt;This implementation is for a single schema owner. If it was to be shared across multiple schemas, there
would likely need to be an additional primary key field in the table for &lt;strong&gt;who&lt;/strong&gt; is running the operation. 
That would also need to consider how Mr. Scheffer configured the &lt;em&gt;as_sfpt_known_hosts&lt;/em&gt; operation. It may be
possible to define this with INVOKER rights and force each schema owner to implement their own tables,
but that seems draconian. Tracking everything by the &lt;em&gt;user&lt;/em&gt; identification of the caller would be my choice.&lt;/p&gt;

&lt;h3 id=&quot;as_sftp_private_keys-table&quot;&gt;as_sftp_private_keys Table&lt;/h3&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_private_keys&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;host&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;id&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_private_keys_pk&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;as_sftp_keymgmt-package-specfication&quot;&gt;as_sftp_keymgmt Package Specfication&lt;/h3&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- Important! The private key lookup is case sensitive on i_host and i_user.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_passphrase&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_log_level&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;pls_integer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- comment out this function when done testing. It should not be public&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--FUNCTION get_priv_key(i_host VARCHAR2, i_user VARCHAR2) RETURN CLOB;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- When keymgmt_security is activated (fine grained access control)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- These three methods are the only way to manipuate the data in the table as_sftp_private_keys&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- other than to truncate it or do the task as sysdba.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- You cannot read the data at all as get_priv_key is a private function that only login() can call.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_key&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_key&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delete_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While testing I made the function &lt;em&gt;get_priv_key&lt;/em&gt; public so that I could validate what it was doing.
It should be private.&lt;/p&gt;

&lt;p&gt;The procuedure &lt;em&gt;login&lt;/em&gt; will lookup the private key (using &lt;em&gt;get_priv_key&lt;/em&gt;) and call &lt;em&gt;as_sftp.login&lt;/em&gt; using it.&lt;/p&gt;

&lt;p&gt;The three DML procedures allow operations on individual private key records for anyone granted execute
on the package. As noted, it might be better to put those in a separate package.&lt;/p&gt;

&lt;p&gt;So far what we have implemented will work without installing the fine grained access control components. Although
anyone with SELECT priv on the table can see our keys, the storage of keys and use of them automatically
during login will work much like SSH/SFTP does on Unix by looking up the key.&lt;/p&gt;

&lt;h3 id=&quot;as_sftp_keymgmt_security-package-specification&quot;&gt;as_sftp_keymgmt_security PACKAGE Specification&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;as_sftp_keymgmt_security&lt;/em&gt; package implements the policy checks that will, when installed via &lt;em&gt;DBMS_RLS&lt;/em&gt;, control
whether access to table &lt;em&gt;as_sftp_private_keys&lt;/em&gt; is permitted.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt_security&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- These control access to records in the table as_sftp_private_keys&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_select_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objname&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_insert_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objname&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_update_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objname&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_delete_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objname&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt_security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We will explore the package body after seeing how it is tied to the table.&lt;/p&gt;

&lt;h3 id=&quot;install_keymgmt_securitysql&quot;&gt;install_keymgmt_security.sql&lt;/h3&gt;

&lt;p&gt;This install script associates the functions in package &lt;em&gt;as_sftp_keymgmt_security&lt;/em&gt; with each of the operations ‘SELECT’,
‘INSERT’, ‘UPDATE’, ‘DELETE’ on table &lt;em&gt;as_sftp_private_keys&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The package &lt;em&gt;as_sftp_keymgmt_security&lt;/em&gt; must be 
both publicly executable and have a public synonym for this to work. If you do not have
the privilege to create a public synonym, the DBA must do it for you.&lt;/p&gt;

&lt;p&gt;You may also need the DBA to ‘GRANT EXECUTE ON DBMS_RLS TO &lt;strong&gt;your_schema&lt;/strong&gt;;’, though it is common enough to grant
it to ‘PUBLIC’.&lt;/p&gt;

&lt;p&gt;Of note is that the &lt;em&gt;add_policy&lt;/em&gt; for ‘INSERT’ uses an additional parameter because ‘INSERT’ statements do 
not have ‘WHERE’ clauses. The implementation checks your condition after the insert, then raises an EXCEPTION
if the operation is not allowed. This is different than the others where it quietly adds a WHERE condition
to cause your operation to not match any rows.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt_security&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SYNONYM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt_security&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_RLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drop_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_PRIVATE_KEYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;USER_DATA_SELECT_POLICY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OTHERS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_RLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_PRIVATE_KEYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;USER_DATA_SELECT_POLICY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_KEYMGMT_SECURITY.USER_DATA_SELECT_SECURITY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_RLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drop_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_PRIVATE_KEYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;USER_DATA_INSERT_POLICY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OTHERS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_RLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_PRIVATE_KEYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;USER_DATA_INSERT_POLICY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_KEYMGMT_SECURITY.USER_DATA_INSERT_SECURITY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- needed because insert does not have where clause. check condition after insert&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_RLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drop_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_PRIVATE_KEYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;USER_DATA_UPDATE_POLICY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OTHERS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_RLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_PRIVATE_KEYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;USER_DATA_UPDATE_POLICY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_KEYMGMT_SECURITY.USER_DATA_UPDATE_SECURITY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UPDATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_RLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drop_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_PRIVATE_KEYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;USER_DATA_DELETE_POLICY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OTHERS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_RLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_PRIVATE_KEYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;USER_DATA_DELETE_POLICY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_KEYMGMT_SECURITY.USER_DATA_DELETE_SECURITY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;DELETE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;as_sftp_keymgmt_security-package-body&quot;&gt;as_sftp_keymgmt_security Package Body&lt;/h3&gt;

&lt;p&gt;Now we get to the fun stuff. How are we determining that ONLY a specific package and method are allowed access?
We look at the call stack. It is tricky though because the &lt;em&gt;DBMS_RLS&lt;/em&gt; operations insert themselves into the
call stack! You must look back a few levels to find the original caller.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BODY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt_security&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_select_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objname&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_lineno&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_caller_t&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dynamic_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;security check found not called by LEE.AS_SFTP_KEYMGMT.GET_PRIV_KEY.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dbms_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;call stack less than 4: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not allowing rows to be selected.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1=0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/*
        FOR i in 1..v_depth
        LOOP
            v_owner := UTL_CALL_STACK.owner(i);
            v_name := UTL_CALL_STACK.concatenate_subprogram(UTL_CALL_STACK.subprogram(i));
            dbms_output.put_line('i='||i||' owner: '||v_owner||' name: '||v_name);
        END LOOP;
        */&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;concatenate_subprogram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subprogram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('owner: '||v_owner||' name: '||v_name);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--owa_util.who_called_me(v_owner, v_name, v_lineno, v_caller_t);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('owner: '||v_owner||' name: '||v_name||' lineno: '||v_lineno||' caller_t: '||v_caller_t);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line(DBMS_UTILITY.format_call_stack);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_KEYMGMT.GET_PRIV_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;security check found not called by LEE.AS_SFTP_KEYMGMT.GET_PRIV_KEY.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;was called by owner: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; name: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not allowing rows to be selected.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1=0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--RETURN NULL;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_select_security&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_insert_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objname&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_lineno&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_caller_t&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dynamic_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;security check found not called by LEE.AS_SFTP_KEYMGMT.INSERT_PRIV_KEY.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dbms_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;call stack less than 4: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not allowing rows to be selected.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1=0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/*
        FOR i in 1..v_depth
        LOOP
            v_owner := UTL_CALL_STACK.owner(i);
            v_name := UTL_CALL_STACK.concatenate_subprogram(UTL_CALL_STACK.subprogram(i));
            dbms_output.put_line('i='||i||' owner: '||v_owner||' name: '||v_name);
        END LOOP;
        */&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;concatenate_subprogram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subprogram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('owner: '||v_owner||' name: '||v_name);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--owa_util.who_called_me(v_owner, v_name, v_lineno, v_caller_t);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('owner: '||v_owner||' name: '||v_name||' lineno: '||v_lineno||' caller_t: '||v_caller_t);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line(DBMS_UTILITY.format_call_stack);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_KEYMGMT.INSERT_PRIV_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;security check found not called by LEE.AS_SFTP_KEYMGMT.INSERT_PRIV_KEY.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;was called by owner: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; name: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not allowing rows to be inserted.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1=0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--RETURN NULL;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_insert_security&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_update_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objname&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_lineno&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_caller_t&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dynamic_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;security check found not called by LEE.AS_SFTP_KEYMGMT.UPDATE_PRIV_KEY.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dbms_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;call stack less than 4: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not allowing rows to be selected.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1=0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/*
        FOR i in 1..v_depth
        LOOP
            v_owner := UTL_CALL_STACK.owner(i);
            v_name := UTL_CALL_STACK.concatenate_subprogram(UTL_CALL_STACK.subprogram(i));
            dbms_output.put_line('i='||i||' owner: '||v_owner||' name: '||v_name);
        END LOOP;
        */&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;concatenate_subprogram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subprogram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('owner: '||v_owner||' name: '||v_name);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--owa_util.who_called_me(v_owner, v_name, v_lineno, v_caller_t);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('owner: '||v_owner||' name: '||v_name||' lineno: '||v_lineno||' caller_t: '||v_caller_t);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line(DBMS_UTILITY.format_call_stack);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_KEYMGMT.UPDATE_PRIV_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;security check found not called by LEE.AS_SFTP_KEYMGMT.UPDATE_PRIV_KEY.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;was called by owner: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; name: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not allowing rows to be updated.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1=0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--RETURN NULL;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_update_security&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_delete_security&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objname&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_lineno&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_caller_t&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dynamic_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;security check found not called by LEE.AS_SFTP_KEYMGMT.DELETE_PRIV_KEY.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dbms_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;call stack less than 4: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not allowing rows to be selected.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1=0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/*
        FOR i in 1..v_depth
        LOOP
            v_owner := UTL_CALL_STACK.owner(i);
            v_name := UTL_CALL_STACK.concatenate_subprogram(UTL_CALL_STACK.subprogram(i));
            dbms_output.put_line('i='||i||' owner: '||v_owner||' name: '||v_name);
        END LOOP;
        */&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;concatenate_subprogram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTL_CALL_STACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subprogram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('owner: '||v_owner||' name: '||v_name);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--owa_util.who_called_me(v_owner, v_name, v_lineno, v_caller_t);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('owner: '||v_owner||' name: '||v_name||' lineno: '||v_lineno||' caller_t: '||v_caller_t);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line(DBMS_UTILITY.format_call_stack);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LEE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AS_SFTP_KEYMGMT.DELETE_PRIV_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;security check found not called by LEE.AS_SFTP_KEYMGMT.DELETE_PRIV_KEY.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;was called by owner: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; name: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not allowing rows to be deleted.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1=0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--RETURN NULL;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data_delete_security&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt_security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Much of the code is repeated so I could have refactored it. Maybe later.&lt;/p&gt;

&lt;h3 id=&quot;as_sftp_keymgmt-package-body&quot;&gt;as_sftp_keymgmt PACKAGE BODY&lt;/h3&gt;

&lt;p&gt;Implementation of the methods to do the login and DML is anticlimatic.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BODY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- This is the only method to select the private key when fine grained access control is added to the table&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- with as_sftp_keymgt_security package. It is a package private function only called by login()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_clob&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_clob&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_private_keys&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO_DATA_FOUND&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;raise_application_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20713&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;no record found in table as_sftp_private_keys for host=&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, id=&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- The method for obtaining the private key and using it to call as_sftp.login&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_passphrase&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_log_level&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;pls_integer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_priv_key&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;as_sftp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_log_level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_priv_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_passphrase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_passphrase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- 3 DML methods for manipulating the key table records&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_key&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_private_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;VALUES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_key&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_private_keys&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_key&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delete_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_private_keys&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_host&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i_user&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delete_priv_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_sftp_keymgmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The code shown here is available on my github page at &lt;a href=&quot;https://github.com/lee-lindley/as_sftp_keymgmt&quot;&gt;as_sftp_keymgmt&lt;/a&gt;.
I also forked &lt;a href=&quot;https://github.com/lee-lindley/as_sftp&quot;&gt;as_ftp&lt;/a&gt; and put in a 
&lt;a href=&quot;https://github.com/antonscheffer/as_sftp/pull/17&quot;&gt;pull request&lt;/a&gt; to Mr. Scheffer. That version is more extensive
and completely integrated with &lt;em&gt;as_sftp&lt;/em&gt;. I cannot imagine supporting it going forward though if Anton does not
incorporate it. You are welcome to the code though.&lt;/p&gt;

&lt;p&gt;I am not sure I enjoyed this exercise, but I learned enough about Oracle Fine Grained Access Control to accomplish
my goal. Hope it was helpful.&lt;/p&gt;
</description>
        <pubDate>Sat, 22 Jan 2022 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2022/01/22/Hiding-Data-Oracle.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2022/01/22/Hiding-Data-Oracle.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>ssh</category>
        
        <category>sftp</category>
        
        <category>dbms_rls</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>HTML Table Markup Redux</title>
        <description>&lt;h1 id=&quot;css-hell&quot;&gt;CSS Hell&lt;/h1&gt;

&lt;p&gt;Previously I wrote about a new package I published on github at &lt;a href=&quot;https://github.com/lee-lindley/app_html_table_pkg&quot;&gt;app_html_table_pkg&lt;/a&gt;. My celebration was premature. The universe reared up and bit me in the hind quarters for daring
to mess around with HTML again. I knew better!&lt;/p&gt;

&lt;p&gt;It turns out that Micrsoft Outlook and Google Gmail are partially braindead with respect to CSS. They support
some of it, but not all. In particular the fancy features I used for right justifying specified 
columns and alternating row colors were a bust.&lt;/p&gt;

&lt;p&gt;I went back to work and retrofitted &lt;em&gt;app_html_table_pkg&lt;/em&gt; with a &lt;em&gt;p_older_css_support&lt;/em&gt; flag that you must use
if you are sending the HTML to Outlook clients (and perhaps others).&lt;/p&gt;

&lt;p&gt;Gmail is a complete bust. It does not even respect the older method of specifying 
a style class in the HTML &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt; tags.
Bah! I’m not going to hard code everything in the HTML just to support Gmail.
Most businesses that would use this are on Outlook I suspect.&lt;/p&gt;

&lt;p&gt;With these changes, the tables display correctly (or mostly correct) in both the web and PC versions
of Outlook 365 (PC version 2112 and also Version 2102). I do not know about older Outlook clients.&lt;/p&gt;

&lt;p&gt;Google Chrome, Edge, Firefox, and Thunderbird email all support the original with &lt;em&gt;p_older_css_support&lt;/em&gt; not
set to ‘Y’, but they also handle the less elegant way just fine.&lt;/p&gt;

&lt;p&gt;I’m not even going there on email clients on phones and tablets. Not interested. If you want to hack at it,
be my guest and send a pull request if you get it working. There may be others who want it too.&lt;/p&gt;

</description>
        <pubDate>Tue, 18 Jan 2022 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/plsql/html/2022/01/18/HTML-Table-PLSQL-Redux.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/plsql/html/2022/01/18/HTML-Table-PLSQL-Redux.html</guid>
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        <category>html</category>
        
        <category>table</category>
        
        <category>css</category>
        
        <category>xlst</category>
        
        <category>dbms_xmlgen</category>
        
        <category>xlstype</category>
        
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        <category>html</category>
        
      </item>
    
      <item>
        <title>HTML Table Markup in Oracle</title>
        <description>&lt;h1 id=&quot;html-table-markup-for-oracle-query-resultset&quot;&gt;HTML Table Markup for Oracle Query Resultset&lt;/h1&gt;

&lt;h2 id=&quot;use-case---generate-html-table-markup-inside-oracle-database&quot;&gt;Use Case - Generate HTML Table Markup Inside Oracle Database&lt;/h2&gt;

&lt;p&gt;Whenever a question is posted on this subject, the immediate knee-jerk reaction is to tell the hapless
suppicant to use sql*plus HTML markup option. All well and good, but we do have use cases where
going out to a client program from the database is not practical. One of the most common is
to generate an HTML Table that is included in an email sent directly from the database.&lt;/p&gt;

&lt;p&gt;There is a well established pattern to do this with &lt;em&gt;DBMS_XMLGEN&lt;/em&gt; and &lt;em&gt;XLSTYPE&lt;/em&gt; stylesheet processing.
A web search will return plenty of examples.&lt;/p&gt;

&lt;p&gt;There are a few issues with the technique, the most
vexing of which is that all of the table data is left justified in the cells. If you want numbers
to be right justified, the standard mechanism we reach for with TO_CHAR or LPAD(TO_CHAR to left pad the
result with spaces does not work because HTML rendering ignores whitespace. 
We need to add a modifier to the HTML Table Data (&amp;lt;td&amp;gt;) tag to right-align the cells, but only for particular columns.&lt;/p&gt;

&lt;h2 id=&quot;css-style-sheet-solution&quot;&gt;CSS Style Sheet Solution&lt;/h2&gt;

&lt;p&gt;The implemented solution can be found on my github &lt;a href=&quot;https://github.com/lee-lindley/app_html_table_pkg&quot;&gt;app_html_table_pkg&lt;/a&gt;
repository page. If you are an HTML guru, you should skip straight to the package and skip my explanation.&lt;/p&gt;

&lt;p&gt;I have of course dabbled in HTML but most of that was in pre-historic times before CSS stylesheets gained
traction. I found the CSS subject overwhelming, and it was compounded by being a moving target. Much of the information
you find on the web is no longer best practice.&lt;/p&gt;

&lt;p&gt;We do not have control of the overall document in this case, or at least not always. We may not be able to
put CSS style information in the HTML Header. Luckily for us, somewhere along the way the committee to make
web page development obtuse decided we could use locally scoped styles, meaning it is legal and not
just a hack to put styles directly in the HTML Body. Coolio!&lt;/p&gt;

&lt;p&gt;I complain about how the rules seem obscure and a moving target, but once I finally got a handle on enough
of the pieces, it turns out to be a relatively understandable and isolated bit of HTML code that will allow
us to control the formatting of our table without impacting anything else in the document.&lt;/p&gt;

&lt;h2 id=&quot;html-components&quot;&gt;HTML Components&lt;/h2&gt;

&lt;h3 id=&quot;div&quot;&gt;div&lt;/h3&gt;

&lt;p&gt;We wrap our entire construct in &amp;lt;div id=”plsql-table”&amp;gt;…&amp;lt;/div&amp;gt; to isolate it from the rest of the document.&lt;/p&gt;

&lt;h3 id=&quot;style&quot;&gt;style&lt;/h3&gt;

&lt;p&gt;Next we produce a &amp;lt;style type=”text/css” scoped&amp;gt; section encapsulating our style rules for the table.&lt;/p&gt;

&lt;p&gt;We have directives that adjust the table border and spacing, the optional caption font and spacing,
and directions for &amp;lt;th&amp;gt; table header and &amp;lt;td&amp;gt; element formatting. We can set background and foreground
colors as well.&lt;/p&gt;

&lt;p&gt;This is fairly standard HTML attribute processing that is familiar enough for most of us who are not CSS gurus.&lt;/p&gt;

&lt;h3 id=&quot;specific-columnrow-styles&quot;&gt;specific column/row styles&lt;/h3&gt;

&lt;p&gt;Where the solution becomes more complex is when we direct style elements to particular
columns or rows. The one to solve our specific issue of right justifying particular columns is encoded
as:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;nt&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-of-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Huh. That was my first thought. I’ll read that as for any table row do this for the fourth table data element (aka column).
I’m not going to try to explain the rules because I’ll butcher it, but that is what it does.&lt;/p&gt;

&lt;p&gt;A related construct that lets us alternate the background color on rows is&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;nt&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;odd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AliceBlue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That says for table row elements, on the odd rows use this background color. You could leave the even rows
with the default background or make another directive for nth-child(even).&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;Putting it all together the package can output an HTML table configured specifically
for our query with any style adjustments you desire. You don’t have to understand it all. Most of the time
you can copy/paste the default style and tweak it a bit. This one is on the fancy side while the 
default from the package is an austere black and white.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;plsql-table&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;style &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/css&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;scoped&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
	
	&lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
	    &lt;span class=&quot;nl&quot;&gt;border-spacing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
	    &lt;span class=&quot;nl&quot;&gt;border-collapse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;collapse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;caption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;italic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;larger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;th&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LightGrey&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;th&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;td&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
	    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;odd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AliceBlue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-of-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-of-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;caption&amp;gt;&lt;/span&gt;Poorly Paid People&lt;span class=&quot;nt&quot;&gt;&amp;lt;/caption&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Emp ID&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Full Name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Date_x002C_Hire&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Salary&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;999&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;  Baggins, Bilbo &lt;span class=&quot;ni&quot;&gt;&amp;amp;quot;&lt;/span&gt;badboy&lt;span class=&quot;ni&quot;&gt;&amp;amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;12/31/1999&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;     $123.45&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;206&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;Gietz, William&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;06/07/2002&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;   $8,300.00&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/table&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is how it is rendered in my browser:&lt;/p&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;HTML Table Markup from PL/SQL&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/html_table.gif&quot; alt=&quot;html_table.gif&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Sun, 16 Jan 2022 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/plsql/html/2022/01/16/HTML-Table-PLSQL.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/plsql/html/2022/01/16/HTML-Table-PLSQL.html</guid>
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        <category>html</category>
        
        <category>table</category>
        
        <category>css</category>
        
        <category>xlst</category>
        
        <category>dbms_xmlgen</category>
        
        <category>xlstype</category>
        
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        <category>html</category>
        
      </item>
    
      <item>
        <title>More CSV Fun - Turn CSV into SQL Resultset</title>
        <description>&lt;h1 id=&quot;ad-hoc-csv-parsing&quot;&gt;Ad-Hoc CSV Parsing&lt;/h1&gt;

&lt;p&gt;I’ve been going down some dark holes trying to be able to use CSV data as a basis for starting an ad-hoc query.
I finally arrived at something workable. While I was at it I built myself some tools for manipulating
lists and strings that we can use to make PL/SQL a little less verbose for some tasks.&lt;/p&gt;

&lt;p&gt;Consider the classic way you can hard code data in a SQL script:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;abc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;xyz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col3&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;UNION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ALL&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;456&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ghi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col3&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;UNION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ALL&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lmn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;789&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;opq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col3&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Uggh, that is painful. If you have bind variables and maybe some TO_DATE functions to throw into
the mix it can get even worse.&lt;/p&gt;

&lt;h1 id=&quot;perlish-utility-methods&quot;&gt;Perlish Utility Methods&lt;/h1&gt;

&lt;p&gt;There are two basic tool types provided by this User Defined Type (UDT) Object you can find 
at &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#perlish_util_udt&quot;&gt;perlish_util_udt&lt;/a&gt;. One type are
methods that work like some Perl builtin methods for manipulating lists. The other type facilitates
parsing CSV data.&lt;/p&gt;

&lt;p&gt;One of the constructor methods for &lt;em&gt;perlish_util_udt&lt;/em&gt; takes a string as a parameter that it expects
to contain Comma Separated Values (CSV) per RFC4180 (which means anything Excel puts out as CSV and anything else you are 
likely to find that calls itself CSV). The constructor calls &lt;em&gt;perlish_util_udt.split_csv&lt;/em&gt; on that puppy
and populates the member attribute named &lt;em&gt;arr&lt;/em&gt; with the resulting collection of VARCHAR2 elements.
Together with the &lt;em&gt;perlish_util_udt.get&lt;/em&gt; method that takes an index parameter, this makes
it possible to build the object and access those array elements in a SELECT.&lt;/p&gt;

&lt;p&gt;Now that is all well and good for a single line of CSV, but what if we have a bunch of lines of CSV? The answer
is another method in that toolbox named &lt;em&gt;perlish_util_udt.split_clob_to_lines&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Together these two static methods plus that instance constructor and &lt;em&gt;get&lt;/em&gt; method give us the tools we need
to deal with that set of CSV records.&lt;/p&gt;

&lt;p&gt;An example tells the story better than all those words:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&quot;abc&quot;,123,xyz
def,456,&quot;ghi&quot;
lmn,789,opq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;
                                                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- remember you need a table alias to access object methods and attributes&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- thus making the table alias x for a here.&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col3&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which results in the following (where the quotes were put around the pieces by SqlDeveloper upon Text output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;COL1&quot;	&quot;COL2&quot;	&quot;COL3&quot;
&quot;abc&quot;	&quot;123&quot;	&quot;xyz&quot;
&quot;def&quot;	&quot;456&quot;	&quot;ghi&quot;
&quot;lmn&quot;	&quot;789&quot;	&quot;opq&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Looking at the query in pieces&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&quot;abc&quot;,123,xyz
def,456,&quot;ghi&quot;
lmn,789,opq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;
                                                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;split_clob_to_line&lt;/em&gt; returns a collection of VARCHAR2(4000) elements. We wrap that function call in a TABLE cast (perhaps
unnecessarily depending on your Oracle version), and now we can select those lines as rows from the input string.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_value&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&quot;abc&quot;,123,xyz
def,456,&quot;ghi&quot;
lmn,789,opq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;
                                                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Showing the result in JSON may or may not help. All of the outputs from SqlDevloper want to mess with the quotes.
This shows those quotes are still in the data&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;results&quot; : [
    {
      &quot;columns&quot; : [
        {
          &quot;name&quot; : &quot;R&quot;,
          &quot;type&quot; : &quot;VARCHAR2&quot;
        }
      ],
      &quot;items&quot; : [
        {
          &quot;r&quot; : &quot;\&quot;abc\&quot;,123,xyz&quot;
        },
        {
          &quot;r&quot; : &quot;def,456,\&quot;ghi\&quot;&quot;
        },
        {
          &quot;r&quot; : &quot;lmn,789,opq&quot;
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we have our CSV input broken into lines, we need to break the individual lines into fields. This
is where we usually run into trouble with the SQL toolbox. If you start thinking about reaching for PIVOT/UNPIVOT
remember that our data has natural order (we want the columns to come out in an order or with names), but there
is nothing in the data itself that let’s us specify that to the SQL engine. You can use a bunch 
of REGEXP_SUBSTR calls to parse the pieces into fields, but c’mon man, that is worse than the problem we are trying
to solve. Well, usually anyway. But if we can hide that complexity in an object that lets us split the
data into fields and then access the fields by index number, we preserve our natural order without
getting too gross.&lt;/p&gt;

&lt;p&gt;That leads us to using &lt;em&gt;split_csv&lt;/em&gt; to parse that CSV row/line into a collection:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&quot;abc&quot;,123,xyz
def,456,&quot;ghi&quot;
lmn,789,opq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;
                                                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Showing that result as JSON we can see each row &lt;em&gt;p&lt;/em&gt; is an object with a member attribute that is a collection:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;results&quot; : [
    {
      &quot;columns&quot; : [
        {
          &quot;name&quot; : &quot;P&quot;,
          &quot;type&quot; : &quot;LEE.PERLISH_UTIL_UDT&quot;
        }
      ],
      &quot;items&quot; : [
        {
          &quot;p&quot; : &quot;LEE.PERLISH_UTIL_UDT(LEE.ARR_VARCHAR2_UDT(abc, 123, xyz))&quot;
        },
        {
          &quot;p&quot; : &quot;LEE.PERLISH_UTIL_UDT(LEE.ARR_VARCHAR2_UDT(def, 456, ghi))&quot;
        },
        {
          &quot;p&quot; : &quot;LEE.PERLISH_UTIL_UDT(LEE.ARR_VARCHAR2_UDT(lmn, 789, opq))&quot;
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then the final piece is to access individual elements of a collection. In PL/SQL you could just use the syntax&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;p.arr(3) 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;but that does not work in SQL. Fortunately, the object &lt;em&gt;perlish_util_udt&lt;/em&gt; gives us a &lt;em&gt;get&lt;/em&gt; function we can call
with an index parameter. The only trick is that in SQL in order to access an object method or attributes,
you must have a table alias for the “table” that provided the object instance in the query. In our example
our “table” is named “a” and our alias for “a” is “x”.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perlish_util_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'[&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&quot;abc&quot;,123,xyz
def,456,&quot;ghi&quot;
lmn,789,opq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]'&lt;/span&gt;
                                                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- remember you need a table alias to access object methods and attributes&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- thus making the table alias x for a here.&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col3&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, the final result this time in json:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;results&quot; : [
    {
      &quot;columns&quot; : [
        {
          &quot;name&quot; : &quot;COL1&quot;,
          &quot;type&quot; : &quot;VARCHAR2&quot;
        },
        {
          &quot;name&quot; : &quot;COL2&quot;,
          &quot;type&quot; : &quot;VARCHAR2&quot;
        },
        {
          &quot;name&quot; : &quot;COL3&quot;,
          &quot;type&quot; : &quot;VARCHAR2&quot;
        }
      ],
      &quot;items&quot; : [
        {
          &quot;col1&quot; : &quot;abc&quot;,
          &quot;col2&quot; : &quot;123&quot;,
          &quot;col3&quot; : &quot;xyz&quot;
        },
        {
          &quot;col1&quot; : &quot;def&quot;,
          &quot;col2&quot; : &quot;456&quot;,
          &quot;col3&quot; : &quot;ghi&quot;
        },
        {
          &quot;col1&quot; : &quot;lmn&quot;,
          &quot;col2&quot; : &quot;789&quot;,
          &quot;col3&quot; : &quot;opq&quot;
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Pretty handy, eh? I’ll save the other half of the functionality provided by &lt;em&gt;perlish_util_udt&lt;/em&gt; for another day.&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Jan 2022 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/09/More-CSV-Fun.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/perl/2022/01/09/More-CSV-Fun.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>perl</category>
        
        <category>regexp</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>perl</category>
        
      </item>
    
      <item>
        <title>Perl Like Operations in PL/SQL</title>
        <description>&lt;h1 id=&quot;the-problem-needs-string-hacking&quot;&gt;The Problem Needs String Hacking&lt;/h1&gt;

&lt;p&gt;I was writing a PL/SQL function to generate a MERGE sql statement from input data, the name of a table, 
a list of columns in the input data,
and either a list of join columns or if not provided, getting them from the primary key constraint.
This is something I’ve done in Perl before relatively easily, and I wanted to turn my hand to
doing it in PL/SQL.&lt;/p&gt;

&lt;p&gt;My first effort let me down the road of creating a Polymorphic Table Function to parse the CSV data.
It was a nice exercise and I produced a workable PTF implementation to parse a clob and generate
Oracle column data, but it is too complex for this relatively simple use case.&lt;/p&gt;

&lt;p&gt;Next I turned towards doing it all in a set of inline PL/SQL WITH functions where I intended to have
it generate the UNION ALL set of rows for the data for the MERGE USING clause. I don’t need to deploy
any code in the database that my team may or may not be able to support. It winds up being
a script I use to generate deployment code.&lt;/p&gt;

&lt;p&gt;Man, what a grind.
PL/SQL is very good for database things. It sucks badly for text processing like this.
I found myself pining away for Perl operations like &lt;em&gt;split&lt;/em&gt;, &lt;em&gt;map&lt;/em&gt; and &lt;em&gt;join&lt;/em&gt;. I’m not even going to complain
about lack of variable interpolation in PL/SQL strings and how you must concatentate everything with hideous syntax
because it is what it is. Yet some things we can do something about.&lt;/p&gt;

&lt;p&gt;Consider needing to take a nested table of column names and generate the list that goes
with the INSERT(…) clause of the MERGE.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;WHEN NOT MATCHED THEN INSERT(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Phew, that is ugly. Or how about the MERGE UPDATE clause? We need to write:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;WHEN MATCHED THEN UPDATE SET
    t.col1 = q.col1,
    t.col2 = q.col2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I started off writing a custom &lt;em&gt;join&lt;/em&gt; function that also took a template string as an argument.
It was sort of a meld between the Perl &lt;em&gt;join&lt;/em&gt; and &lt;em&gt;map&lt;/em&gt; methods.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ku$_vcnt&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_template&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l_s&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apply_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_val&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;l_t&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_template&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;l_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; 
                &lt;span class=&quot;n&quot;&gt;l_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;__PLACEHOLDER__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apply_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;l_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apply_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- join&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now to write the UPDATE portion I have&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;WHEN MATCHED THEN UPDATE SET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_non_pk_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,
    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;t.__PLACEHOLDER__ = q.__PLACEHOLDER__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It did not make me happy though. It was a little too customized.&lt;/p&gt;

&lt;p&gt;I also had some utility methods as standalone functions (&lt;em&gt;split_csv&lt;/em&gt; and &lt;em&gt;transform_perl_regexp&lt;/em&gt;) that
really needed a package or user defined type home. I wound up creating a new User Defined Type
to hold my Perlish methods. I called it &lt;em&gt;japh_util_udt&lt;/em&gt; originally, which comes from the phrase “I’m just another perl hacker.”
Most people don’t get that, so I renamed it to &lt;em&gt;perlish_util_udt&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;perlish-utility-user-defined-type&quot;&gt;Perlish Utility User Defined Type&lt;/h1&gt;

&lt;p&gt;You can find it in 
my &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;plsql_utilities github repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From the REAMDE.md in the repository:&lt;/p&gt;

&lt;h2 id=&quot;perlish_util_udt&quot;&gt;perlish_util_udt&lt;/h2&gt;

&lt;p&gt;It isn’t Perl, but it makes some Perlish things a bit easier in PL/SQL. We also get
handy methods for splitting Comma Separated Value (CSV) text into lines and fields,
which you can use independent of the Perlish methods, and even one that turns a CSV
clob into a private temporary table.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There is valid argument
that when you are programming in a language you should use the facilities of that language, 
and that attempting to layer the techniques of another language upon it is a bad idea. I see the logic
and partially agree. I expect those who later must support my work that uses this utility will curse me. Yet
PL/SQL really sucks at some string and list related things. This uses valid PL/SQL object techniques
to manipulate strings and lists in a way that is familiar to Perl hackers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;em&gt;perlish_util_udt&lt;/em&gt; object instance holds an &lt;em&gt;arr_varchar2_udt&lt;/em&gt; collection attribute which you will use when employing the following member methods;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;map&lt;/li&gt;
  &lt;li&gt;join&lt;/li&gt;
  &lt;li&gt;sort&lt;/li&gt;
  &lt;li&gt;get&lt;/li&gt;
  &lt;li&gt;combine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All member methods except &lt;em&gt;get&lt;/em&gt; have static alternatives using &lt;em&gt;arr_varchar2_udt&lt;/em&gt; parameters and return types, so you
are not forced to use the Object Oriented syntax.&lt;/p&gt;

&lt;p&gt;It has static method &lt;em&gt;split_csv&lt;/em&gt; (returns &lt;em&gt;arr_varchar2_udt&lt;/em&gt;) that 
formerly lived as a standalone function in the plsql_utilities library as &lt;em&gt;split&lt;/em&gt;.
We have a static method &lt;em&gt;split_clob_to_lines&lt;/em&gt; that returns an &lt;em&gt;arr_varchar2_udt&lt;/em&gt; collection
of “records” from what is assumed to be a CSV file. It parses for CSV syntax when splitting the lines
which means there can be embedded newlines in text fields in a “record”.&lt;/p&gt;

&lt;p&gt;There is a static procedure &lt;em&gt;create_ptt_csv&lt;/em&gt; that consumes a CLOB containing lines of CSV data
and turns it into a private temporary table for your session. The PTT has column names from the
first line in the CLOB.&lt;/p&gt;

&lt;p&gt;It also has a static method named &lt;em&gt;transform_perl_regexp&lt;/em&gt; that has nothing to do with arrays/lists, but is Perlish.&lt;/p&gt;

&lt;p&gt;Most of the member methods are chainable which is handy when you are doing a series of operations.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;I will work on a full demonstration to generate that deployable MERGE I described
in the introduction. Seeing it in real action rather than the contrived cases
of the documentation examples will show why I’m excited about it. Maybe PL/SQL
can suck a little less for hacking strings and lists.&lt;/p&gt;
</description>
        <pubDate>Sun, 02 Jan 2022 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/plsql/perl/2022/01/02/Perl-Like-Operations-PLSQL.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/plsql/perl/2022/01/02/Perl-Like-Operations-PLSQL.html</guid>
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        <category>perl</category>
        
        <category>regexp</category>
        
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        <category>perl</category>
        
      </item>
    
      <item>
        <title>Polymorphic Table Function (PTF) for CSV (take 3)</title>
        <description>&lt;h2 id=&quot;why-polymorphic-table-functions-ptf&quot;&gt;Why Polymorphic Table Functions (PTF)?&lt;/h2&gt;

&lt;p&gt;They let you code a resultset transformation for the SQL engine 
without knowing at compile time either or both of&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The input cursor/resultset definition.&lt;/li&gt;
  &lt;li&gt;The output cursor/resultset definition.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ordinary Pipelined Table Functions already let you consume any type of input, but you must build
your own code to figure out what is in it, generally by using &lt;em&gt;DBMS_SQL&lt;/em&gt;. If you want to see
how that is done, I have a User Defined Type 
Object &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities/tree/main/app_dbms_sql&quot;&gt;app_dbms_sql_udt&lt;/a&gt; you
can check out. Ordinary Pipelined Table Functions require that you define the output type at compile time.&lt;/p&gt;

&lt;p&gt;Even if you can define your output resultset at compile time, the PTF functionality takes care of
many of the details you must otherwise encode manually with &lt;em&gt;DBMS_SQL&lt;/em&gt;. As I’ll demonstrate later, the 
amount of code needed to produce a CSV file generator is substantially less using a PTF than &lt;em&gt;DBMS_SQL&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;polymorphic-table-function-tutorials-and-examples&quot;&gt;Polymorphic Table Function Tutorials and Examples&lt;/h2&gt;

&lt;p&gt;Oracle’s introduction to Polymorphic Table Functions is in the &lt;em&gt;Database PL/SQL Language Reference&lt;/em&gt;
&lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-optimization-and-tuning.html#GUID-981102A8-5204-4931-B10A-93486304B184&quot;&gt;12.6 Overview of Polymorphic Table Functions&lt;/a&gt;.
&lt;em&gt;PL/SQL Packages and Types Reference&lt;/em&gt; has documentation for the 
&lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_TF.html&quot;&gt;DBMS_TF&lt;/a&gt; package
that provides the server side implementation.
When I want to really understand the types and constants I found it easier to look directly at the &lt;em&gt;DBMS_TF&lt;/em&gt; package specification in the database using the Toad or SqlDeveloper schema browser. The types and constants are scattered through the documentation which makes them a little harder to put together than looking straight at the package spec.&lt;/p&gt;

&lt;p&gt;The best beginner introduction to PTFs I found was by 
Tim Hall &lt;a href=&quot;https://oracle-base.com/articles/18c/polymorphic-table-functions-18c&quot;&gt;Polymorphic Table Functions in Oracle Database 18c&lt;/a&gt;. He has simple examples to lead you in gently.&lt;/p&gt;

&lt;p&gt;There are a suite of example PTF implementations in the &lt;a href=&quot;https://livesql.oracle.com/&quot;&gt;Oracle LiveSql collection&lt;/a&gt;.
Enter the search term “polymorphic table function”. 
This &lt;a href=&quot;https://blogs.oracle.com/oraclemagazine/post/how-to-dynamically-change-the-columns-in-a-sql-query&quot;&gt;article from Oracle Magazine by Chris Saxon&lt;/a&gt; 
goes along with one of them and I found it helpful.&lt;/p&gt;

&lt;h2 id=&quot;replication_factor&quot;&gt;Replication_Factor&lt;/h2&gt;

&lt;p&gt;The general design pattern for a PTF is that it transforms rows from one result set into another. 
By default there is a one to one relationship on the number of rows OUT to the number of rows IN.
You can choose to include columns from the input row in the output row by setting the column &lt;em&gt;pass_through&lt;/em&gt;
flag to TRUE. You can add new columns to the output resultset.
Whether you have any passthrough columns or not, there is still a relationship
between the number of input rows and the number of output rows the function will produce from your &lt;em&gt;New_Columns&lt;/em&gt; tables.
In other words you might set all of the input columns to &lt;em&gt;pass_through&lt;/em&gt;=FALSE, but the PTF will only
produce the number of rows from your &lt;em&gt;New_Columns&lt;/em&gt; tables that match the number of rows of your input resultset.&lt;/p&gt;

&lt;p&gt;There is a capability to specify how many output rows there are for any given input row using
&lt;em&gt;DBMS_TF.row_replication&lt;/em&gt; procedure (or a scalar parameter). The procedure version requires an argument that
is an index-by table you populate with a numeric value for every input row in that &lt;em&gt;fetch_rows&lt;/em&gt; call. 
Note how carefully I stated that.
You can control the number of output rows counted for each and every input row by populating an array
with a numeric value for every input row. You can do that for any of the &lt;em&gt;fetch_rows&lt;/em&gt; calls but do not have to do so
for all of them. If you do not call &lt;em&gt;row_replication&lt;/em&gt; procedure during a particular &lt;em&gt;fetch_rows&lt;/em&gt;,
then you get one to one row output.&lt;/p&gt;

&lt;p&gt;You can have 0, 1 or more output rows for any specific input row. If a column from an input row is
marked with &lt;em&gt;pass_through&lt;/em&gt;=TRUE, you will get that value in your output if replication factor is 1.
You will get it on 2 rows if the replication factor is 2, etc… If the replication factor is 0
for that input row, you will not see that value in the output.  In the diagram below,
the input value from row 2 will not appear in the output resultset.
The number of output rows will be the sum of the &lt;em&gt;replication factor&lt;/em&gt; values.&lt;/p&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;PTF Replication Factor&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/PTF_Replication_Factor.gif&quot; alt=&quot;PTF Replication Factor&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;In the work I’ve done with CSV data I have been setting all input columns to &lt;em&gt;pass_through&lt;/em&gt;=FALSE and
using the &lt;em&gt;replication_factor&lt;/em&gt; strictly as a way to tell Oracle how many output rows are in my output column arrays.
When you have &lt;em&gt;pass_through&lt;/em&gt;=TRUE you have to understand what Oracle does behind the scenes and how the two arrays
match up.&lt;/p&gt;

&lt;h2 id=&quot;walk-through-a-ptf-example&quot;&gt;Walk Through a PTF Example&lt;/h2&gt;

&lt;p&gt;We will walk through a Polymorphic Table Function and an associated package
for creating a CSV file from an Oracle query. This requires Oracle 18c or better.&lt;/p&gt;

&lt;p&gt;I have
a prior effort for CSV file creation at &lt;a href=&quot;https://github.com/lee-lindley/app_csv&quot;&gt;app_csv_udt&lt;/a&gt;
that should run under Oracle 10g or later. It is an Oracle Object Type, an approach I happen
to like, but my coworkers not so much.&lt;/p&gt;

&lt;p&gt;That earlier code is more complicated than this effort
using a PTF. Oracle has taken care of most of the gnarly cursor management and bulk array processing
leaving us with less to do in the PTF guts of our package. That doesn’t mean it is simple. I found
the PTF functionality a struggle to grasp. The documentation is light and the examples I 
see published didn’t help me understand how it worked on the inside and what our design choices were
as easily as I pick up many new things. When I look back at the examples different
things stand out now than when I first went through them, so perhaps I just had a blind spot. 
I hope this article may help a few people learn about PTFs more easily.&lt;/p&gt;

&lt;p&gt;The complete implementation can be found in 
the &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#app_csv_pkg&quot;&gt;PLSQL_utilities library&lt;/a&gt; 
I maintain on github.&lt;/p&gt;

&lt;h2 id=&quot;the-ptf-components&quot;&gt;The PTF Components&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;The PTF function specification may be standalone, but it makes sense to me to include it in the package. Since it is part of the package which has a descriptive name, I named the function simply &lt;em&gt;ptf&lt;/em&gt;. Note that there is no function body for the PTF function and no entry for it in the package body. It exists only in the package specification.&lt;/li&gt;
  &lt;li&gt;The &lt;em&gt;describe&lt;/em&gt; function is a required element to support the PTF. It is usually not that complex (at least as far as I have seen so far).&lt;/li&gt;
  &lt;li&gt;The &lt;em&gt;fetch_rows&lt;/em&gt; procedure is where we do most of the work to transform our input resultset to an output resultset.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here are the PTF components of the package specification for our Example. There are a few more utility procedures and functions we will add later. The package name is &lt;em&gt;app_csv_pkg&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;AUTHID&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CURRENT_USER&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- All non numeric fields will be surrounded with double quotes. Any double quotes in the&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- data will be backwacked to protect them. Newlines in the data are passed through as is&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- which might cause issues for some CSV parsers.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;                   &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_header_row&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;            &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- you can set these to NULL if you want the default TO_CHAR conversions&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_format&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_interval_format&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt; 
        &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- so can ORDER the input&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--ROW &lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;POLYMORPHIC&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- the describe and fetch methods are used exclusively by the PTF mechanism. You cannot&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- call them directly.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OUT&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TABLE_T&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_header_row&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;            &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- you can set these to NULL if you want the default TO_CHAR conversions&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_format&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_interval_format&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DESCRIBE_T&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;p_header_row&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;            &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- you can set these to NULL if you want the default TO_CHAR conversions&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_format&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_interval_format&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how the first argument to the function named &lt;em&gt;ptf&lt;/em&gt; is of type &lt;em&gt;TABLE&lt;/em&gt;. That is special in PTF land.
It must be either a schema level table or view name, or else a Common Table Expression (CTE) (aka WITH clause).
You cannot pass in a CURSOR or subselect. I vaguely understand the reasons for that, but not well enough
to try to explain. The first argument with the same name is given to the &lt;em&gt;describe&lt;/em&gt; function, but it
has a different type.&lt;/p&gt;

&lt;p&gt;If you have COLUMNS Pseudo-Operator arguments, they are to be the same between the PTF and &lt;em&gt;describe&lt;/em&gt;
functions (except for type – see below).&lt;/p&gt;

&lt;p&gt;Neither the TABLE nor COLUMNS type arguments are passed to the &lt;em&gt;fetch_rows&lt;/em&gt; procedure.&lt;/p&gt;

&lt;p&gt;This gem from the &lt;em&gt;DBMS_TF&lt;/em&gt; documentation says what I just said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The arguments of the PTF function and DESCRIBE function must match, but with the type of any TABLE argument replaced with the DBMS_TF.TABLE_T descriptor type, and the type of any COLUMNS argument replaced with DBMS_TF.COLUMN_T descriptor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All of the other arguments to all three methods are application specific and must be identical
between the three methods.  Even though you may not need the
arguments in the &lt;em&gt;describe&lt;/em&gt; function, the argument footprint much match what the SQL engine is going
to provide in the call based on what it sees in the PTF definition.&lt;/p&gt;

&lt;p&gt;It is interesting that our &lt;em&gt;describe&lt;/em&gt; and &lt;em&gt;fetch_rows&lt;/em&gt; methods are stateless. The SQL engine code that
calls our methods maintains the state. When we call &lt;em&gt;get/put&lt;/em&gt; methods that state is respected. There
is an identifier called the XID that indexes that runtime state.
It looks similar to the “Cursor id” value in &lt;em&gt;DBMS_SQL&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-describe-function&quot;&gt;The &lt;em&gt;describe&lt;/em&gt; Function&lt;/h2&gt;

&lt;p&gt;From the package body:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OUT&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TABLE_T&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_header_row&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;            &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- you can set these to NULL if you want the default TO_CHAR conversions&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_format&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_interval_format&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DESCRIBE_T&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_new_cols&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns_new_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- stop all input columns from being in the output &lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pass_through&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_read&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- create a single new output column for the CSV row string&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_new_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_metadata_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;k&quot;&gt;name&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CSV_ROW&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_varchar2&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;-- we will use row replication to put a header out on the first row if desired&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_columns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_new_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row_replication&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_header_row&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We need to examine all of the input column values but we do not want them to be passed through to our
output rows. To that end we loop through the input column list setting &lt;em&gt;pass_through&lt;/em&gt; to FALSE
and &lt;em&gt;for_read&lt;/em&gt; to TRUE.&lt;/p&gt;

&lt;p&gt;We will be defining a single “new” column for our output rowset. To do that we need a table of column types
which we get with the type &lt;em&gt;DBMS_TF.columns_new_t&lt;/em&gt; for our variable &lt;em&gt;v_new_cols&lt;/em&gt;. We then populate
the first and only element of that table with a &lt;em&gt;column_metadata_t&lt;/em&gt; record with values for the column &lt;em&gt;name&lt;/em&gt;
and column &lt;em&gt;type&lt;/em&gt;. Note that the type is a numeric we get from a constant, not the descriptive name like ‘VARCHAR2’.
Digesting this bit of convoluted crafting is where it is handy to be staring at the package
specification for &lt;em&gt;DBMS_TF&lt;/em&gt;. It is easier than hunting through the type definitions in the documentation.&lt;/p&gt;

&lt;p&gt;The thing we return from the &lt;em&gt;describe&lt;/em&gt; function 
is a &lt;em&gt;describe_t&lt;/em&gt; record. We populate &lt;em&gt;new_columns&lt;/em&gt; with the structure we populated
for our new output column named ‘CSV_ROW’. We also provide a boolean value for &lt;em&gt;row_replication&lt;/em&gt;. We set 
that to TRUE if we need to produce a header row. If we do not need to produce a header row,
we will have one to one correspondence of output rows from input rows so no replication is required.&lt;/p&gt;

&lt;h2 id=&quot;the-fetch_rows-procedure&quot;&gt;The &lt;em&gt;fetch_rows&lt;/em&gt; Procedure&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;fetch_rows&lt;/em&gt; is where most of the work is done. There is a lot to unpack here&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;p_header_row&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;            &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- you can leave these NULL if you want the default TO_CHAR conversions for your session&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_format&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_interval_format&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;               &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_rowset&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_set_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- the input rowset of CSV rows&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_row_cnt&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_val_col&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_varchar2_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_repfac&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_naturaln_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_fetch_pass&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_out_row_i&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;v_rowset&lt;/em&gt; will contain our input data for this fetch. &lt;em&gt;v_val_col&lt;/em&gt; is a collection
where we will place our output data for this fetch. &lt;em&gt;v_repfac&lt;/em&gt; is where we MAY
put a numeric value for each input row that will be 2 on the first row and 1 on all the rest.
The reason we need that is to produce a header row. We need to output one more row than
we have input rows, but we only need to do that on the first fetch. After that it won’t
be necessary to populate or use &lt;em&gt;v_repfac&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;v_fetch_pass&lt;/em&gt; is used to determine whether or not we are on the first fetch and &lt;em&gt;v_out_row_i&lt;/em&gt;
is to keep track of the number of output rows on this fetch iteration.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;c1&quot;&gt;-- If the user does not want to change the NLS formats for the session&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- but has custom coversions for this query, then we will apply them using TO_CHAR&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_conv_fmt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;RECORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- type&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- to_char fmt string&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_tab_conv_fmt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_conv_fmt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_conv_fmts&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;t_tab_conv_fmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apply_cust_conv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_row_index&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_conv_fmts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXISTS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                      &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                        &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                            &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_conv_fmts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
                                &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_date&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; 
                                    &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rowset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_row_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_conv_fmts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_interval_ym&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; 
                                    &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rowset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_interval_ym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_row_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_conv_fmts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_interval_ds&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; 
                                    &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rowset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_interval_ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_row_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_conv_fmts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- backwack the dquotes if any&lt;/span&gt;
                        &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col_to_char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rowset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_row_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;INSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- apply_cust_conv&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The custom conversion code is a bit ugly. The default conversion to char provided by &lt;em&gt;DBMS_TF.col_to_char()&lt;/em&gt;
is almost perfect. It takes care of putting strings into double quotes and backwacking any embedded double quotes.
For Date and Interval conversions it will also place the results in double quotes; however, for those
it depends on the default string coversions (which can be further muddied by NLS_DATE_FORMAT).&lt;/p&gt;

&lt;p&gt;The other oddity can happen when NLS_NUMERIC_CHARACTERS employs a comma. 
If we encounter a separator character in an unquoted value, we quote it.&lt;/p&gt;

&lt;p&gt;Other than the use of &lt;em&gt;DBMS_TF.col_to_char&lt;/em&gt;, this is standard PL/SQL (though perhaps a bit ugly), so
I’m not going to expand on what it does other than to say it converts Oracle types to strings in
a user specified manner while meeting the quoting needs for CSV output.&lt;/p&gt;

&lt;p&gt;Now with the main &lt;em&gt;fetch_rows&lt;/em&gt; body:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_header_row&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- We need to put out a header row, so we have to engage in replication_factor shenanigans.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- This is in case FETCH is called more than once. We get and put to the store&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- the fetch count.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- get does not change value if not found in store so starts with our default 0 on first fetch call&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xstore_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;v_fetch_pass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_fetch_pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('xstore_get: '||v_fetch_pass);&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_fetch_pass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- we do not need a header column. this will double as the flag&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If we need to produce a header row, then we need to know whether this is the first fetch call
or not. We use &lt;em&gt;xstore_get&lt;/em&gt; here and &lt;em&gt;xstore_put&lt;/em&gt; later to maintain our state between calls to &lt;em&gt;fetch_rows&lt;/em&gt;.
If we do not need a header row, set our flag variable to skip that.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;c1&quot;&gt;-- get the data for this fetch &lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_row_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rowset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_row_cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;-- set up for custom TO_CHAR conversions if requested for date and/or interval types&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_format&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_conv_fmts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_conv_fmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_date_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_interval_format&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; 
                &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_interval_ym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_interval_ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
            &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_conv_fmts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_conv_fmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_interval_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We obtain the resultset data for this fetch, the number of rows and the number of columns.
We then set up the custom conversion configuration if needed. Note that &lt;em&gt;v_conv_fmts&lt;/em&gt; is sparse
and possibly empty.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_fetch_pass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- this is first pass and we need header row&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- the first row of our output will get a header row plus the data row&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_repfac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- the rest of the rows will be 1 to 1 on the replication factor&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_row_cnt&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_repfac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- these names are already double quoted and Oracle will not allow a doublequote inside a column alias&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_val_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_val_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_val_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;--join the column names with ,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_out_row_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('header row: '||v_val_col(1));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- otherwise v_out_row_i is 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On the first fetch and only when we need to produce a header row, we set up our
replication factor table. As stated above we want two output rows for the first
input row, then one each for all the others.&lt;/p&gt;

&lt;p&gt;We build the header row by joining the column names with the separator character (comma most likely).&lt;/p&gt;

&lt;p&gt;Next we loop through the input rows building the corresponding output column (we only output a single column!).&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_row_cnt&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_out_row_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_out_row_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- concatenate the string representations of columns with ',' separator&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- into a single column for output on this row.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- col_to_char() conveniently surrounds the character representation&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- of non-numeric fields with double quotes. If there is a double quote in&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- that data it will backwack it. Newlines in the field are passed through unchanged.&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_val_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_out_row_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apply_cust_conv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;--DBMS_TF.col_to_char(v_rowset(1), i);&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_cnt&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_val_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_out_row_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_val_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_out_row_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apply_cust_conv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;--DBMS_TF.col_to_char(v_rowset(j), i);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we generated a header row on this pass we submit our &lt;em&gt;replication_factor&lt;/em&gt; table,
then store our state for the next fetch pass.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_header_row&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;-- save for possible next fetch call&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_fetch_pass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- only on the first fetch &lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_replication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replication_factor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_repfac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_fetch_pass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_fetch_pass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xstore_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;v_fetch_pass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_fetch_pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- otherwies we did not do any replication and will get one for one with input rows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Notice that if we did not output a header row on this pass, we do not call &lt;em&gt;DBMS_TF.row_replication&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And finally we tell the engine about our single output column collection.&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_val_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;example-usage&quot;&gt;Example Usage&lt;/h2&gt;

&lt;p&gt;An example including an ORDER BY clause for the PTF input:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hire_date&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Hire Date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--ORDER BY last_name, first_name&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_csv_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Employee Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Hire Date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_date_format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;YYYYMMDD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rownum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Output (notice how the header row counts as one of the 10 rows! It is just a data record in the resultset.):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;Employee Name&quot;,&quot;Hire Date&quot;,&quot;Employee ID&quot;
&quot;Abel, Ellen&quot;,&quot;20040511&quot;,174
&quot;Ande, Sundar&quot;,&quot;20080324&quot;,166
&quot;Atkinson, Mozhe&quot;,&quot;20051030&quot;,130
&quot;Austin, David&quot;,&quot;20050625&quot;,105
&quot;Baer, Hermann&quot;,&quot;20020607&quot;,204
&quot;Baida, Shelli&quot;,&quot;20051224&quot;,116
&quot;Banda, Amit&quot;,&quot;20080421&quot;,167
&quot;Bates, Elizabeth&quot;,&quot;20070324&quot;,172
&quot;Bell, Sarah&quot;,&quot;20040204&quot;,192
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The package implementation at &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#app_csv_pkg&quot;&gt;PLSQL_utilities library&lt;/a&gt; 
adds two &lt;em&gt;get_clob&lt;/em&gt; functions and two &lt;em&gt;write_file&lt;/em&gt; procedures that can be passed either a CURSOR
(that is expected to end with a call to SELECT * FROM app_csv_pkg.ptf(…), 
or a SQL string CLOB that does the same.&lt;/p&gt;

&lt;p&gt;This implementation has almost as much functionality as my original Object Oriented version
that uses &lt;em&gt;DBMS_SQL&lt;/em&gt;, but with a LOT less code. I think this version is also easier to understand
once you get over the shock and awe around learning about Polymorphic Table Functions.
I hope this article reduces the impact of that flash bang.&lt;/p&gt;
</description>
        <pubDate>Fri, 31 Dec 2021 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2021/12/31/Polymorphic-Table-Functions-3.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2021/12/31/Polymorphic-Table-Functions-3.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Polymorphic Table Function for CSV (take 2)</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;In a prior blog post &lt;a href=&quot;https://lee-lindley.github.io/2021/12/27/PTF-Hard-Coding-Data&quot;&gt;Polymorphic Table Function to the Rescue?&lt;/a&gt;
I used a hammer to make a Polymorphic Table Function do what I wanted. I wanted a single clob as input
and multiple rows as output. I abused a PTF replication feature to get my way, but
it wasn’t the right thing to do.&lt;/p&gt;

&lt;p&gt;The general design pattern for a PTF is that it transforms rows from one result set into another, but
for the most part there is a one to one relationship on the number of rows (replication feature nothwithstanding).&lt;/p&gt;

&lt;p&gt;There is a capability to specify how many output rows there are for any given input row using
&lt;em&gt;DBMS_TF.row_replication&lt;/em&gt; procedure. The argument you provide is a table you populate with a
value for every row returned by &lt;em&gt;DBMS_TF.get_row_set&lt;/em&gt; in that fetch call. The value can be 0 meaning
you do not return a value for that row. In the documentation
for &lt;em&gt;DBMS_TF.get_row_set&lt;/em&gt; is an example section titled &lt;em&gt;Stack Polymorphic Table Function Example&lt;/em&gt;
that shows a PTF named &lt;em&gt;stack&lt;/em&gt; that does just that.&lt;/p&gt;

&lt;p&gt;Although it is possible to do what I originally intended, it still seems klunky.&lt;/p&gt;

&lt;p&gt;In order to conform to the more common PTF design pattern I broke my problem into two parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;split a clob into lines&lt;/li&gt;
  &lt;li&gt;split each line into fields and output as Typed column values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It requires the user to do two steps, but it is a cleaner design that fits the pattern of other PTF functions.&lt;/p&gt;

&lt;p&gt;The first part is achieved with an ordinary Pipelined table function that takes a CLOB as input
and splits it into lines. It respects the CSV format quoting mechanism for protecting newlines in the data,
so it is a little more complex than you might think.&lt;/p&gt;

&lt;p&gt;The second part is achieved much as I did in the above mentioned blog post, but using the CSV row data as
TABLE input rather than reading the CLOB directly.&lt;/p&gt;

&lt;p&gt;From the README.md on github:&lt;/p&gt;

&lt;h2 id=&quot;csv_to_table&quot;&gt;csv_to_table&lt;/h2&gt;

&lt;p&gt;Given a set of rows containing CSV strings, or a CLOB containing multiple lines of CSV strings,
split the records into component column values and return a resultset
that appears as if it was read from 
a table in your schema (or a table to which you have SELECT priv).
We provide a Polymorphic Table Function for your use to achieve this.&lt;/p&gt;

&lt;p&gt;You can either start with a set of CSV strings as rows, or with a CLOB
that contains multiple lines, each of which are a CSV record. Note that 
this is a full blown CSV parser that should handle any records that comply
with RFC4180 (See https://www.loc.gov/preservation/digital/formats/fdd/fdd000323.shtml).&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;           &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- csv list -- could be COLUMNS() construct instead&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- uses nls_date_format if null&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROW&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;POLYMORPHIC&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- public type to be returned by split_clob_to_lines PIPE ROW function&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_csv_row_rec&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;RECORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- the csv row&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;-- line number in the input&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_arr_csv_row_rec&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_csv_row_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- split a clob into a row for each line.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- Handle case where a &quot;line&quot; can have embedded LF chars per RFC for CSV format&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- Throw out completely blank lines (but keep track of line number)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_arr_csv_row_rec&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;To continue with my example from before I have a demo table from which to grab column types:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CREATE TABLE my_table_name(id number, msg VARCHAR2(1024), dt DATE);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is the query using the revised package:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'!&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;23, &quot;this contains a comma (,)&quot;, 06/30/2021
47, &quot;this contains a newline (
)&quot;, 01/01/2022

73, and we can have backwacked comma (\,),
92, what about backwacked dquote &amp;gt;\&quot;&amp;lt;?, 12/28/2021
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;my_table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;id, msg, dt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MM/DD/YYYY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The blank line is ignored; however, the line numbers are maintained through the process so that
errors/problems can be reported.&lt;/p&gt;

&lt;p&gt;The resultset is as expected. Note the NULL date value on line 4. Here is a JSON representation:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;results&quot; : [
    {
      &quot;columns&quot; : [
        {
          &quot;name&quot; : &quot;ID&quot;,
          &quot;type&quot; : &quot;NUMBER&quot;
        },
        {
          &quot;name&quot; : &quot;MSG&quot;,
          &quot;type&quot; : &quot;VARCHAR2&quot;
        },
        {
          &quot;name&quot; : &quot;DT&quot;,
          &quot;type&quot; : &quot;DATE&quot;
        }
      ],
      &quot;items&quot; : [
        {
          &quot;id&quot; : 23,
          &quot;msg&quot; : &quot;this contains a comma (,)&quot;,
          &quot;dt&quot; : &quot;06/30/2021&quot;
        },
        {
          &quot;id&quot; : 47,
          &quot;msg&quot; : &quot;this contains a newline (\n)&quot;,
          &quot;dt&quot; : &quot;01/01/2022&quot;
        },
        {
          &quot;id&quot; : 73,
          &quot;msg&quot; : &quot;and we can have backwacked comma (,)&quot;,
          &quot;dt&quot; : &quot;&quot;
        },
        {
          &quot;id&quot; : 92,
          &quot;msg&quot; : &quot;what about backwacked dquote &amp;gt;\\\&quot;&amp;lt;?&quot;,
          &quot;dt&quot; : &quot;12/28/2021&quot;
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As I mentioned this tracks the input line numbers including the blank lines that it discards.
Here is an example of an error in the date conversion on the 4th row:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_clob_to_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;q'!&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;23, &quot;this contains a comma (,)&quot;, 06/30/2021
47, &quot;this contains a newline (
)&quot;, 01/01/2022

73, and we can have backwacked comma (\,),12/24/
92, what about backwacked dquote &amp;gt;\&quot;&amp;lt;?, 12/28/2021
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;my_table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;id, msg, dt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MM/DD/YYYY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The error text that it raises is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ORA-20202: line number:4 col:3
Line: 73, and we can have backwacked comma (\,),12/24
has Oracle error: ORA-01840: input value not long enough for date format
ORA-06512: at &quot;LEE.CSV_TO_TABLE_PKG&quot;, line 283
ORA-06512: at line 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;getting-the-code&quot;&gt;Getting the Code&lt;/h2&gt;

&lt;p&gt;You can find the package on my github site under repository &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;plsql_utilities&lt;/a&gt;.
For the moment it is in the branch named &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities/tree/parse_csv&quot;&gt;parse_csv&lt;/a&gt;,
but I expect to merge it to main in the not too distant future.&lt;/p&gt;

&lt;p&gt;Hope it was helpful.&lt;/p&gt;
</description>
        <pubDate>Tue, 28 Dec 2021 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2021/12/28/Polymorphic-Table-Function-for-CSV-2.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2021/12/28/Polymorphic-Table-Function-for-CSV-2.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Polymorphic Table Function to the Rescue?</title>
        <description>&lt;h1 id=&quot;hard-coding-data-in-sql-and-plsql&quot;&gt;Hard Coding Data in SQL and PL/SQL&lt;/h1&gt;

&lt;h2 id=&quot;use-case---populate-a-configuration-table-with-multiple-entries-during-deployment&quot;&gt;Use Case - Populate a configuration table with multiple entries during deployment&lt;/h2&gt;

&lt;p&gt;One method is to write a set of single insert statements with &lt;em&gt;values&lt;/em&gt; clause. Example:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;util_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;VALUES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;My App&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;email_address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;bogus@mycompany.com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SYSDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;util_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;VALUES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;My App&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;debug_level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SYSDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course you can only run that once in any region so it makes testing your deployment difficult.
It also doesn’t handle changing your mind about a value. You could do a DELETE first and that is
a fine answer for our deployment task, but I prefer a MERGE.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;MERGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;util_config&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;My App&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;email_address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;bogus@mycompany.com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DUAL&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;UNION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ALL&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;My App&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;debug_level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DUAL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;MATCHED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;VALUES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SYSDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;MATCHED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SET&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updated_by&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;USER&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_DT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sysdate&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DECODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- compares NULLs too&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;All of that is quite messy though. I dislike having to write all that code just to deploy 
configuration table values. Sure, much of it is copy and paste after the first one, but still…
In the past I have written Perl scripts that generate that code for me, but my clients are 
often not Perl people. It is time to scratch this itch with some PL/SQL.&lt;/p&gt;

&lt;p&gt;I would like to have something where I can provide the table values in a CLOB, give it a set
of column names and have it generate and execute the MERGE like so:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_lines&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;My App,email_address,&quot;bogus@mycompany.com&quot;
My App,debug_level,0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_column_names&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app_name, param_name, param_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_merge_my_config_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;util_config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_cloumn_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We will leave aside the &lt;em&gt;created_by&lt;/em&gt;, &lt;em&gt;created_dt&lt;/em&gt;, &lt;em&gt;updated_by&lt;/em&gt;, and &lt;em&gt;updated_dt&lt;/em&gt; columns for the
moment. They are common enough that they might require special treatment. I would hope most
would have triggers to populate them, or insert/update/merge procedures for populating tables that
employ this pattern, but we can add logic for it later.&lt;/p&gt;

&lt;h2 id=&quot;parsing-a-csv-clob&quot;&gt;Parsing a CSV CLOB&lt;/h2&gt;

&lt;p&gt;There are many ways to load a CSV file into the database. You can use sqlldr or an external table. SQLcl, SqlDeveloper
and Toad all provide ways to do it; however, when we are deploying code and data via a continuous improvement process,
we typically can only use sqlplus scripts. That is the state of CI at the moment.&lt;/p&gt;

&lt;p&gt;I need to parse that clob with my CSV rows and have that data available for a multi-row MERGE or INSERT statement. 
Let’s try using &lt;em&gt;Polymorphic Table Function&lt;/em&gt; capability that was added in Oracle 18.&lt;/p&gt;

&lt;p&gt;I have a single clob input and want table data output. When you read the PTF documentation and look at the
examples it is clearly geared toward reading a table, not so much for writing to one. In my case I want
a single CLOB as input and I want multi-row table column data as output. This is backwards from the common PTF model.&lt;/p&gt;

&lt;p&gt;The closest I found was this &lt;a href=&quot;https://livesql.oracle.com/apex/livesql/file/content_F99JG73Z169WENDTTQFDQ0J09.html&quot;&gt;Dynamic CSV to Columns Converter: Polymorphic Table Function Example&lt;/a&gt;
by Chris Saxon. It assumes the input is a table of CSV rows though, not a single clob. That turns
out to be important because the PTF model is one row of output for every row of input (or a multiple
of the number of rows of input).&lt;/p&gt;

&lt;p&gt;I started off providing an input TABLE consisting of:&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;my data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_ptf_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;but quickly ran afoul of the fact that CLOB is not a first class citizen in SQL. We can change to VARCHAR2,
but then we may need more than one input row and we run into an issue figuring out the &lt;em&gt;replication_factor&lt;/em&gt; to be able 
to have more output rows than input rows. It got ugly.&lt;/p&gt;

&lt;p&gt;I finally abandonded doing anything of significance with the TABLE input parameter to the PTF. I just feed it &lt;em&gt;dual&lt;/em&gt;
as the table name and basically ignore it. All of the action happens with the other parameters to the PTF function.
You may ask “well why are you using a PTF then?” The answer is that I still need that run time described field
list for the output. Without that we have to define an object type or have a package type to match whatever we are doing.
The PTF allows us to provide any field list and types at run time.&lt;/p&gt;

&lt;h2 id=&quot;ptf-package-specification-and-example-use&quot;&gt;PTF Package Specification and Example Use&lt;/h2&gt;

&lt;p&gt;Here is the package specification. I include the PTF function inside the package even though it can be standalone.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;AUTHID&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CURRENT_USER&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;           &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- csv list&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- uses nls_date_format if null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROW&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;POLYMORPHIC&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OUT&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TABLE_T&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- csv list&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- uses nls_date_format if null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DESCRIBE_T&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- csv list&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- uses nls_date_format if null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;errors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You would call it like this:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;my_table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;id, msg, dt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MM/DD/YYYY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
23, &quot;this contains a comma (,)&quot;, 06/30/2021
47, &quot;this contains a newline (
)&quot;, 01/01/2022
73, and we can have backwacked comma (&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;), 12/25/2021
92, what about backwacked dquote &amp;gt;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;?, 12/28/2021
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and the output looks as I expect. I export the resultset in JSON format from sqldeveloper to demonstrate that
the data in the select list is SQL data types, not just strings. Note that the JSON formatter changed
the embeded newline I threw in to the ‘\n’ representation. Cool.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;results&quot; : [
    {
      &quot;columns&quot; : [
        {
          &quot;name&quot; : &quot;ID&quot;,
          &quot;type&quot; : &quot;NUMBER&quot;
        },
        {
          &quot;name&quot; : &quot;MSG&quot;,
          &quot;type&quot; : &quot;VARCHAR2&quot;
        },
        {
          &quot;name&quot; : &quot;DT&quot;,
          &quot;type&quot; : &quot;DATE&quot;
        }
      ],
      &quot;items&quot; : [
        {
          &quot;id&quot; : 23,
          &quot;msg&quot; : &quot;this contains a comma (,)&quot;,
          &quot;dt&quot; : &quot;06/30/2021&quot;
        },
        {
          &quot;id&quot; : 47,
          &quot;msg&quot; : &quot;this contains a newline (\n)&quot;,
          &quot;dt&quot; : &quot;01/01/2022&quot;
        },
        {
          &quot;id&quot; : 73,
          &quot;msg&quot; : &quot;and we can have backwacked comma (,)&quot;,
          &quot;dt&quot; : &quot;12/25/2021&quot;
        },
        {
          &quot;id&quot; : 92,
          &quot;msg&quot; : &quot;what about backwacked dquote &amp;gt;\\\&quot;&amp;lt;?&quot;,
          &quot;dt&quot; : &quot;12/28/2021&quot;
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;p_tab&quot;&gt;p_tab&lt;/h3&gt;

&lt;p&gt;We expect the table to provide a single row (and only 1 row!) and do not care what is in it. The simplest
thing to do is provide the name &lt;em&gt;dual&lt;/em&gt; for p_tab.&lt;/p&gt;

&lt;h3 id=&quot;p_table_name&quot;&gt;p_table_name&lt;/h3&gt;

&lt;p&gt;Provide the name of a table that we expect to find in &lt;em&gt;all_tab_columns&lt;/em&gt;. The
column names should match what is in &lt;em&gt;p_columns&lt;/em&gt; list (after it is UPPER cased).
We do not do anything to the table. We merely use it to find the column data
types. The expectation is that you want rows from the CSV that look like they
came from or belong in that table.&lt;/p&gt;

&lt;h3 id=&quot;p_columns&quot;&gt;p_columns&lt;/h3&gt;

&lt;p&gt;Provide a CSV separated string with a list of column names from that table.
The CSV input rows must have a matching
number of columns and be in the same order as that column list (which need not
match the table definition order). Our
output column list will be in that order by default as well.&lt;/p&gt;

&lt;h3 id=&quot;p_clob&quot;&gt;p_clob&lt;/h3&gt;

&lt;p&gt;Provide the CSV data rows as a clob. You can use a text literal. If it exceeds
32767 characters, simply concatenate another literal until you have it all.&lt;/p&gt;

&lt;h3 id=&quot;p_date_fmt&quot;&gt;p_date_fmt&lt;/h3&gt;

&lt;p&gt;If your target field list contains DATE data types, the CSV values will
need to be converted. If you are certain of the NLS_DATE_FORMAT in play
in your session and your data matches that, you can leave it NULL. You can
also alter your session to set NLS_DATE_FORMAT before calling the PTF.
I would provide a date format. There are too many times I’ve stumbled
upon something that changes the NLS_DATE_FORMAT of my session out from
under me.&lt;/p&gt;

&lt;p&gt;Note that this version does not support BLOB or INTERVAL data types.&lt;/p&gt;

&lt;h2 id=&quot;ptf-package-body&quot;&gt;PTF Package Body&lt;/h2&gt;

&lt;p&gt;The functions &lt;em&gt;split&lt;/em&gt; and &lt;em&gt;transform_perl_regexp&lt;/em&gt; are embedded here as
package private functions. I also 
publish them separately as part of a &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;library of plsql utilities&lt;/a&gt;
that I support.&lt;/p&gt;

&lt;p&gt;I wrote a &lt;a href=&quot;https://leelindley.blogspot.com/2021/12/perl-regexp-vs-oracle.html&quot;&gt;blog post&lt;/a&gt; about
&lt;em&gt;transform_perl_regexp&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BODY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;g_rows_regexp&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- defined at the end of the package&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;split&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;            &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- also unquotes \&quot; and &quot;&quot; pairs within the field to just &quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DETERMINISTIC&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OUT&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TABLE_T&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- csv list&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- uses nls_date_format if null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DESCRIBE_T&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_new_cols&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns_new_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_col_names&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_col_order&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_col_order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_col_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; 
        &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;RAISE_APPLICATION_ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Input table to csv_to_table_pkg.t should be table DUAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pass_through&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_read&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;v_col_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UPPER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- we need a hash to get from the column name to the index for both input csv order and output field order&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_col_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_value&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_type&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TIMESTAMP%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TIMESTAMP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_type&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data_type&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OUTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_tab_columns&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UPPER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_value&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_type&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;RAISE_APPLICATION_ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;table: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; does not have a column named &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;BLOB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_type&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;INTERVAL%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;RAISE_APPLICATION_ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;table: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; column &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; is unsupported data type &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- we create these in any order, but they must be in the right location in the array&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_new_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_metadata_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                        &lt;span class=&quot;k&quot;&gt;name&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;
                                        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data_type&lt;/span&gt;
                                                    &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TIMESTAMP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;        &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_timestamp&lt;/span&gt;
                                                    &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;BINARY_DOUBLE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_binary_double&lt;/span&gt;
                                                    &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;BINARY_FLOAT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;     &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_binary_float&lt;/span&gt;
                                                    &lt;span class=&quot;c1&quot;&gt;--WHEN 'BLOB'             THEN DBMS_TF.type_blob&lt;/span&gt;
                                                    &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;             &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_char&lt;/span&gt;
                                                    &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;             &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_clob&lt;/span&gt;
                                                    &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;             &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_date&lt;/span&gt;
                                                    &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;NUMBER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;           &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_number&lt;/span&gt;
                                                    &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;                         &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_varchar2&lt;/span&gt;
                                                   &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;-- we have 1 input row and MANY output rows, so replication is true&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_columns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_new_cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row_replication&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- csv list&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- uses nls_date_format if null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;               &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- put_columns.count&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_rowset&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_set_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_in_row_count&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_set_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_col_out_cnt&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_row&lt;/span&gt;               &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_row_cnt&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;-- input row numbers including blank lines that can be skipped&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g_row_num&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- the number of columns in our output rows should match number of csv fields&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_col_out_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;-- in case FETCH is called more than once (unlikely)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- get does not change value if not found in store so starts with our default 0&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--DBMS_TF.xstore_get('g_row_num', g_row_num); &lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_row_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rowset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_in_row_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_in_row_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;RAISE_APPLICATION_ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;input table should only have 1 placeholder row. Use DUAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- we need to use count because our regexp will match an empty row. We will skip&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- the empty row but we need to line number to help with debug error message&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_row_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_rows_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- one extra matches on $&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('got '||v_row_cnt||' rows from clob');&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- loop over the rows split from the input string&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_row_cnt&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;g_row_num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_row_num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- pull a line out of the text input (sans newline)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_rows_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('row '||g_row_num||' : '||v_row);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- split the row into csv fields stripping dquotes and unquoting chars inside dquotes&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- just skip empty rows now that we captured the rownumber&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('row '||g_row_num||' had 0 csv columns');&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;CONTINUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_out_cnt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;RAISE_APPLICATION_ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;row &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_row_num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; has cnt=&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; csv fields, but we need &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_out_cnt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; columns
ROW: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--dbms_output.put_line('row '||g_row_num||' had '||v_col_strings.COUNT||' csv columns');&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- populate the output rowset column tables for this row&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_out_cnt&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_timestamp&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;-- better set nls value yourself because we just shoving the string in with default conversion&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;-- likely not to ever be used&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_binary_double&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_binary_double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
				&lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_binary_float&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_binary_float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
				&lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_char&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
				&lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_clob&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
				&lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_date&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- default to nls_date_fmt&lt;/span&gt;
                    &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TO_DATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_date_fmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_output_col_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_number&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- in describe we made sure the only thing left is varchar2&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tab_varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_col_strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- end loop on columns&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- end loop on newline separated rows in clob&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;--DBMS_TF.xstore_set('g_row_num', g_row_num);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- we got a single row of input, but are now writing v_rows_out records output.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- The only way to do that is with the funky replication_factor. It was not designed&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- for this, but it works.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_TF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_row_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_rowset_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;replication_factor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_rows_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_re&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;DETERMINISTIC&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/*
            strip comment blocks that start with at least one blank, then
            '--' or '#', then everything to end of line or string
        */&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;c_strip_comments_regexp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTANT&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;[[:blank:]](--|#).*($|
    )&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;-- note that \n and \t will be replaced if not preceded by a \&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;-- \\n and \\t will not be replaced. Unfortunately, neither will \\\n or \\\t.&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;-- We are not parsing into tokens, so this is as close as we can get cheaply&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; 
        &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
              &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_strip_comments_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- strip comments&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;-- strip spaces and newlines too like 'x' modifier&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
              &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(^|[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;-- replace \t with tab character value so it works like in perl&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(^|[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;-- replace \n with newline character value so it works like in perl&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(^|[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;-- replace \r with CR character value so it works like in perl&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
      &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;


  &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;split&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;            &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- also unquotes \&quot; and &quot;&quot; pairs within the field to just &quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DETERMINISTIC&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- when p_s IS NULL, returns initialized collection with COUNT=0&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/*

Treat input string p_s as following the Comma Separated Values (csv) format 
(not delimited, but separated) and break it into an array of strings (fields) 
returned to the caller. This is overkill for the most common case
of simple separated strings that do not contain the separator char and are 
not quoted, but if they are double quoted fields, this will handle them 
appropriately including the quoting of &quot; within the field.

We comply with RFC4180 on CSV format (for what it is worth) while also 
handling the mentioned common variants like backwacked quotes and 
backwacked separators in non-double quoted fields that Excel produces.

See https://www.loc.gov/preservation/digital/formats/fdd/fdd000323.shtml

*/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/*
MIT License

Copyright (c) 2021 Lee Lindley

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;-- individual parsed values cannot exceed 4000 chars&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_occurence&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_cnt&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_varchar2_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;-- we are going to match multiple times. After each match the position &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- will be after the last separator.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*                         -- optional whitespace before anything, or after
                            -- last delim
                            --
   (                        -- begin capture of &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; which is what we will return.
                            -- It can be NULL!
                            --
       &quot;                        -- one double quote char binding start of the match
           (                        -- just grouping
 --
 -- order of these next things matters. Look for longest one first
 --
               &quot;&quot;                       -- literal &quot;&quot; which is a quoted quote 
                                        -- within dquote string
               |                        
               &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;                      -- Then how about a backwacked double
                                        -- quote???
               | 
               [^&quot;]                     -- char that is not a closing quote
           )*                       -- 0 or more of those chars greedy for
                                    -- field between quotes
                                    --
       &quot;                        -- now the closing dquote 
       |                        -- if not a double quoted string, try plain one
 --
 -- if the capture is not going to be null or a &quot;*&quot; string, then must start 
 -- with a char that is not a separator or a &quot;
 --
       [^&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;]    -- so one non-sep, non-&quot; character to bind 
                                -- start of match
                                --
           (                        -- just grouping
 --
 -- order of these things matters. Look for longest one first
 --
               &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;      -- look for a backwacked separator
               |                        
               [^&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;]     -- a char that is not a separator
           )*                       -- 0 or more of these non-sep, non backwack
                                    -- sep chars after one starting (bound) a 
                                    -- char 1 that is neither sep nor &quot;
                                    --
   )?                       -- end capture of our field &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, and we want 0 or 1
                            -- of them because we can have ,,
 --
 -- Since we allowed zero matches in the above, regexp_subst can return null
 -- or just spaces in the referenced grouped string &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
 --
   (&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;|$)    -- now we must find a literal separator or be at 
                            -- end of string. This separator is included and 
                            -- consumed at the end of our match, but we do not
                            -- include it in what we return
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--v_log app_log_udt := app_log_udt('TEST');&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- will be empty&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--v_log.log_p(TO_CHAR(REGEXP_COUNT(p_s,v_regexp)));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- since our matched group may be a null string that regexp_substr &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- returns before we are done, we cannot rely on the condition that &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- regexp_substr returns null to know we are done. &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- That is the traditional way to loop using regexp_subst, but that &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- will not work for us. So, we first have to find out how many fields&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- we have including null captures and run regexp_substr that many times&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- A &quot;delimited&quot; string, as opposed to a separated string, will end in&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- a delimiter char. In other words there is always one &quot;delimiter&quot;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- after every field. But the most common case of CSV is a &quot;separator&quot;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- style which does not have separator at the end, and if we actually&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- have a separator at the end of the string, it is because the last&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- field value was NULL!!!! In that scenario with the trailing separator&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- we want to count that NULL and include it in our array.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- In the case where the last char is not a &quot;separator&quot; char, &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- the regexp will match one last time on the zero-width $. That is an&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- oddity of how it is constructed.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- For our purposes of expecting a separated string, not delimited,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- we need to reduce the count by 1 for the case where the last&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- character is NOT a separator. &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- I do not know what to say. I was very dissapointed I could not handle&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- all the logic in the regexp, but Oracle regexp are just not as&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- powerful as the ones in Perl which have negative/positive lookahead&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- and lookbehinds plus the concept of &quot;POS()&quot; so you can match against&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- the start of the substr like ^ does for the whole thing. Without&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- those features, this was very difficult. It is also possible I am&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- missing something important and a regexp expert could do it more&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- simply. I would not mind being corrected and shown a better way.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- if last char of string is not the separator&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_occurence&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cnt&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;p_s&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;-- the string we are parsing&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;-- the regexp we built using the chosen separator (like ',')&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;-- starting at the beginning of the string on the first call&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_occurence&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;-- but on subsequent calls we will get 2nd, then 3rd, etc, match of the pattern&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;''&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;-- no regexp modifiers&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;-- we want the \1 grouping match returned, not the entire expression&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--v_log.log_p(TO_CHAR(v_occurence)||' x'||v_str||'x');&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;-- cannot use this test for NULL like the man page for regexp_substr&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- shows because our grouped match can be null as a valid value&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- while still parsing the string.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;--EXIT WHEN v_str IS NULL;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TRIM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                               &lt;span class=&quot;c1&quot;&gt;-- if it is a double quoted string, can still have leading/trailing spaces in the value&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_keep_nulls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;-- otherwise it was an empty string which we discard.&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- we WILL add this to the array&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                    &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ELSIF&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;-- it IS a double quoted string&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_strip_dquote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;-- get rid of starting and ending &quot; char&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;-- replace any \&quot; or &quot;&quot; pairs with single &quot;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                    &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^&quot;|&quot;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;-- leading &quot; or ending &quot;&lt;/span&gt;
                                    &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;|[&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;-- or one of chars &quot; or \&lt;/span&gt;
                                        &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(&quot;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;-- that is followed by a &quot; and we capture that one in \1&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;-- We put any '&quot;' we captured back without the backwack or &quot; quote&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;-- start at position 1 in v_str&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;-- 0 occurence means replace all of these we find&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
                    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; 
                        &lt;span class=&quot;c1&quot;&gt;-- not a double quoted string so unbackwack separators inside it. Excel format&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- end if double quoted string&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- Note that if it was an empty double quoted string we are still putting it into the array.&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- So, you can still get nulls in the case they are given to you as &quot;&quot; and we stripped the dquotes,&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- even if you asked to not keep nulls. Cause an empty string is not NULL. Uggh.&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXTEND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- this will raise an error if the value is more than 400 chars&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- end not an empty string or we want to include NULL&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;split&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- start package initialization block&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;g_rows_regexp&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
(                               # capture in &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
  (                             # going to group 0 or more of these things
    [^&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;]+                   # any number of chars that are not dquote, backwack or newline
    |
    (                           # just grouping for repeat
        &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;                   # or a backwacked &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; but put space between them so gets transformed correctly
    )+                          # one or more protected newlines (as if they were in dquoted string)
    |
    (                           # just grouping for repeat
        &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;                     # or a backwacked &quot;
    )+                          # one or more protected &quot;
    |
    &quot;                           # double quoted string start
        (                       # just grouping. Order of the next set of things matters. Longest first
            &quot;&quot;                  # literal &quot;&quot; which is a quoted dquoute within dquote string
            |
            &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;                 # a backwacked dquote 
            |
            [^&quot;]                # any single character not the above two multi-char constructs, or a dquote
                                #     Important! This can be embedded newlines too!
        )*                      # zero or more of those chars or constructs 
    &quot;                           # closing dquote
    |                           
    &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;                          # or a backwack, but do this last as it is the smallest and we do not want
                                #   to consume the backwack before a newline or a dquote
  )*                            # zero or more strings on a single &quot;line&quot; that could include newline in dquotes
                                # or even a backwacked newline
)                               # end capture &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
(                               # just grouping 
    $|&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;                        # require match newline or string end 
)                               # close grouping
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;errors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-the-ptf-for-data-deploy&quot;&gt;Using the PTF for Data Deploy&lt;/h2&gt;

&lt;p&gt;Here is how I could deploy the rows in my example at the start of this blog post:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;MERGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;util_config&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv_to_table_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_tab&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_table_name&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;util_config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columns&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app_name, param_name, param_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_clob&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
My App, email_address, bogus@mycompany.com
My App, debug_level, 0
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;MATCHED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;VALUES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SYSDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;MATCHED&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SET&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updated_by&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;USER&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_DT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sysdate&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DECODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- compares NULLs too&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Our use case was to simplify deploy of DATA to configuration tables. We certainly seemed to have
done a crapton of hard work to make this &lt;em&gt;easier&lt;/em&gt;. I do not know whether the solution is worth
it or not.&lt;/p&gt;

&lt;p&gt;I will likely add this package to &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;plsql utilities&lt;/a&gt;,
but have not done so yet. I need to exercise it a bit more to see if anything shakes out. I’m
also still uncomfortable with this pattern of abusing the replication factor for PTF. It does not
really fit the PTF design. I might break this into two pieces, one of which parses the clob
into lines and the other which uses a PTF to parse the CSV lines as was done by Mr. Saxon in
his example.&lt;/p&gt;

&lt;p&gt;If you are exploring &lt;em&gt;Polymorphic Table Functions&lt;/em&gt;, I hope you might find this exercise helpful.
We certainly need more examples of them to help learn about it. This was not a &lt;em&gt;gimme&lt;/em&gt;.&lt;/p&gt;

</description>
        <pubDate>Mon, 27 Dec 2021 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2021/12/27/PTF-Hard-Coding-Data.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2021/12/27/PTF-Hard-Coding-Data.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Perl REgexp vs Oracle</title>
        <description>&lt;h1 id=&quot;writing-regular-expressions-for-oracle-as-if-you-were-in-a-perl-script&quot;&gt;Writing Regular Expressions for Oracle As If You Were in a Perl Script&lt;/h1&gt;

&lt;p&gt;I am accustomed to writing regular expressions for Perl. It has gotten to the point
where I’m super annoyed when I write regular expressions for Oracle because of both how strings
are handled and lack of comment support in the RE itself. I prefer building the
regular expression in small pieces with assembler style comments on the right side to remind
myself what it does. I also favor using ‘\n’ and ‘\t’ to represent newline and tab rather than
having to hardcode these values in the regexp string. Having a newline in the middle
of an Oracle regular expression is particularly disconcerting.&lt;/p&gt;

&lt;p&gt;Likewise building the RE by concatentating quoted string pieces and having regular SQL comments
out on the right is less than satisfying. It gets ugly with all of the “||’ whatever’”, especially
with embedded newlines or even concatenating a constant that contains a newline.
These may seem trivial concerns, but I’m not changing.&lt;/p&gt;

&lt;p&gt;The following Function transforms a regular expression in a manner similar to the way
Perl would treat the ‘x’ modifier stripping comments and whitespace, plus allowing
us to use ‘\n’, ‘\r’ and ‘\t’ as character representations that are not stripped as whitespace.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_re&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/*
            strip comment blocks that start with at least one blank, then
            '--' or '#', then everything to end of line or string
        */&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;c_strip_comments_regexp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTANT&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;[[:blank:]](--|#).*($|
)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- note that \n, \r and \t will be replaced if not preceded by a \&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- \\n and \\t will not be replaced. Unfortunately, neither will \\\n or \\\t.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- If you need \\\n, use \\ \n since the space will be removed.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- We are not parsing into tokens, so this is as close as we can get cheaply&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; 
        &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
              &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c_strip_comments_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- strip comments&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;-- strip spaces and newlines too like 'x' modifier&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
              &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(^|[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;-- replace \t with tab character value so it works like in perl&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(^|[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;-- replace \n with newline character value so it works like in perl&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;(^|[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;-- replace \r with CR character value so it works like in perl&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 

      &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The function &lt;em&gt;transform_perl_regexp&lt;/em&gt; is distributed in 
a &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#transform_perl_regexp&quot;&gt;utility libray I support&lt;/a&gt;. 
You can use it without attribution.&lt;/p&gt;

&lt;p&gt;Below is an example of writing a somewhat convoluted regular expression to parse a multi-line CLOB
that contains CSV “records” that may contain embedded newlines with quoting as described 
in &lt;a href=&quot;https://www.loc.gov/preservation/digital/formats/fdd/fdd000323.shtml&quot;&gt;RFC4180&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have more about that in the
&lt;em&gt;plsql_utilities&lt;/em&gt; library if it interests you, but the main point is to show the regular expression
presentation in the Perl style and how it is transformed.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_lines&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;------------------------&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;23, &quot;this contains a comma (,)&quot;, 06/30/2021
47, &quot;this contains a newline (
)&quot;, 01/01/2022

73, and we can have backwacked comma (&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;), 12/25/2021
92, what about backwacked dquote &amp;gt;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;?, 12/28/2021&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;------------------------&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;-- beware this is using Perl-like regexp so we have to apply a transform&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
(                               # capture in &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
  (                             # going to group 0 or more of these things
    [^&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;]+                   # any number of chars that are not dquote, backwack or newline
    |
    (                           # just grouping for repeat
        &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;                   # or a backwacked &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; but put space between them so gets transformed correctly
    )+                          # one or more protected newlines (as if they were in dquoted string)
    |
    (                           # just grouping for repeat
        &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;                     # or a backwacked &quot;
    )+                          # one or more protected &quot;
    |
    &quot;                           # double quoted string start
        (                       # just grouping. Order of the next set of things matters. Longest first
            &quot;&quot;                  # literal &quot;&quot; which is a quoted dquoute within dquote string
            |
            &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;                 # a backwacked dquote 
            |
            [^&quot;]                # any single character not the above two multi-char constructs, or a dquote
                                #     Important! This can be embedded newlines too!
        )*                      # zero or more of those chars or constructs 
    &quot;                           # closing dquote
    |                           
    &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;                          # or a backwack, but do this last as it is the smallest and we do not want
                                #   to consume the backwack before a newline or a dquote
  )*                            # zero or more strings on a single &quot;line&quot; that could include newline in dquotes
                                # or even a backwacked newline
)                               # end capture &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;
(                               # just grouping 
    $|&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;                        # require match newline or string end 
)                               # close grouping
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_line&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_cnt&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform_perl_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;preprocessed regexp: &amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;-- get number of times regexp will match less match on $&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_cnt&lt;/span&gt;                            &lt;span class=&quot;c1&quot;&gt;-- for each time we know it will match including NULL match values&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REGEXP_SUBSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dbms_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;line &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; is &amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;preprocessed regexp: &amp;gt;(([^&quot;
\\]+|(\\\n)+|(\\&quot;)+|&quot;(&quot;&quot;|\\&quot;|[^&quot;])*&quot;|\\)*)($|
)&amp;lt;
line 1 is &amp;gt;23, &quot;this contains a comma (,)&quot;, 06/30/2021&amp;lt;
line 2 is &amp;gt;47, &quot;this contains a newline (
)&quot;, 01/01/2022&amp;lt;
line 3 is &amp;gt;&amp;lt;
line 4 is &amp;gt;73, and we can have backwacked comma (\,), 12/25/2021&amp;lt;
line 5 is &amp;gt;92, what about backwacked dquote &amp;gt;\&quot;&amp;lt;?, 12/28/2021&amp;lt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;transform_perl_regexp&lt;/em&gt; is NOT a sophisticated parser. 
If you want to have the strings ‘ –’ (space dash dash)
or ‘ #’ (space hash) appear in your regular expression, you will have to get creative or not use it.
(HINT: you can put one of the characters in character class brackets).
Likewise when looking for the ‘\n’ and friends, we are not actually
parsing anything, so it gets messy. I’ve protected ‘\\n’, but not dealt with ‘\\\n’. These are
easier though because you can separate ‘\’ from ‘\n’ with a space that is going to be stripped.
It works the way I expect most of the time.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Regular expressions, even those you wrote last week, take signficant effort for your brain to parse.
It is worthwhile to have them well documented in the code. A way to do that in Perl has been established
and is used by many serious practitioners. I much prefer it and can now use it in Oracle SQL and PL/SQL
programs.&lt;/p&gt;
</description>
        <pubDate>Sun, 26 Dec 2021 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/plsql/2021/12/26/Perl-Regexp-vs-Oracle.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/plsql/2021/12/26/Perl-Regexp-vs-Oracle.html</guid>
        
        <category>oracle</category>
        
        <category>plsql</category>
        
        
        <category>oracle</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Extracting BLOB from Oracle with Sqlplus</title>
        <description>&lt;h1 id=&quot;how-do-we-get-a-blob-from-the-database-to-a-client-file&quot;&gt;How do we get a BLOB from the database to a client file?&lt;/h1&gt;

&lt;p&gt;I wrote about using a WITH clause function for various purposes
&lt;a href=&quot;https://lee-lindley.github.io/oracle/sql/plsql/2021/09/25/Inline-PLSQL-Methods.html&quot;&gt;Why do I need Inline PL/SQL Methods?&lt;/a&gt;, 
one example of which was
extracting a BLOB that contained an XLSX file.
In that article I blithely waved my hands and said use Toad or SqlDeveloper to then extract the output into a file.&lt;/p&gt;

&lt;p&gt;What do we do if we want to script this? There are options. 
You can send email from inside
the database and attach the blob (see &lt;a href=&quot;https://github.com/lee-lindley/html_email&quot;&gt;html_email&lt;/a&gt;), but if your goal is to
automatically deliver it to a shared drive or other file destination, email is not a good option.&lt;/p&gt;

&lt;p&gt;You can use pretty much any language that supports
a robust Oracle database interface to do it from a client ETL server. Pro-C, Java, Perl DBI::DBD::Oracle, 
and I presume Python all
have the capability to extract a BLOB from the database and write to the local filesystem. I also assume
the big commercial ETL packages like Ab-initio and Informatica will do it, but I haven’t looked.&lt;/p&gt;

&lt;p&gt;Maybe you do not have access to or expertise in these tools.  The Oracle client library has SQLcl, which can be
scripted to download a BLOB as mentioned in this article 
&lt;a href=&quot;https://www.thatjeffsmith.com/archive/2020/07/using-sqlcl-to-write-out-blobs-to-files-in-20-lines-of-js/&quot;&gt;Using SQLcl to write out BLOBs to files in 20 lines of js&lt;/a&gt;. But it turns out that you don’t always have sqlcl or even Java
installed on an ETL server. I know that seems odd, but I’ve seen it enough I’m no longer surprised.&lt;/p&gt;

&lt;p&gt;I wanted to do it with &lt;em&gt;sqlplus&lt;/em&gt; because it is almost universally available at every client and on every ETL
server. Unfortunately &lt;em&gt;sqlplus&lt;/em&gt; does not support BLOB data type as a local variable for binding. Drat. When 
I searched I hit the article &lt;a href=&quot;https://ogobrecht.com/posts/2020-01-01-download-blobs-with-sqlplus/&quot;&gt;Download BLOBs with SQLPlus&lt;/a&gt;
by Ottmar Gobrect. He spreads plenty of credit around for the idea. The crux of the solution is to convert
from BLOB, which &lt;em&gt;sqlplus&lt;/em&gt; does not support, to CLOB which it does. How, you ask? The same way we do it when we attach
a binary file to an email. We base64 encode it. Clever!&lt;/p&gt;

&lt;h1 id=&quot;extracting-a-blob-with-sqlplus&quot;&gt;Extracting a BLOB with Sqlplus&lt;/h1&gt;

&lt;p&gt;Reusing my spreadsheet example from the article mentioned at the top, and a base64encode function
published by Tim Hall we have a sqlplus script that generates and extracts a BLOB
to a file as base64 encoded text.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;-- 16mb should be plenty for most spreadsheets.&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16777216&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;longchunksize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heading&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;verify&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt; 
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feedback&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt; 
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimout&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt; 
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimspool&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt; 
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pagesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; 
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;whenever&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlerror&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;failure&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- sqlplus supports clob variables but not blob&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vo_clob&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;clob&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SYS_REFCURSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base64encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_blob&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- -----------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- File Name    : https://oracle-base.com/dba/miscellaneous/base64encode.sql&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- Author       : Tim Hall&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- Description  : Encodes a BLOB into a Base64 CLOB.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- Last Modified: 09/11/2011&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- -----------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l_clob&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l_step&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PLS_INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- make sure you set a multiple of 3 not higher than 24573&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TRUNC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getlength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;l_clob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_clob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_RAW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cast_to_varchar2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTL_ENCODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base64_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;substr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_clob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_xlsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_src&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SYS_REFCURSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CLOB&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctxHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addSheetFromCursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employee Salaries&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_sheetIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- freeze the top row with the column headers&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_frozen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- style with alternating colors on each row. &lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setTableFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TableStyleLight11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- single column format on the salary column. The ID column keeps default format&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setColumnFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columnId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;-- the salary column&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_format&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;$#,##0.00&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getFileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;closeContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base64encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_bilbo&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;departments&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;UNION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ALL&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;999&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Baggins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Bilbo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Sales&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;123.45&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_bilbo&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- assign the uuencoded clob data to the sqlplus bind variable&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vo_clob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_xlsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- spool out the encoded clob&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;termout&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x.xlsx.base64&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vo_clob&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;termout&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--  base64 -d -i x.xlsx.base64 &amp;gt;x.xlsx &amp;amp;&amp;amp; rm x.xlsx.base64&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- OR on windows&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- certutil -decode x.xlsx.base64 x.xlsx&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- del x.xlsx.base64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On my windows machine I can run the following command in cygwin. I assume it works fine in Linux.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;base64 -d -i spool_file_name &amp;gt; spreadsheet_name.xlsx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you fail to included the -i option, it may complain about invalid input. Turns out my cygwin
version was not forgiving of the carriage return characters (\015) sqlplus put into the file. It
was fine with the linefeeds (\012). The -i option makes it ignore them. It may not be necessary
on a true Unix machine or if there is an option to turn off carriage returns in sqlplus on Windows.
I didn’t try to find out.&lt;/p&gt;

&lt;p&gt;On windows the following command works just fine.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;certutil -decode spool_file_name spreadsheet_name.xlsx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In my small example the encoding added around 25% to the file size. I’m not claiming that is the
amount you will get, but it should be in that ballpark. It isn’t enough to break the bank.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;If you are looking to get a BLOB out of Oracle and you don’t have access to a better scripting
language or tool, knowing you can at least do it with sqlplus is a nice option to have in your
back pocket.&lt;/p&gt;
</description>
        <pubDate>Sat, 18 Dec 2021 10:30:00 -0500</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2021/12/18/sqlplus-blob.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2021/12/18/sqlplus-blob.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>blob</category>
        
        <category>sqlplus</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Sql Tuning for Multiple Use Cases</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;The primary goal of tuning an Oracle SQL statement is to obtain the best performance 
(shortest response time and minimal resourse utilization)
for the intended task.  The best answer usually achieves both,
but occasionally you sacrifice some of one for the other.&lt;/p&gt;

&lt;p&gt;The Oracle optimizer is good at achieving an optimal execution plan (&lt;em&gt;access path, join order, join mechanism&lt;/em&gt;),
defined as minimal &lt;em&gt;cost&lt;/em&gt;, if it has sufficient statistics about the data.
There are reams of books and articles about it. I bashed my
head against the rocks of Jonathan Lewis’s book &lt;em&gt;Cost Based Oracle Fundamentals&lt;/em&gt; for several years. The subject is
so massive and fluid, I do not even pretend to get it all. This article is not about that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem to be solved is that we have a single SQL statement with multiple use cases, each of which require a
different execution plan.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I will present two use cases for this article.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A nightly batch that touches a full set of data&lt;/li&gt;
  &lt;li&gt;An incremental, near real-time batch that touches a very small subset of the data&lt;/li&gt;
&lt;/ul&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;Multiple Use Case SQL Statements&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/use_case_sql_tuning.gif&quot; alt=&quot;use_case_sql_tuning.gif&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;(In practice I have third and forth use cases of nightly “correction” batches that touch 
what can be either middling sized sets of the data or really huge, almost full sized subsets.)&lt;/p&gt;

&lt;p&gt;How can this be addressed?&lt;/p&gt;

&lt;h1 id=&quot;adaptive-plan&quot;&gt;Adaptive Plan&lt;/h1&gt;

&lt;p&gt;Modern Oracle databases have &lt;em&gt;dynamic&lt;/em&gt; execution plans where the database attempts
to re-plan after finding that the statistics used to calculate the original plan do not match reality.
That is a good idea, and I am sure it is helpful in situations where the data changes slowly over time. For example
at the start of the day one of the source tables may be tiny and a Full Table Scan (FTS) access path is chosen.
As the table fills during the day, the &lt;em&gt;adaptive plan&lt;/em&gt; logic may determine an Index Access Path is lower
cost and switch to it. Adaptive plans may be a great idea and it may solve many issues in many environments,but I have
not personally experienced that. Your mileage may vary.&lt;/p&gt;

&lt;p&gt;The adaptive plan mechanism does not help sufficiently with this use case. 
Imagine that we start with the nightly batch case where the
driver table statistics would tip off the optimizer to use FTS and Hash join plans. Works great for the nightly batch
which runs for thirty minutes (or hours).
Now we begin the daily mini-batches. We re-gather statistics on our driver table, but Oracle does not bother
to re-evaluate our execution plan. Nope, the idiot savant has a perfectly good plan already, and it is going to use it. It does
full table scans and hash joins to obtain what we need for our tiny subset of accounts,
wasting resources and taking forever on the first &lt;em&gt;near real-time&lt;/em&gt; run.
Probably on the second one too. By some later run (perhaps hours later) the adaptive plan technology may figure
out something isn’t right and substitute a better plan. By now though we are fielding calls from the business that
stuff isn’t working right. Booooooooo!&lt;/p&gt;

&lt;p&gt;Even worse would be the behavior that night. Oracle has a good plan now for the small driver table footprint but when
we load it up for the nightly batch, Oracle continues to use an unscalable execution plan with index access that runs
all night. Uggghhhh!&lt;/p&gt;

&lt;h1 id=&quot;invalidate-the-plan&quot;&gt;Invalidate the Plan&lt;/h1&gt;

&lt;p&gt;We could do something to invalidate the plan forcing Oracle to evaluate the SQL again with the changed statistics.&lt;/p&gt;

&lt;h2 id=&quot;dbms_shared_pool&quot;&gt;DBMS_SHARED_POOL&lt;/h2&gt;

&lt;p&gt;Although there is a package named DBMS_SHARED_POOL that provides fine grained control over invalidating
individual sql plans, I have yet to meet a DBA willing to grant execute on the package and I do not blame
them one bit. You would also have to do so on every instance of a RAC.&lt;/p&gt;

&lt;h2 id=&quot;dbms_stats&quot;&gt;DBMS_STATS&lt;/h2&gt;

&lt;p&gt;There is also a parameter you can set during statistics gathering that is supposed to invalidate any SQL
plan using the table (no_invalidate =&amp;gt; FALSE), but it comes with caveats and is generally ignored for at least a while.
(DMS_STATS.AUTO_INVALIDATE default value is to let Oracle decide and it does not invalidate the plans immediately.)&lt;/p&gt;

&lt;h2 id=&quot;ddl-invalidates-plans&quot;&gt;DDL Invalidates Plans&lt;/h2&gt;

&lt;p&gt;At least for Oracle versions 10 through 19, updating a comment on a table counted as DDL that marks all SQL that
used the table as needing to be parsed again. (You do have to make the comment different than the existing one though
or it is a noop.) Observe:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_log_app&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sql_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;parse_calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sql_text&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v$sql&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sql_text&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;%app_log_app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;ow&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sql_text&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;%v$sql%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;SQL_ID&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;PARSE_CALLS&lt;/th&gt;
      &lt;th&gt;SQL_TEXT&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;4t32sfzvq63zf&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1&lt;/td&gt;
      &lt;td&gt;select * from app_log_app&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Notcie the number of parse calls is &lt;em&gt;one&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;COMMENT ON TABLE app_log_app IS 'invalidate plan comment &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SYSDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MM/DD/YYYY HH24:MI:SS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;||q'{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- repeat SQL listed above to query the table then v$sql.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;SQL_ID&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;PARSE_CALLS&lt;/th&gt;
      &lt;th&gt;SQL_TEXT&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;4t32sfzvq63zf&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;2&lt;/td&gt;
      &lt;td&gt;select * from app_log_app&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;DDL, even a simple COMMENT, invalidates all plans that reference the object, forcing a re-parse. So we could do this
once during the nightly batch after gathering statistics on our driver table, and again on the first run of the
mini-batch in the morning. It is a bit fragile though as a solution, and there is a downside that there could
be large numbers of SQLs that need to be re-parsed if the table you chose was a common one. The scenario I 
describe it would be a driver table specific to our process, but one should be aware of the possible impact. Nevertheless,
this is a viable answer for the multiple Use Case issue assuming we keep gathering statistics 
on the driver table when needed.&lt;/p&gt;

&lt;h1 id=&quot;multiple-sql-statements&quot;&gt;Multiple Sql Statements&lt;/h1&gt;

&lt;p&gt;So what if we just use two different SQL statements in our code so that we get the right plan for each case?&lt;/p&gt;

&lt;p&gt;My response, having gone down that road, is “what if they come up with different answers?”. This is particularly
egregious if you wind up only testing one path. Worse yet when you go to make changes, you have to make it in
two places.&lt;/p&gt;

&lt;p&gt;It may not be possible to completely avoid this though. If you decide to use a pattern that sometimes uses Update/Delete
of records in place, and sometimes does a Create Table AS (CTAS) followed by partition exchange, then of course
you will be executing different SQL for the different use cases.&lt;/p&gt;

&lt;p&gt;Yet whenever possible we should strive to have a single set of code that implements the business logic. This implies
we will likely be using Dynamic SQL for the task. Even so, when building the dynamic SQL it would be best if we could avoid
having more than one set of code that implements the same &lt;em&gt;logic&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;“Sure” you say, you get that. But there will come a time when you do it anyway for expediency. I’ve done that, and have
the scars to prove it. If you are like me, you will likely have to learn the hard way once or twice before it sticks.
Maybe this article will help you avoid as many scars as this slow learner has accumulated.&lt;/p&gt;

&lt;p&gt;It turns out that using two different SQL statements is the answer I’m proposing as far as the database is
concerned, just using a common base.&lt;/p&gt;

&lt;h1 id=&quot;use-case-specific-hints&quot;&gt;Use Case Specific Hints&lt;/h1&gt;

&lt;p&gt;Many purists turn up their noses at hints, and lecture you to just make sure your statistics are correct
so that the optimizer can determine the best answer.&lt;/p&gt;

&lt;p&gt;I can happily engage in a religious war dialogue on the subject if you are one who wishes to be pendantic on either side.
I can argue both sides fairly well. I agree that if you can solve the issue by controlling the statistics on the tables
and indexes, then by all means do so. In particular this means making rational decisions about histograms
and partition level statistics. Mastering that can address many issues with both current and future SQL.&lt;/p&gt;

&lt;p&gt;People who argue against hints often have their opinion informed by too often seeing inappropriate hints used
by well meaning developers or business users. I get that too. I’m even guilty of it and I’m sure you are too at
least at times over your career.&lt;/p&gt;

&lt;p&gt;I can also make the case that it is not possible to solve all problems without hints. The case of a piplelined TABLE(f()) function
in your query means the optimizer has no clue how many records will come from it. You can give it a &lt;em&gt;cardinality&lt;/em&gt; hint,
but what if the number of records is variable with the use case?&lt;/p&gt;

&lt;p&gt;We also just demonstrated that gathering statistics again on a table does not invalidate existing plans.&lt;/p&gt;

&lt;p&gt;There are other scenarios where you as the data expert simply have more information than the optimizer will
determine from statistics, regardless of how carefully you gather or even customize the statistics it uses. That
rabbit hole is deep.&lt;/p&gt;

&lt;p&gt;Let’s embrace the suck and use a minimal set of hints that will guide the optimizer into understanding our
use case. The term “minimal” leaves some wiggle room. For the scenario I described we might get by with
as little as a single &lt;em&gt;CARDINALITY&lt;/em&gt; hint on the driver table plus a degree of parallel hint for the entire query. 
Example:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'!&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;INSERT INTO ...
        SELECT /*+ CARDINALITY(d __DRIVER_TABLE_CARDINALITY__) __PARALLEL__ */
            ...
        FROM my_driver_table d
        INNER JOIN ...
    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;__DRIVER_TABLE_CARDINALITY__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_is_nightly_batch&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;__PARALLEL__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_is_nightly_batch&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;PARALLEL(16)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;NO_PARALLEL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we call our procedure the parameter p_is_nightly_batch will be TRUE or FALSE. Now the numbers 10 and a million
are not exactly what we have in our driver table, and in fact we could get a mini-batch where the number
of records is enough that we would be better off with a different plan than that we get from telling
the optimizer to assume 10 records. That is something you might have to experiment with.&lt;/p&gt;

&lt;p&gt;Note that this winds up being two different SQL statements in v$sql, each with a different plan.&lt;/p&gt;

&lt;p&gt;Putting the parallel degree into the hint is somewhat controversial. One can say it should be determined
from the DOP on the table. There are other considerations depending on system load. That is a topic for another
day.&lt;/p&gt;

&lt;p&gt;In practice I would likely put those values, 10 and 100000 plus the DOP, into a parameter table that I could adjust at run time.
(See &lt;em&gt;app_parameter&lt;/em&gt; in &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities&quot;&gt;plsql_utilities&lt;/a&gt;.)  That way if something
happened either over time or during a particular event, I could adjust the values fed to the optimizer on the fly,
and since the text of the SQL is different from that already parsed, it of course parses again with the new hint value.&lt;/p&gt;

&lt;p&gt;This may not be enough though. There are cases where the optimizer simply refuses to come up with 
a correct plan without hints, and you must bludgeon it into submission.
I offer &lt;a href=&quot;https://jonathanlewis.wordpress.com/2020/12/08/hash-joins-2/&quot;&gt;this article on hash joins&lt;/a&gt;
as an example of how you might want to compel the optimizer to do things a particular way (though I am almost certain 
Mr. Lewis would never say the words “bludgeon optimizer into submission”).&lt;/p&gt;

&lt;p&gt;You may likely find yourself needing to use more than a single cardinality hint plus degree of parallelism. Example:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'!&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;INSERT INTO ...
        SELECT 
            /*+ 
                __USE_CASE_HINT__
            */
            ...
        FROM my_driver_table d
        INNER JOIN big_table1 t1
            ON ...
        INNER JOIN midsize_table2 t2
            ON ...
        INNER JOIN big_table t3
            ON ...
    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IMMEDIATE&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;__USE_CASE_HINT__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_is_nightly_batch&lt;/span&gt; 
            &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'+&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;
                leading(d t1 t2 t3)
                full(d)
                use_hash(t1) full(t1) swap_join_inputs(t1)
                use_hash(t2) full(t2) no_swap_join_inputs(t2)
                use_hash(t3) full(t3) swap_join_inputs(t3)
                &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+'&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;q'+&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;
                leading(d t1 t2 t3)
                full(d)
                use_nl(t1) index(t1)
                use_nl(t2) index(t2)
                use_nl(t3) index_ss(t3)
                &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+'&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can imagine it can get fairly complex especially if you have a very large query perhaps
broken down using WITH query subfactoring (as I heartily recommend). It would be good to establish
a naming convention for the hint parameters. You may also want to add an option to the procedure
call that prints the SQL with DBMS_OUTPUT rather than executing it. It will help with 
debugging and establishing the best plan.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;When you have one business problem with multiple SQL optimization use cases, make every attempt
to keep the business logic in a single set of code. Decorate the dynamic sql with replaceable hints
that are appropriate for the use case. Use the minimal set of hints that achieve the objective, 
but also give yourself some room to adapt when the datbase changes out from under you.
Parameterizing the hint values so that they may be changed at run time is a good option
that does NOT mean you are changing the code at run time. The actual business logic
is that which was tested and promoted to production. Only the optimization is changing
and that in response to a business need. This is a solution that comports with best practices
while providing some flexibility.&lt;/p&gt;
</description>
        <pubDate>Sun, 17 Oct 2021 11:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2021/10/17/Use-Case-SQL-Tuning.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2021/10/17/Use-Case-SQL-Tuning.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>tuning</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Why do I need Inline PL/SQL Methods?</title>
        <description>&lt;h1 id=&quot;declaring-plsql-in-an-oracle-sql-with-clause&quot;&gt;Declaring PL/SQL in an Oracle SQL &lt;em&gt;WITH&lt;/em&gt; Clause&lt;/h1&gt;

&lt;p&gt;The capability to define PL/SQL functions and procedures inside an Oracle SQL query (and even 
the query portion of DML statements) was added in Oracle version 12.1. The Oracle documentation 
for it that I have found so far is sparse. Tim Hall of &lt;em&gt;Oracle Base&lt;/em&gt; fame has a good primer
on it &lt;a href=&quot;https://oracle-base.com/articles/12c/with-clause-enhancements-12cr1&quot;&gt;WITH Clause Enhancements in Oracle Database 12c Release 1 (12.1)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The primary reason (allegedly) that Oracle provided for defining the PL/SQL code inline is
to improve performance by avoiding context switching. Yet in the same release we were given
&lt;em&gt;PRAGMA UDF&lt;/em&gt; which allows optimizing schema level PL/SQL functions as inline. That seems a wash.&lt;/p&gt;

&lt;p&gt;The best case I have for deploying inline methods in production code is when the logic is specific to that single query, and is not easily and cleanly done directly in SQL. There are not that many tasks that are so difficult in SQL that this condition exists, but there are some where procedural code is just a better answer. I would also add a caveat that if the procedure is big and gnarly, I would rather compile it in the schema than trying to debug it in the middle of a giant query.&lt;/p&gt;

&lt;h1 id=&quot;another-reason-for-needing-inline-plsql&quot;&gt;Another Reason for Needing Inline PL/SQL&lt;/h1&gt;

&lt;p&gt;I have another use for inline PL/SQL. Consider the case where you cannot deploy schema level PL/SQL. The best example I have is doing adhoc queries on a production system. You can deploy PL/SQL to that system after going through the formal process, but not today.&lt;/p&gt;

&lt;p&gt;Maybe you have a real need for PL/SQL, like calling procedures with OUT parameters then selecting the value in a query, but you have query-only access on a system. Or perhaps your access to the database elements you need is through roles, not direct grants, so you cannot deploy PL/SQL that uses those elements.&lt;/p&gt;

&lt;h2 id=&quot;role-access-for-inline-plsql&quot;&gt;Role Access for Inline PL/SQL&lt;/h2&gt;

&lt;p&gt;For this test my schema has not been granted &lt;em&gt;SELECT&lt;/em&gt; on &lt;em&gt;HR.job_history&lt;/em&gt;. I have the role privilege &lt;em&gt;HR_SELECT&lt;/em&gt;,
and that role has been granted &lt;em&gt;SELECT&lt;/em&gt; on &lt;em&gt;HR.job_history&lt;/em&gt;. If I attempted to create a function
that read from the table &lt;em&gt;HR.job_history&lt;/em&gt;, the create would fail. The inline PL/SQL function works though.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_hist_job_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_emp&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l_job_id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEEP&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DENSE_RANK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FIRST&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end_date&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_job_id&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job_history&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_emp&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_job_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_hist_job_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;176&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET_HIST_JOB_ID(176)
--------------------------------------------------------------------------------
SA_MAN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Granted that the function doesn’t do anything that we could not have done directly in the query, but the test was merely to prove that we can avoid the issue with grants through roles versus direct grants to the schema owner. If you need PL/SQL and face a limitation where you cannot get the direct grants, this might be a way around it. Note that this is not likely to please certain control freaks, so keep it to yourself.&lt;/p&gt;

&lt;p&gt;You might also be able to get around this limitation by using dynamic sql (EXECUTE IMMEDIATE) and AUTHID CURRENT_USER. I admit to not having a complete grasp of all of the intricacies of the privilege stack where dynamic sql is involved, but I think it can be done. I like the inline PL/SQL method for it though.&lt;/p&gt;

&lt;h2 id=&quot;selecting-a-value-from-a-procedure-out-parameter&quot;&gt;Selecting a Value from a Procedure OUT Parameter&lt;/h2&gt;

&lt;p&gt;Consider a scenario where the only methods available to you for retrieving some value
is through a procedure with an OUT parameter. You cannot call that directly from a
SQL select. In this system we cannot deploy schema level PL/SQL today, so we will use
an inline function to provide the result.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;DBMS_LOB&lt;/em&gt; package has a procedure &lt;em&gt;LOADBLOBFROMFILE&lt;/em&gt; that puts the result in an OUT parameter.
I want to SELECT that BLOB value and save it to my local drive (which I can do easily from Toad
or SqlDeveloper).&lt;/p&gt;

&lt;p&gt;As of Oracle 12.2 you can do this in straight SQL (see &lt;a href=&quot;https://odieweblog.wordpress.com/2017/04/17/oracle-12-2-to_clob-and-to_blob-enhancements/&quot;&gt;TO_CLOB and TO_BLOB enhancements&lt;/a&gt;), 
but I’m going to use the example anyway.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_directory&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_filename&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l_bfile&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;BFILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l_blob&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l_blob_out&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l_dest_offset&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l_src_offset&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;INTEGER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l_bfile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BFILENAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_bfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_readonly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getlength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_bfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createtemporary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loadblobfromfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dest_lob&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_blob&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_bfile&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_bfile&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getlength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_bfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dest_offset&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_dest_offset&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_offset&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_src_offset&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l_blob_out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;freetemporary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DBMS_LOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileclose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l_bfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l_blob_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TMP_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;x.xlsx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That gives me a “(BLOB)” in the query result in SqlDeveloper that I can download to my local disk.&lt;/p&gt;

&lt;h2 id=&quot;procedure-required-to-produce-result&quot;&gt;Procedure Required to Produce Result&lt;/h2&gt;

&lt;p&gt;In a similar vein you may require execution of multiple PL/SQL procedural steps to produce
the output you ultimately want to select in your adhoc query.&lt;/p&gt;

&lt;p&gt;Marc Bleron has an excellent tool for generating MS Excel files directly in the database &lt;a href=&quot;https://github.com/mbleron/ExcelGen&quot;&gt;ExcelGen - An Oracle PL/SQL Generator for MS Excel Files&lt;/a&gt;. Most of the time you will use this
tool within PL/SQL procedures or packages that you deploy. Yet there are times when it can
be useful for an adhoc task. Sure, you can have Toad or Sqldeveloper produce CSV or even Excel
files from a query, but they do not match this for capability. And if you happen to have
already written a procedure that does much of what you need for your adhoc query, you can
quickly copy/paste the code into a WITH clause and be ready to rock and roll.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_xlsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_src&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SYS_REFCURSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctxHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;BINARY_INTEGER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addSheetFromCursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employee Salaries&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_sheetIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- freeze the top row with the column headers&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_frozen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- style with alternating colors on each row. &lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setTableFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TableStyleLight2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- single column format on the salary column. The ID column keeps default format&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setColumnFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p_ctxId&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_sheetId&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_sheetHandle&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_columnId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;-- the salary column&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_format&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;$#,##0.00&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getFileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;closeContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- begin sql portion &lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_bilbo&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;departments&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;UNION&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ALL&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;999&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Baggins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Bilbo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Sales&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;123.45&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emp_curs&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_bilbo&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_xlsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CURSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emp_curs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DUAL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The trick of passing a CURSOR variable using the last named subquery of the WITH clause (last line of above sample)
is awesome. Many times I’ve written huge queries that were stuffed into a CURSOR cast to pass into a Function
before I discovered this handy syntax.&lt;/p&gt;

&lt;p&gt;Since the ExcelGen package uses session level package global variables to store everything, 
you could probably execute an anonymous PL/SQL
block using a client declared context variable that does the first part of this through &lt;em&gt;setColumnFormat&lt;/em&gt;, then use a simple&lt;/p&gt;
&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExcelGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getFileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_ctxId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;to get the result. I like the &lt;em&gt;WITH PL/SQL&lt;/em&gt; version much better.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/excelpage.gif&quot; alt=&quot;ExcelPage&quot; class=&quot;centered&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here is another example using &lt;a href=&quot;https://github.com/lee-lindley/PdfGen&quot;&gt;PdfGen&lt;/a&gt;, a package I wrote that wraps
the core &lt;em&gt;as_pdf3&lt;/em&gt; written by Anton Scheffer.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_src&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SYS_REFCURSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;BLOB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_widths&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t_col_widths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headers&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t_col_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_formats&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t_col_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
                                                &lt;span class=&quot;c1&quot;&gt;-- Similar to the sqlplus COLUMN HEADING commands&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employee ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_widths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Last Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_widths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;First Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_widths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                                                &lt;span class=&quot;c1&quot;&gt;-- will not print this column, &lt;/span&gt;
                                                &lt;span class=&quot;c1&quot;&gt;-- just capture it for column page break&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                       &lt;span class=&quot;c1&quot;&gt;--'Department Name'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_widths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                          &lt;span class=&quot;c1&quot;&gt;-- sqlplus COLUMN NOPRINT &lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_widths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- override default number format for this column&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_formats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;$999,999,999.99&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_page_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_format&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;LETTER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_orientation&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;PORTRAIT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_top_margin&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_bottom_margin&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_left_margin&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.75&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_right_margin&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.75&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_footer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                          &lt;span class=&quot;c1&quot;&gt;-- 'Page #PAGE_NR# of &quot;PAGE_COUNT#' is the default&lt;/span&gt;
                                                &lt;span class=&quot;c1&quot;&gt;-- sqlplus TITLE command&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_page_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_txt_center&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Employee Salary Report&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_font_family&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;helvetica&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_style&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_fontsize_pt&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_txt_left_2&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Department: !PAGE_VAL#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_font_family_2&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;helvetica&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_fontsize_pt_2&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_style_2&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;as_pdf3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_font&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;courier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;refcursor2table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_src&lt;/span&gt;                       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_src&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_widths&lt;/span&gt;                   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_widths&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_headers&lt;/span&gt;                  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_headers&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_formats&lt;/span&gt;                  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_formats&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_bold_headers&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;-- also light gray background on headers&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_char_widths_conversion&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_break_col&lt;/span&gt;                &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;-- sqlplus BREAK ON column&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_grid_lines&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                              
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- begin sql&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SUM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;-- emulate sqplus COMPUTE SUM&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;departments&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GROUPING&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SETS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                        &lt;span class=&quot;c1&quot;&gt;-- seemingly useless SUM on single record, &lt;/span&gt;
                                        &lt;span class=&quot;c1&quot;&gt;-- but required to get detail records&lt;/span&gt;
                                        &lt;span class=&quot;c1&quot;&gt;-- in same query as the subtotal and total aggregates&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;-- sqlplus COMPUTE SUM of salary ON department_name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,()&lt;/span&gt;                             &lt;span class=&quot;c1&quot;&gt;-- sqlplus COMPUTE SUM of salary ON report - the grand total&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- NULL last_name indicates an aggregate result.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- NULL department_name indicates it was the grand total&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;-- Similar to the LABEL on COMPUTE SUM&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NVL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;NULL&lt;/span&gt;
                            &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LPAD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;GRAND TOTAL:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;kr&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LPAD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;DEPT TOTAL:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULLS&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LAST&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;-- to get the aggregates after detail&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULLS&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LAST&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;-- notice based on FROM column value, not the one we munged in resultset&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CURSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dual&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;An example page from that pdf report follows. That is all a bit much for something you are doing adhoc, but
given the scrutiny placed over moving code into production in modern corporate environments, you may
find yourself reaching at times. This is a capability that will allow you to reach pretty far without
deploying any code to production. That is nice to have in your back pocket when everyone is looking to
you for a miracle.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/test0_pg1.png&quot; alt=&quot;PDFGen_Page&quot; class=&quot;centered&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;writes&quot;&gt;Writes&lt;/h1&gt;

&lt;p&gt;Since this facility is part of a SELECT query (or the SELECT portion of DML), it is limited to read only operations. If your inline procedure or function will perform DML or DDL, you need to declare it with PRAGMA AUTONOMOUS_TRANSACTION 
as mentioned in this &lt;a href=&quot;https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:9537276300346582444&quot;&gt;asktom question&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I won’t disagree with the assertion that it is generally a bad idea, but when constrained by circumstances, one does as needs must.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Although avoidance of context switching may have been the driving force behind Oracle delivering
the inline PL/SQL capability for the SQL engine, we can take advantage of it for many reasons.
I hope these examples were enlightening.&lt;/p&gt;
</description>
        <pubDate>Sat, 25 Sep 2021 11:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2021/09/25/Inline-PLSQL-Methods.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2021/09/25/Inline-PLSQL-Methods.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>The Ubiquitous CSV File</title>
        <description>&lt;h1 id=&quot;the-oh-so-common-csv-files&quot;&gt;The Oh So Common CSV Files&lt;/h1&gt;

&lt;p&gt;One of the most common methods for transferring data between applications and businesses 
is the comma separated value format file. It has many advantages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It is relatively simple.&lt;/li&gt;
  &lt;li&gt;It is entirely text, so it can be transported between computers running pretty much any operating system.&lt;/li&gt;
  &lt;li&gt;It can be opened by common desktop applications like Excel.&lt;/li&gt;
  &lt;li&gt;It isn’t that difficult to generate manually.&lt;/li&gt;
  &lt;li&gt;Many applications can generate and consume it automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a semi-official standard for the format 
at &lt;a href=&quot;https://www.loc.gov/preservation/digital/formats/fdd/fdd000323.shtml&quot;&gt;RFC4180&lt;/a&gt;,
but if you read it you will find it is fairly permissive about how you can quote
fields that contain separators and even newlines.&lt;/p&gt;

&lt;p&gt;Parsing a CSV file seems simple
until you read all of the rules and sit down to try to do it. I have what I think
is a complete solution for parsing CSV in the Oracle PL/SQL 
function &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#split&quot;&gt;split&lt;/a&gt;. The regular
expression it employs is so ugly the comments outweigh the code ten to one. Yet
it seems to work just fine, passing all of the test cases I can conjour to throw at it.&lt;/p&gt;

&lt;h1 id=&quot;loading-csv-file&quot;&gt;Loading CSV File&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;sqlldr&lt;/em&gt; (and by extension external tables) support a text import option that should
work for most honest CSV input files:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '&quot;'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I did not go out of my way to torture it the way I did my &lt;em&gt;split&lt;/em&gt; function, yet the only
issues I’ve ever encountered with it had to do with funky non-ascii characters, which is
only tangentially related to the CSV format in that it depends on an understood common
or convertible character set. When the input file does not comply, as for instance when
a non-quoted string contains the delimiter, all bets are off.&lt;/p&gt;

&lt;p&gt;If the input file follows &lt;a href=&quot;https://www.loc.gov/preservation/digital/formats/fdd/fdd000323.shtml&quot;&gt;RFC4180&lt;/a&gt;,
I suspect Oracle &lt;em&gt;sqlldr&lt;/em&gt; will parse it just fine. I could be wrong.&lt;/p&gt;

&lt;h1 id=&quot;generating-csv&quot;&gt;Generating CSV&lt;/h1&gt;

&lt;p&gt;From an Oracle database there are tools we can use to generate a CSV output file.&lt;/p&gt;

&lt;h2 id=&quot;gui-clients&quot;&gt;GUI Clients&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Toad&lt;/em&gt; and &lt;em&gt;Sqldeveloper&lt;/em&gt; both have CSV format file output options. You chan choose whether
or not to quote and whether or not to include column headers. These work well, but they
require a person to execute them. That is not a viable solution to something you want to automate.&lt;/p&gt;

&lt;h2 id=&quot;sqlcl-aka-sql-command-line&quot;&gt;SQLcl (aka sql command line)&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;SQLcl&lt;/em&gt; (SQL Developer Command Line) is a Java-based tool. It supports most of the cruft of &lt;em&gt;sqlplus&lt;/em&gt;
while also adding new stuff and many of the features of &lt;em&gt;SqlDeveloper&lt;/em&gt;. Although it ships with the Oracle
client, it may or may not be installed on your ETL server. It has a robust set of
output formats and is quite handy with CSV, both for exporting and for importing!&lt;/p&gt;

&lt;p&gt;I have not seen
it used as a replacement for sqlplus and am not completely sure why. It may be that the people who
install and maintain database and ETL servers are just dinosaurs who don’t trust new stuff. I mean
it is only up to version 20.4. &lt;em&gt;sqlplus&lt;/em&gt; on the other hand has been around as long as Oracle has been
and it is everywhere.&lt;/p&gt;

&lt;h2 id=&quot;sqlplus&quot;&gt;sqlplus&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;SQL*Plus 12.2&lt;/em&gt; has added a &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqpug/SET-system-variable-summary.html#GUID-0AA910C4-C22A-4A9E-BE13-AAA059CC7919&quot;&gt;set markup csv&lt;/a&gt;
option, and when you turn on text quoting&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;set markup csv on quote on
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;it will “escape” double quotes embedded witin the text. 
This can also give you the column headers in CSV format when you have “set heading on”, 
so it may be a decent option. We will come back to that.&lt;/p&gt;

&lt;p&gt;If you are stuck with an older version, you can concatenate data elements converted to text
with comma’s between them in your SQL statement and spool it to a text file. For example:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pagesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heading&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimspool&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;termout&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
    &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;departments&lt;/span&gt; 
&lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I have seen countless scripts like this in my career. I cringe every time I see one because I know it is
one careless data entry event away from waking up some poor support person in the middle of the night
when the file won’t load because there is a comma in one of the field values. A little better pattern
can protect against that:&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pagesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heading&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimspool&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;termout&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
                  &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;       &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;       &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;departments&lt;/span&gt; 
&lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Aside from how ugly it is and the chance that you make a mistake while typing it in, there is another
potential issue. What if the comma we fear is not what gets entered into the field value?
What if it is a double quote mark? What if it is a newline character?&lt;/p&gt;

&lt;p&gt;I chose to not enclose the numeric fields with double quotes, but what if some joker changed the NLS
settings on us and the new default number format uses a comma for the decimal? DOH!&lt;/p&gt;

&lt;p&gt;Another issue with this is that you don’t get the column headers. If you need them there are
several clever ways to do it. You can google search it. I’m not happy with any of them.&lt;/p&gt;

&lt;p&gt;The markup csv option available starting in sqlplus that comes with Oracle 12c looks promising. 
Let’s try it out. We’ll add some tricks with the NLS
date and numeric default formats to see how smart it is. I’ve made both default conversion
formats contain a comma in the result.&lt;/p&gt;

&lt;p&gt;First we will try with &lt;em&gt;quote off&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SESSION&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nls_date_format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;YYYYMMDD, HH24:MI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SESSION&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nls_numeric_characters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;markup&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pagesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--set heading off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimspool&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;termout&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_markup_noquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a string with comma(,)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;s1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a string with dquote(&quot;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;s2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a string with newline(
)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SYSDATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12345&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;num1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DUAL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And now the spool file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat test_markup_noquote.csv

s1,s2,s3,date1,num1
a string with comma(,),a string with dquote(&quot;),a string with newline(
),20210910, 04:52,123,45
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As I suspected if you set quote off in the markup directive, you get what you paid for. That does
not comply with the CSV format specification.&lt;/p&gt;

&lt;p&gt;It also seems to have both a leading and trailing blank line in it. Not sure what is up with that or whether
you can get rid of them short of doing something in the shell afterward. If I decide to use it
for a job, I’ll have to figure that out.&lt;/p&gt;

&lt;p&gt;Let’s try the same but with &lt;em&gt;quote on&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SESSION&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nls_date_format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;YYYYMMDD, HH24:MI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SESSION&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nls_numeric_characters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;markup&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pagesize&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--set heading off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trimspool&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;on&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;termout&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_markup_wquote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a string with comma(,)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;s1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a string with dquote(&quot;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;s2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a string with newline(
)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SYSDATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12345&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;num1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DUAL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;off&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And now the spool file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;s1&quot;,&quot;s2&quot;,&quot;s3&quot;,&quot;date1&quot;,&quot;num1&quot;
&quot;a string with comma(,)&quot;,&quot;a string with dquote(&quot;&quot;)&quot;,&quot;a string with newline(
)&quot;,&quot;20210910, 05:06&quot;,123,45
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well that is interesting. It handles the character types that contain double quotes just
like the RFC specifies by doubling up the internal doublequote marks. A competent CSV parser
should have no trouble with that. As a bonus, it was smart enough to put double quotes around the 
date value converted by the default NLS format. Sweet! But it did NOT quote the number “num1” value
even though it contained a separator comma,
and that is a potential issue.  It is not a very likely issue to happen to you in the real world though.
And if you think it could be, you can always do the TO_CHAR conversion of numbers to strings in your
query.&lt;/p&gt;

&lt;p&gt;Overall using sqlplus with the &lt;em&gt;markup csv&lt;/em&gt;
option looks to be a fine way to generate CSV files.&lt;/p&gt;

&lt;h1 id=&quot;custom-tools&quot;&gt;Custom Tools&lt;/h1&gt;

&lt;p&gt;Maybe the ability to run a batch operation on an ETL server using sqlplus is not part of your
operational toolset. Maybe you need to generate the file from inside a program that is not able
to launch sqlplus, say for example you have a job that is running from DBMS_SCHEDULER_JOBS directly 
in the database, or the task is launched from a trigger or a web application interacting with the database.&lt;/p&gt;

&lt;p&gt;For those scenarios where you cannot use a client program to generate the file, we can generate the
file using PL/SQL.&lt;/p&gt;

&lt;h2 id=&quot;the-hard-way&quot;&gt;The Hard Way&lt;/h2&gt;

&lt;p&gt;You can open a file on the database server and manually construct and write the CSV file.
This is a fairly common technique I have seen deployed by clients (though they rarely bothered
with the bulk collect).&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;CURSOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c1&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;departments&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SUBTYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_c1_rec&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c1&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;%ROWTYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_arr_c1_rec&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;t_arr_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_file&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;UTL_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_dir&lt;/span&gt;           &lt;span class=&quot;k&quot;&gt;CONSTANT&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TMP_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_file_name&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;CONSTANT&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;test_csv_dbfile.csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_file_name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;location&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_dir&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open_mode&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_linesize&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32767&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UTL_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;employee_id,last_name,first_name,department_name,salary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FETCH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BULK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;COLLECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;EXIT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;UTL_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LTRIM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TO_CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_c1_rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;$999,999,999.99&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                                                            &lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;CLOSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UTL_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fclose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OTHERS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTL_FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fclose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;RAISE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lee@linux2 [/tmp]
$ head -10 test_csv_dbfile.csv
employee_id,last_name,first_name,department_name,salary
206,&quot;Gietz&quot;,&quot;William&quot;,&quot;Accounting&quot;,&quot;$8,300.00&quot;
205,&quot;Higgins&quot;,&quot;Shelley&quot;,&quot;Accounting&quot;,&quot;$12,008.00&quot;
200,&quot;Whalen&quot;,&quot;Jennifer&quot;,&quot;Administration&quot;,&quot;$4,400.00&quot;
102,&quot;De Haan&quot;,&quot;Lex&quot;,&quot;Executive&quot;,&quot;$17,000.00&quot;
100,&quot;King&quot;,&quot;Steven&quot;,&quot;Executive&quot;,&quot;$24,000.00&quot;
101,&quot;Kochhar&quot;,&quot;Neena&quot;,&quot;Executive&quot;,&quot;$17,000.00&quot;
110,&quot;Chen&quot;,&quot;John&quot;,&quot;Finance&quot;,&quot;$8,200.00&quot;
109,&quot;Faviet&quot;,&quot;Daniel&quot;,&quot;Finance&quot;,&quot;$9,000.00&quot;
108,&quot;Greenberg&quot;,&quot;Nancy&quot;,&quot;Finance&quot;,&quot;$12,008.00&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That works well enough, but man oh man is it painful.&lt;/p&gt;

&lt;h2 id=&quot;refcursor-to-csv&quot;&gt;refcursor-to-csv&lt;/h2&gt;

&lt;p&gt;William Robertson published a nice package that can generate CSV data that complies with the RFC
from any ref cursor. &lt;a href=&quot;https://www.williamrobertson.net/documents/refcursor-to-csv.shtml&quot;&gt;refcursor-to-csv&lt;/a&gt;
is an approach that is completely within the database rather than in a client. This means
you can select the resulting CSV rows in any client or write them to a file on the database server. It is
a fine tool that may well meet your needs. He adds some bells and whistles for producing
optional header and trailer records that may be exactly what you are looking for.&lt;/p&gt;

&lt;p&gt;Here are two examples from Mr. Robertson’s documentation at the above link:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;select column_value
from   table(csv.report(cursor(
        select * from dept
    ), p_separator =&amp;gt; '|', p_label =&amp;gt; 'DEPT', p_heading =&amp;gt; 'Y', p_rowcount =&amp;gt; 'Y'));

COLUMN_VALUE
----------------------------------------------
HEADING|DEPT|DEPTNO|DNAME|LOC
DEPT|10|ACCOUNTING|NEW YORK
DEPT|20|RESEARCH|DALLAS
DEPT|30|SALES|CHICAGO
DEPT|40|OPERATIONS|BOSTON
ROW_COUNT|DEPT|4

6 rows selected.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the example of writing to a file on the database server:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;declare
    l_dataset sys_refcursor;
begin
    open l_dataset for select * from dept;

    csv.write_file
    ( p_dataset =&amp;gt; l_dataset
    , p_separator =&amp;gt; '|', p_label =&amp;gt; 'DEPT', p_heading =&amp;gt; 'Y', p_rowcount =&amp;gt; 'Y'
    , p_directory =&amp;gt; 'DATA_FILE_DIR'
    , p_filename =&amp;gt; 'dept.csv' );
end;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;app_csv&quot;&gt;app_csv&lt;/h2&gt;

&lt;p&gt;I was inspired by Mr. Robertson’s work to extend it in several ways where I wanted more flexibility.
I also had another use for the underlying mechanism of using DBMS_SQL to gather
the cursor results for any unknown query. I created base Object types I 
call &lt;a href=&quot;https://github.com/lee-lindley/plsql_utilities#app_dbms_sql&quot;&gt;app_dbms_sql&lt;/a&gt;
and then built &lt;a href=&quot;https://github.com/lee-lindley/app_csv&quot;&gt;app_csv&lt;/a&gt; on top of those.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/lee-lindley/app_csv#use-cases&quot;&gt;app_csv documentation on use cases&lt;/a&gt;
show selecting from a TABLE function, writing to a database server file, and retrieving
a CLOB. The latter is particularly useful if you want to email the CSV data as an attachment.
(See &lt;a href=&quot;https://github.com/lee-lindley/html_email&quot;&gt;html_email&lt;/a&gt; for a tool that can send email with
attachments from the database server.)&lt;/p&gt;

&lt;p&gt;Both of these tools process completely within the database engine.&lt;/p&gt;

&lt;h2 id=&quot;excelgen&quot;&gt;ExcelGen&lt;/h2&gt;

&lt;p&gt;If the only reason you are producing CSV files is because you want to give them to a user to open
in Excel, why stop there? &lt;a href=&quot;https://github.com/mbleron/ExcelGen&quot;&gt;ExcelGen&lt;/a&gt; is a package by Marc Bleron that
can convert a refcursor (just like &lt;em&gt;app_csv&lt;/em&gt; and &lt;em&gt;refcursor-to-csv&lt;/em&gt; do) and generate an XLSX
file. Maybe your users are happy enough with opening a CSV file in excel and mucking with the column formats
for a few minutes every day, but you can go the extra step without a whole lot more trouble. Plus as a bonus
if you have multiple queries and files you are producing for them, you can combine them as separate
tabs/sheets in the same workbook. The format is compressed too so files are smaller.&lt;/p&gt;

&lt;p&gt;It is just a little bit more work than producing CSV files, yet the end product is sure to be appreciated
by your users.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;These are some techniques you may be able to use to fit a need. Hope it was helpful.&lt;/p&gt;
</description>
        <pubDate>Fri, 10 Sep 2021 11:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2021/09/10/Ubiquitous-CSV_file.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2021/09/10/Ubiquitous-CSV_file.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>csv</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Oracle Object Types as Application Interface</title>
        <description>&lt;h1 id=&quot;scenario&quot;&gt;Scenario&lt;/h1&gt;

&lt;p&gt;As the Oracle specialist on an application development team your goal is to provide an interface to the application data for use by team members developing in other tools such as a Java or .NET development platform.&lt;/p&gt;

&lt;table class=&quot;img-table-centered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;em&gt;Object Type Application Interface Deployment Diagram&lt;/em&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/object_type_interface_deployment.gif&quot; alt=&quot;object_type_interface_deployment.gif&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h1 id=&quot;traditional-method&quot;&gt;Traditional Method&lt;/h1&gt;

&lt;p&gt;You provide queries and perhaps views and/or materialized views for use by the team. Perhaps you provide functions or procedures that return cursors or maybe a Pipelined Table function. These are all perfectly valid methods for providing a two dimensional data structure of rows and columns to the application. You can build business rules into the queries and methods you provide, and assist with database operations where you add value to the team.&lt;/p&gt;

&lt;p&gt;What if your data is more complex? When the data is logically represented by parent/child relationships with multiple child objects, the traditional method might simply duplicate common parent data on every row. The database client, a Java program for example, will likely build a method to consume this data, pulling the common data elements into one structure and the child data elements into another, rebuilding the logical relationship into memory. This is wasteful on multiple levels. There is bandwidth and memory waste with transferring the redundant data as well as the development time waste of recreating the structure after it had been flattened during the transfer.&lt;/p&gt;

&lt;p&gt;Remember also that in a high volume environment, each round trip to the database has a real cost in time and resources. Most modern Web based applications are built using stateless, single operation connections from a pool. This is relatively efficient, but there is real overhead in time and resources for each separate database call. Whenever practical, it is better to retrieve necessary data in a single call rather than multiple round trips to the database (assuming memory is not the constraint).&lt;/p&gt;

&lt;h1 id=&quot;using-oracle-object-types&quot;&gt;Using Oracle Object Types&lt;/h1&gt;

&lt;p&gt;Modern Oracle database connectors provide classes and methods for consuming complex structures from the database in the form of database Types. These can be traditional record Types as well as complex Objects, which includes Collections. The Oracle drivers will transport the complex structures over the wire efficiently and convert them into native data structures. IDE’s usually have tools to see these object types when you point them at a database procedure, and assist with building custom structures and classes for consuming and accessing the data.&lt;/p&gt;

&lt;h1 id=&quot;object-hiearchy-example&quot;&gt;Object Hiearchy Example&lt;/h1&gt;

&lt;p&gt;Although Object types can have methods, we will focus on using them as a hierachical structure. The &lt;em&gt;MAP&lt;/em&gt; method is defined in this example to make it convenient to sort the objects as they are collected. You may be able to use other techniques to sort before aggregating, but this is the recommended way. Don’t focus on that too much. The main point is that the Objects are basically Structures for our purpose.&lt;/p&gt;

&lt;p&gt;We will use the &lt;em&gt;HR&lt;/em&gt; sample schema for the demonstration. The example is contrived in that the overhead of putting the &lt;em&gt;department&lt;/em&gt; data into a flattened row or running two separate queries, is minimal, and we would not bother normally. Pretend that we have a much larger common object, perhaps multiple disparate child arrays, and/or extremely high volume that will justify the added complexity.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OBJECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;              &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;phone_number&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hire_date&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;DATE&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;             &lt;span class=&quot;kt&quot;&gt;NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- for sorting&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;MEMBER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BODY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;MAP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;MEMBER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'||&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_employee_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OBJECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_last_name&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_first_name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_employee&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;arr_employee_udt&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;MEMBER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dmap&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BODY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;MAP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;MEMBER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dmap&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;VARCHAR2&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_department_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each department object will contain all of the employees in the department. The top level array of departments object is a single unit that can be transferred in one database call. Alternatively, if that object could be too large to be efficient, a cursor can return rows of &lt;em&gt;department_udt&lt;/em&gt; objects, perhaps bulk fetched depending on how it is called. Here is a package that provides both. The query is written in multiple pieces using &lt;em&gt;WITH&lt;/em&gt; subquery factoring to make it easier to follow (a practice I recommend to keep large queries easier to understand).&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employees_by_dept_pkg&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p_arr_department&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OUT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_department_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_department_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PACKAGE&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BODY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employees_by_dept_pkg&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;CURSOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;IS&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;he&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manager_last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;he&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manager_first_name&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;departments&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hd&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;he&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;he&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_id&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;phone_number&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;phone_number&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hire_date&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hire_date&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emp&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;-- uses employee_udt.map method to sort&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COLLECT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emp&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_employee_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_employee&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                     &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_last_name&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_last_name&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_first_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manager_first_name&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_employee&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_employee&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e2&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;department_id&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dept&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p_arr_department&lt;/span&gt;    &lt;span class=&quot;kr&quot;&gt;OUT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_department_udt&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- if the cursor was previsously opened and not yet closed in this session&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- then close it and try again. &lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;EXIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CURSOR_ALREADY_OPEN&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CLOSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FETCH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BULK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;COLLECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p_arr_department&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;CLOSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr_department_udt&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;PIPELINED&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;AS&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;v_arr_department&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;arr_department_udt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- if the cursor was previsously opened and not yet closed in this session&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- then close it and try again. &lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;OPEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;EXIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;EXCEPTION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CURSOR_ALREADY_OPEN&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CLOSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;-- pretending object is very large, thus small limit&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;FETCH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BULK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;COLLECT&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_department&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;EXIT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_arr_department&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_department&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;COUNT&lt;/span&gt;
            &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;
                &lt;span class=&quot;kr&quot;&gt;PIPE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_arr_department&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;LOOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;CLOSE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;RETURN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employees_by_dept_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To call the procedure version from a language using an Oracle database driver you would typically find the package in your IDE interface and allow it to help you build a class to call the procedure and populate an internal structure via the OUT parameter. For us mere mortals restricted to standard database tools such as sqldeveloper, we can use the pipelined table function&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees_by_dept_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here is one row as represented in sqldeveloper query result window:&lt;/p&gt;

&lt;div class=&quot;table-scroll&quot;&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;DEPARTMENT_NAME&lt;/th&gt;
        &lt;th&gt;MANAGER_LAST_NAME&lt;/th&gt;
        &lt;th&gt;MANAGER_FIRST_NAME&lt;/th&gt;
        &lt;th&gt;ARR_EMPLOYEE&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Accounting&lt;/td&gt;
        &lt;td&gt;Higgins&lt;/td&gt;
        &lt;td&gt;Shelley&lt;/td&gt;
        &lt;td&gt;LEE.ARR_EMPLOYEE_UDT(LEE.EMPLOYEE_UDT(‘Gietz’, ‘William’, ‘WGIETZ’, ‘515.123.8181’, ‘2002-06-07 00:00:00.0’, 8300), LEE.EMPLOYEE_UDT(‘Higgins’, ‘Shelley’, ‘SHIGGINS’, ‘515.123.8080’, ‘2002-06-07 00:00:00.0’, 12008))&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

&lt;/div&gt;

&lt;p&gt;A query that joins back to the employee object column flattens it back out to one record per employee with duplication of the department information, but at least we can see everything.&lt;/p&gt;

&lt;div class=&quot;language-plsql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;department_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manager_last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manager_first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employees_by_dept_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr_employee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rownum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-scroll&quot;&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;DEPARTMENT_NAME&lt;/th&gt;
        &lt;th&gt;MANAGER_LAST_NAME&lt;/th&gt;
        &lt;th&gt;MANAGER_FIRST_NAME&lt;/th&gt;
        &lt;th&gt;LAST_NAME&lt;/th&gt;
        &lt;th&gt;FIRST_NAME&lt;/th&gt;
        &lt;th&gt;EMAIL&lt;/th&gt;
        &lt;th&gt;PHONE_NUMBER&lt;/th&gt;
        &lt;th&gt;HIRE_DATE&lt;/th&gt;
        &lt;th style=&quot;text-align: right&quot;&gt;SALARY&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Accounting&lt;/td&gt;
        &lt;td&gt;Higgins&lt;/td&gt;
        &lt;td&gt;Shelley&lt;/td&gt;
        &lt;td&gt;Gietz&lt;/td&gt;
        &lt;td&gt;William&lt;/td&gt;
        &lt;td&gt;WGIETZ&lt;/td&gt;
        &lt;td&gt;515.123.8181&lt;/td&gt;
        &lt;td&gt;07-JUN-02&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;8300&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Accounting&lt;/td&gt;
        &lt;td&gt;Higgins&lt;/td&gt;
        &lt;td&gt;Shelley&lt;/td&gt;
        &lt;td&gt;Higgins&lt;/td&gt;
        &lt;td&gt;Shelley&lt;/td&gt;
        &lt;td&gt;SHIGGINS&lt;/td&gt;
        &lt;td&gt;515.123.8080&lt;/td&gt;
        &lt;td&gt;07-JUN-02&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;12008&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Administration&lt;/td&gt;
        &lt;td&gt;Whalen&lt;/td&gt;
        &lt;td&gt;Jennifer&lt;/td&gt;
        &lt;td&gt;Whalen&lt;/td&gt;
        &lt;td&gt;Jennifer&lt;/td&gt;
        &lt;td&gt;JWHALEN&lt;/td&gt;
        &lt;td&gt;515.123.4444&lt;/td&gt;
        &lt;td&gt;17-SEP-03&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;4400&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Executive&lt;/td&gt;
        &lt;td&gt;King&lt;/td&gt;
        &lt;td&gt;Steven&lt;/td&gt;
        &lt;td&gt;De Haan&lt;/td&gt;
        &lt;td&gt;Lex&lt;/td&gt;
        &lt;td&gt;LDEHAAN&lt;/td&gt;
        &lt;td&gt;515.123.4569&lt;/td&gt;
        &lt;td&gt;13-JAN-01&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;17000&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Executive&lt;/td&gt;
        &lt;td&gt;King&lt;/td&gt;
        &lt;td&gt;Steven&lt;/td&gt;
        &lt;td&gt;King&lt;/td&gt;
        &lt;td&gt;Steven&lt;/td&gt;
        &lt;td&gt;SKING&lt;/td&gt;
        &lt;td&gt;515.123.4567&lt;/td&gt;
        &lt;td&gt;17-JUN-03&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;24000&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Executive&lt;/td&gt;
        &lt;td&gt;King&lt;/td&gt;
        &lt;td&gt;Steven&lt;/td&gt;
        &lt;td&gt;Kochhar&lt;/td&gt;
        &lt;td&gt;Neena&lt;/td&gt;
        &lt;td&gt;NKOCHHAR&lt;/td&gt;
        &lt;td&gt;515.123.4568&lt;/td&gt;
        &lt;td&gt;21-SEP-05&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;17000&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Finance&lt;/td&gt;
        &lt;td&gt;Greenberg&lt;/td&gt;
        &lt;td&gt;Nancy&lt;/td&gt;
        &lt;td&gt;Chen&lt;/td&gt;
        &lt;td&gt;John&lt;/td&gt;
        &lt;td&gt;JCHEN&lt;/td&gt;
        &lt;td&gt;515.124.4269&lt;/td&gt;
        &lt;td&gt;28-SEP-05&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;8200&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Finance&lt;/td&gt;
        &lt;td&gt;Greenberg&lt;/td&gt;
        &lt;td&gt;Nancy&lt;/td&gt;
        &lt;td&gt;Faviet&lt;/td&gt;
        &lt;td&gt;Daniel&lt;/td&gt;
        &lt;td&gt;DFAVIET&lt;/td&gt;
        &lt;td&gt;515.124.4169&lt;/td&gt;
        &lt;td&gt;16-AUG-02&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;9000&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Finance&lt;/td&gt;
        &lt;td&gt;Greenberg&lt;/td&gt;
        &lt;td&gt;Nancy&lt;/td&gt;
        &lt;td&gt;Greenberg&lt;/td&gt;
        &lt;td&gt;Nancy&lt;/td&gt;
        &lt;td&gt;NGREENBE&lt;/td&gt;
        &lt;td&gt;515.124.4569&lt;/td&gt;
        &lt;td&gt;17-AUG-02&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;12008&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Finance&lt;/td&gt;
        &lt;td&gt;Greenberg&lt;/td&gt;
        &lt;td&gt;Nancy&lt;/td&gt;
        &lt;td&gt;Popp&lt;/td&gt;
        &lt;td&gt;Luis&lt;/td&gt;
        &lt;td&gt;LPOPP&lt;/td&gt;
        &lt;td&gt;515.124.4567&lt;/td&gt;
        &lt;td&gt;07-DEC-07&lt;/td&gt;
        &lt;td style=&quot;text-align: right&quot;&gt;6900&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

&lt;/div&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;The technique of using Oracle Object Types to represent complex data can be an efficient way to provide applications what they need in a format that is well suited. It allows the back-end database developer the opportunity to shelter the human interface developer from needing to understand the full database model. Instead they can focus on the data model presented to them at the application level. This also enhances the ability to put business rules into the database back end as opposed to (or in addition to) building that logic on the front end. That may or may not be a desired approach for your organization, but at a minimum the business logic that is necessary at the database level can be enforced.&lt;/p&gt;
</description>
        <pubDate>Thu, 09 Sep 2021 11:30:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/oracle/sql/plsql/2021/09/09/Oracle-Objects-AppInterface.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/oracle/sql/plsql/2021/09/09/Oracle-Objects-AppInterface.html</guid>
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
        <category>object_types</category>
        
        
        <category>oracle</category>
        
        <category>sql</category>
        
        <category>plsql</category>
        
      </item>
    
      <item>
        <title>Install Oracle Enterprise Linux and Oracle Database on a Dual Boot Windows 10 Machine</title>
        <description>&lt;h1 id=&quot;project&quot;&gt;Project&lt;/h1&gt;

&lt;p&gt;Install &lt;em&gt;Oracle Enterprise Linux&lt;/em&gt; and &lt;em&gt;Oracle Database 19c&lt;/em&gt; on a dual boot Windows 10 machine.&lt;/p&gt;

&lt;p&gt;This will be a full Oracle install including ASM and grid for my professional experience rather than a minimal database under a virtual box.&lt;/p&gt;

&lt;p&gt;Note that I do not have a service contract with Oracle, am not paying $500 for one for my personal use, and therefore cannot get to the support documentation or the Oracle Unbreakable Linux Network (ULN). This includes all of the bug fixes and reports and lots of helpful information. If you run into trouble without this you are SOL. I learned the hard way to stick with the supported releases rather than go off trying to figure it out. I spent a week with Ubuntu and then another few days with Oracle Enterprise Linux 8 latest and greatest (which is not supported for Oracle 19c!!!). Just don’t do it. Frustration will be immense if you cannot get to the support documentation. [Note: You can get the database to install, but grid is another matter].&lt;/p&gt;

&lt;p&gt;This is our Home Theatre PC. Other than acting as a file server, the main usage it gets is Netflix via a web browser. That will work just find under Linux too, so I’m hopeful wife won’t mind which OS is running most of the time. That is the goal.&lt;/p&gt;

&lt;h1 id=&quot;credits&quot;&gt;Credits&lt;/h1&gt;

&lt;p&gt;I found all sorts of references using google searches and learned from them (as well as having been doing similar stuff for 35 years including sysadmin a very long time ago). Unfortunately, I did not save links except in a few cases where I included them. Rest assured though that if you google any of these subjects you will hit the same resources that I worked my way through. I wish I had done a better job with it. Nobody can know all this stuff. I mostly just fumbled my way through.&lt;/p&gt;

&lt;h1 id=&quot;hardware&quot;&gt;Hardware&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Intel NUC 7i5BNH with 512GB SSD with windows 10 installed.&lt;/li&gt;
  &lt;li&gt;LaCie d2 Professional SCSI Disk Device 4TB Thunderbolt USB drive ($200!!!!). It isn’t SSD but it is an enterprise level 4TB disk drive that is plenty fast through the USB-C Thunderbolt interface. I’ll be installing Oracle Linux and Oracle database on this drive.&lt;/li&gt;
  &lt;li&gt;“My Book” 3TB USB drive for windows public smb share for photos. This drive is shared by the windows boot as our “G” drive and I’ll be making the Linux boot share it the same way. The clients use the IP address to mount it and don’t care whether the machine is booted as Windows or Linux.&lt;/li&gt;
  &lt;li&gt;Although you can shrink the space on main windows drive and install Linux, I’m installing everything on the 4TB Lacie Thunderbolt disk. I’m leaving the main windows drive completely alone, though the boot loader may get touched along the way. My expectation is I can recover completely to the windows normal boot if needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;partitioning-the-drive&quot;&gt;Partitioning the Drive&lt;/h1&gt;

&lt;p&gt;Although you can let the Oracle Linux installer do this, it doesn’t behave the way I want. I want a raw partition to use with ASM for the Oracle data disks. I couldn’t figure out how to make the installer create that partition without putting a file system on it, so I would have to come back and do it later anyway. I found it easier just to use the Windows “Disk Management” tool to do it (or you can boot a gparted image from USB, but I didn’t find it necessary). On windows hit Start then type in “Disk Management”. I decided on the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;200 MB (198) partition for EFI /boot/efi&lt;/li&gt;
  &lt;li&gt;1 GB partition for /boot&lt;/li&gt;
  &lt;li&gt;552 GB partition for Volume Manager&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to leave room for the volume manager partition to grow so I created a partition with 1124.81 GB (the remainder) that I later removed leaving a gap. I realize the volume manager probably could have dealt with it just fine with it split across the disk, but I haven’t explored it yet.&lt;/p&gt;

&lt;p&gt;I was going to create a 2TB partition for ASM, but ran into a snag (it was slightly over). The documentation says the 2TB limitation is fixed after Oracle 11, but it is apparently still a problem. Breaking it into two 1TB partitions works fine and also gives me more chance to play with ASM. So, two 1024 GB (1048576 MB) partitions after the gap to use with ASM.&lt;/p&gt;

&lt;p&gt;After creating the two partitions for ASM, I deleted the place holder for the volume manager partition growth (1124.81 GB now unallocated). The resulting allocation looks like this (DISK 2 which is the Lacie drive):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0017_installorac1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;bios-setup&quot;&gt;BIOS Setup&lt;/h1&gt;

&lt;p&gt;While in Windows you may want to Turn off Fast Startup. If you google it you will find plenty of entries on how to do it. I don’t think it actually matters for this and it might get turned back on by windows update, but there are reports of it interfering with dual boot. On my machine I have to press F2 during startup during a very limited period to get to the bios setup. While there I can extend that time so it isn’t so hard to catch it. Now I don’t remember where exactly I found that on the NUC bios, but it waits 5 or 10 seconds for keyboard interrupt now on a power cycle. Turn off Fast Boot and make sure Secure boot is off. Make sure boot from USB is turned on. Here are some pictures of my bios screen. Not sure how useful they will be, but can’t hurt:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0017_installorac2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0017_installorac3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;download-the-oracle-linux-distribution&quot;&gt;Download the Oracle Linux Distribution&lt;/h1&gt;

&lt;p&gt;I learned the hard way that the latest and greatest distribution (OL 8) is not supported for the Oracle database 19c!!!!!!! Go to the Oracle database documentation to find a table with supported Linux releases. At this time, it is OEL 7.4 with Unbreakable Kernel 5 (which is included by default). But I found out that when you auto update 7.4, you wind up with 7.8. I hope it will work because 7.4 unbreakable linux kernel did not support my network card. In any case I’m going to try 7.8&lt;/p&gt;

&lt;p&gt;Go to the Oracle.com website and fumble around until you get to the dropdown menu that has On Premise Products/Software. Pick Oracle Linux. From there you can get to Download Oracle Linux which takes you to edelivery.oracle.com. From there you have to create an account or sign in. You do not need a support contract for this.&lt;/p&gt;

&lt;p&gt;Search for Oracle Linux. Choose DLP: Oracle Linux 7.8. I know you want that tasty looking 8. Don’t do it. 7.8 is last supported release for the database. Add it to your cart and then go to Checkout on top right.&lt;/p&gt;

&lt;p&gt;Pick x86 64bit for Platforms/Languages. Don’t freak out at the 14.0 GB, we will trim it down on the next step. Hit continue. Agree to the license. Get rid of the feedback popup. Uncheck everything by unchecking top level. Then go back and pick:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;V995537-01.iso Oracle Linux Release 7 Update 8 for x86 (64 bit), 4.5 GB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s all you will need. Note that if you use the wget option, that shell script invokes wget with ask-password which the manual says will prompt for it, but I think it directed the prompt to stderr which was writing to a log file. I entered the password even though not prompted and it started downloading.&lt;/p&gt;

&lt;h1 id=&quot;download-oracle-grid-and-oracle-database-19c&quot;&gt;Download Oracle Grid and Oracle database 19c&lt;/h1&gt;

&lt;p&gt;Also get grid install and Oracle 19c from edelivery.oracle.com. You should be able to find them by searching. I did not take a screenshot of this. I have the files locally:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\-rwxrwxrwx. 1 nobody nobody 3059705302 Apr 29 10:55 LINUX.X64\_193000\_db\_home.zip 
\-rwxrwxrwx. 1 nobody nobody 2889184573 Apr 29 10:55 LINUX.X64\_193000\_grid\_home.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;burn-iso-image-to-usb-stick&quot;&gt;Burn ISO image to USB stick&lt;/h1&gt;

&lt;p&gt;Download &lt;em&gt;rufus&lt;/em&gt; executable for windows. You don’t even have to install it. Just download it to the same place as your iso file. You can use that to create the bootable USB.&lt;/p&gt;

&lt;p&gt;You can find plenty of instructions on the web about how to do that. The only non-obvious thing is choose FreeDOS for the format. I don’t know why, but it works. Install the iso file onto the USB.&lt;/p&gt;

&lt;h1 id=&quot;boot-from-usb-stick-and-install&quot;&gt;Boot from USB stick and Install&lt;/h1&gt;

&lt;p&gt;If you set up the bios correctly, then it should see the USB stick first on boot up.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I picked Server with GUI as I want gnome desktop. I also added some optional products as shown on the right.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;eno1-network-port&quot;&gt;ENO1 network port&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;allocating-disk-partitions&quot;&gt;Allocating Disk Partitions&lt;/h1&gt;

&lt;p&gt;As mentioned above I had already created the partitions I wanted. You can do it here too. There are plenty of resources you can find about using the installer to do it. Here I’m just assigning the mount points to each partition and to the LVM.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac5.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that the /boot/efi partition is standard type EFI format, and /boot is standard type XFS format.&lt;/p&gt;

&lt;p&gt;Swap is, well, swap format but is on the LVM partition. I made it twice the size of my 8GB of memory. Seems to be debatable how big it should be but 16GB is trivial.&lt;/p&gt;

&lt;p&gt;The rest go on the LVM partition along with swap and are XFS. I probably overallocated / at 30GB, but I’m putting the /u01 directory there for the Oracle binaries and anything else I install. I’m slightly paranoid about running out of root space from past experience. Your mileage may vary. /home is 500GB because I have it available and sometimes you just need somewhere to put stuff.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac6.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac7.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac8.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac9.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac10.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;finish-the-install&quot;&gt;Finish the Install&lt;/h1&gt;

&lt;p&gt;I gave myself an administrator account I can “sudo” from.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0031_installorac11.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then let the install rip.&lt;/p&gt;

&lt;p&gt;When it finishes and wants to reboot, select that and give it a moment to shut down before pulling the USB stick. If you do it too soon, the shutdown hangs. If you it too late, it comes up on the installer boot image again. Either way just turn the machine off at that point. Pull the USB and it should boot to “grub” which will have the Unbreakable Linux kernel first in the boot list, with the RHL kernel next, a recovery image, and then the Windows image. You can rearrange the order later with “grubby” or directly in the grub config file. There are plenty of resources about that.&lt;/p&gt;

&lt;p&gt;Remember the way I’ve done this, if I pull the plug out on the Lacie driver, the original boot loader on the internal drive should be picked up. This is as safe as it can be as long as I don’t ever do anything to that original internal Windows drive.&lt;/p&gt;

&lt;p&gt;I booted from the unbreakable linux kernel and came up in gnome desktop, logged in as myself, then went to the Applications menu on the top left to get a gnonme-terminal and did:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo su -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I’m ready to rock.&lt;/p&gt;

&lt;h1 id=&quot;software-updates&quot;&gt;Software Updates&lt;/h1&gt;

&lt;p&gt;From gnome desktop ran &lt;em&gt;Applications/System Tools/Software Updates&lt;/em&gt;. Eventually it came up with 59 updates. Installed them. Rebooted.&lt;/p&gt;

&lt;h2 id=&quot;xterm&quot;&gt;xterm&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo su - 
yum -y install xterm # Now I can start X on another machine, ssh into this one and launch an xterm 
# could use gnome-terminal but I'm old school.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;add-epel-repository&quot;&gt;Add EPEL repository&lt;/h2&gt;

&lt;p&gt;Put the following lines in /etc/yum.repos.d/epel-yum-ol7.repo&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[ol7\_epel\] name=Oracle Linux $releasever EPEL ($basearch) 
baseurl=http://yum.oracle.com/repo/OracleLinux/OL7/developer\_EPEL/$basearch/ 
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle 
gpgcheck=1 
enabled=1

\[root@linux2 ~\]# yum repolist 
Loaded plugins: langpacks, ulninfo 
ol7\_epel | 2.7 kB 00:00:00 
(1/3): ol7\_epel/x86\_64/updateinfo | 6.1 kB 00:00:00 
(2/3): ol7\_epel/x86\_64/group | 365 kB 00:00:02 
(3/3): ol7\_epel/x86\_64/primary\_db | 17 MB 00:01:01 
repo id repo name status 
ol7\_UEKR4/x86\_64 Latest Unbreakable Enterprise Kernel Release 4 for Oracle Linux 7Server (x86\_64) 96 
ol7\_epel/x86\_64 Oracle Linux 7Server EPEL (x86\_64) 31,547 
ol7\_latest/x86\_64 Oracle Linux 7Server Latest (x86\_64) 18,516 
repolist: 50,159
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;ntfs-3g&quot;&gt;ntfs-3g&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# yum -y install ntfs-3g 
...
Complete!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point the File Manager/Media Manager could mount the NTFS filesystem from the 3TB USB disk drive named “My Drive” on /media/run/*my login id*/something… A “df” command showed me the device was /dev/sdc1. There are other ways to find the device. In any case I dismounted that automatic drive using file manager and put the following entry into /&lt;em&gt;etc/fstab&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/dev/sdc1   /mnt/g  ntfs-3g context=&quot;system\_u:object\_r:samba\_share\_t:s0&quot;,defaults,uid=nobody,gid=nobody 0   0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The context stuff may be required for samba, but that could have been a side effect of my experiment with security profiles. In any case it didn’t hurt anything, and with Secure Linux environment stuff you have to have that attribute for samba to share it. Also notice I’m mapping the username/groupname of all files on this drive as “nobody”. The default is “root” which is messy. Remember I am going to make this a public share.&lt;/p&gt;

&lt;p&gt;Now create the mount point and mount it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# mkdir /mnt/g 
\[root@linux2 ~\]# mount /mnt/g 
\[root@linux2 ~\]# ls -ld /mnt/g 
drwxrwxrwx. 1 nobody nobody 20480 May 5 11:47 /mnt/g 
\[root@linux2 ~\]#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;samba&quot;&gt;samba&lt;/h2&gt;

&lt;p&gt;This reference was helpful: &lt;a href=&quot;https://oracle-base.com/articles/linux/linux-samba-configuration&quot;&gt;https://oracle-base.com/articles/linux/linux-samba-configuration&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 lee\]# yum -y install samba 
Loaded plugins: langpacks, ulninfo 
Package samba-4.10.4-10.el7.x86\_64 already installed and latest version 
Nothing to do 
\[root@linux2 lee\]# service smb start 
Redirecting to /bin/systemctl start smb.service 
\[root@linux2 lee\]# chkconfig smb on 
Note: Forwarding request to 'systemctl enable smb.service'. 
Created symlink from /etc/systemd/system/multi-user.target.wants/smb.service to /usr/lib/systemd/system/smb.service. 
\[root@linux2 lee\]#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next/ I put the following in &lt;em&gt;/etc/samba/smb.conf&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[global\] 
workgroup = WORKGROUP 
netbios name = linux2 
security = user 
server string = Samba Server %v 
map to guest = bad user

\[g\] 
path = /mnt/g 
writable = yes 
browsable = yes 
guest ok = yes 
# guest account = lee 
# guest only = yes 
public = yes 
read only = no 
create mask = 0775 
directory mask = 0664

\[root@linux2 lee\]# service smb restart 
Redirecting to /bin/systemctl restart smb.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But it still doesn’t work. The reason is the Linux firewall.&lt;/p&gt;

&lt;h2 id=&quot;firewall&quot;&gt;Firewall&lt;/h2&gt;

&lt;p&gt;I’m going to add the samba ports, but while I’m here I’ll also add Remote Dekstop Protocol ports for the step that follows samba. You’ll need to set your DISPLAY to point to your X server. If you are on gnome desktop, set it to :0.0. But in a window with your login shell or before doing switch user to root, remember to do &lt;em&gt;xhost +&lt;/em&gt; so that the root shell can open the display.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 lee\]# export DISPLAY=192.168.2.162:0.0 
\[root@linux2 lee\]# firewall-config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This brings up a gui for configuring the firewall on Linux. I selected eno1, which is my hardwired network interface, and hit the “Change Zone” button. I changed it from “public” to “home”. Next I added the samba services to home as shown:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0154_installorac1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Notice that was Runtime Configuration. Under Options menu choose “Runtime to Permanent” to save those changes. I also want to add “rdp” protocol so I can add xrdp and get the gnome desktop to my windows machine using Remote Desktop. To do that we have to add the ports which requires editing the Permanent config.&lt;/p&gt;

&lt;p&gt;Change the Configuration: dropdown from Run time to Permanent and go to the Services tab. Hit the “+” at bottom:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0154_installorac2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Add “rdp” and click OK. Then select rdp from the list and click the Add button under Ports:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0154_installorac3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Also add same port with “udp”.&lt;/p&gt;

&lt;p&gt;Select Zones tab. Select “home”. Add the new “rdp” service to home. Note that this is in the Permanent configuration. We will have to restart the service to pick it up. Under options select Reload Firewalld. After that you should be able to select rdp for the home zone. Now do options/Reload Firewalld again and you should see rdp checked for home zone.&lt;/p&gt;

&lt;p&gt;It shouldn’t be needed, but you can restart the daemon to be sure it will come up next time:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 lee\]# service firewalld stop 
Redirecting to /bin/systemctl stop firewalld.service 
\[root@linux2 lee\]# service firewalld start 
Redirecting to /bin/systemctl start firewalld.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I fired firewall-config back up and now I see that under the “home” profile the new “rdp” service is allowed.&lt;/p&gt;

&lt;p&gt;At this point samba is running and exposing my “/mnt/g” NTFS drive as the “g” share. The files on linux are owned by user “nobody”. It’s all read/write and my wife doesn’t yell at me any more about her pictures. It doesn’t matter if I’m booting windows or linux. In both cases using &lt;em&gt;\\ip-address\g&lt;/em&gt; mounts the same drive!!!!!!!!!!!!!!!!&lt;/p&gt;

&lt;p&gt;It is not secure you say. Hey. Ask me if I care.&lt;/p&gt;

&lt;h2 id=&quot;xrdp&quot;&gt;Xrdp&lt;/h2&gt;

&lt;p&gt;I want to use Windows Remote Desktop client to get the gnome desktop on my PC so I’m not sitting in front of the TV to use it. Now that we have the EPEL repository configured, this is not that hard. Well, it was hard until I figured out the firewall issue that we addressed in the prior step, but now it is not hard.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 lee\]# yum -y install xrdp tigervnc-server 
Loaded plugins: langpacks, ulninfo 
Resolving Dependencies 
--&amp;gt; Running transaction check 
---&amp;gt; Package tigervnc-server.x86\_64 0:1.8.0-19.0.1.el7 will be installed 
---&amp;gt; Package xrdp.x86\_64 1:0.9.5-1.el7 will be installed 
--&amp;gt; Processing Dependency: xorgxrdp for package: 1:xrdp-0.9.5-1.el7.x86\_64 
--&amp;gt; Running transaction check 
---&amp;gt; Package xorgxrdp.x86\_64 0:0.2.11-1.0.1.el7 will be installed 
--&amp;gt; Finished Dependency Resolution Dependencies Resolved 
======================================================================================================================================== 
Package Arch Version Repository Size 
======================================================================================================================================== 
Installing: 
tigervnc-server x86\_64 1.8.0-19.0.1.el7 ol7\_latest 216 k 
xrdp x86\_64 1:0.9.5-1.el7 ol7\_epel 412 k 
Installing for dependencies: 
xorgxrdp x86\_64 0.2.11-1.0.1.el7 ol7\_epel 63 k

Transaction Summary 
======================================================================================================================================== 
Install 2 Packages (+1 Dependent package)

Total download size: 691 k 
Installed size: 2.7 M 
Downloading packages: 
(1/3): xorgxrdp-0.2.11-1.0.1.el7.x86\_64.rpm | 63 kB 00:00:02 
(2/3): tigervnc-server-1.8.0-19.0.1.el7.x86\_64.rpm | 216 kB 00:00:03 
(3/3): xrdp-0.9.5-1.el7.x86\_64.rpm | 412 kB 00:00:03 
---------------------------------------------------------------------------------------------------------------------------------------- 
Total 178 kB/s | 691 kB 00:00:03 
Running transaction check 
Running transaction test 
Transaction test succeeded 
Running transaction 
Installing : xorgxrdp-0.2.11-1.0.1.el7.x86\_64 1/3 
Installing : 1:xrdp-0.9.5-1.el7.x86\_64 2/3 
Installing : tigervnc-server-1.8.0-19.0.1.el7.x86\_64 3/3 
Verifying : tigervnc-server-1.8.0-19.0.1.el7.x86\_64 1/3 
Verifying : xorgxrdp-0.2.11-1.0.1.el7.x86\_64 2/3 
Verifying : 1:xrdp-0.9.5-1.el7.x86\_64 3/3

Installed: 
tigervnc-server.x86\_64 0:1.8.0-19.0.1.el7 xrdp.x86\_64 1:0.9.5-1.el7

Dependency Installed: 
xorgxrdp.x86\_64 0:0.2.11-1.0.1.el7

Complete! 
\[root@linux2 lee\]# systemctl start xrdp 
\[root@linux2 lee\]# netstat -antup |grep xrdp 
tcp 0 0 0.0.0.0:3389 0.0.0.0:\* LISTEN 9515/xrdp 
tcp 0 0 127.0.0.1:3350 0.0.0.0:\* LISTEN 9514/xrdp-sesman 
\[root@linux2 lee\]# systemctl enable xrdp 
Created symlink from /etc/systemd/system/multi-user.target.wants/xrdp.service to /usr/lib/systemd/system/xrdp.service. 
\[root@linux2 lee\]#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now from my laptop I run Windows Remote Desktop client entering the IP address of the Linux server (I’ll get around to naming service later) and it works!!!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0154_installorac4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We may get a secondary authentication request to enter the password for “keyring”. I am living with that for now.&lt;/p&gt;

&lt;p&gt;We also get multiple authentication requests to create a color profile. Pffft. Google search turns up this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://unix.stackexchange.com/questions/417906/authentication-is-required-to-create-a-color-profile&quot;&gt;https://unix.stackexchange.com/questions/417906/authentication-is-required-to-create-a-color-profile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The answer that worked for me was to put the following in the file: &lt;em&gt;/etc/polkit-1/localauthority/50-local.d/color.pkla&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[Allow colord for all users\] 
Identity=unix-user:\* 
Action=org.freedesktop.color-manager.create-device;org.freedesktop.color-manager.create-profile;org.freedesktop.color-manager.delete-device;org.freedesktop.color-manager.delete-profile;org.freedesktop.color-manager.modify-device;org.freedesktop.color-manager.modify-profile 
ResultAny=yes 
ResultInactive=yes 
ResultActive=yes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now when I use Windows Remote Desktop I do not get a secondary prompt for a password (except sometimes for keyring). I have a remote desktop that looks just like the main gnome screen on the actual computer. I can still run an X windows server locally on my laptop and set the DISPLAY variable to have applications come that way, but most of the time just using the rdp connection desktop window works fine.&lt;/p&gt;

&lt;h2 id=&quot;oracle-database-preinstall-19c-10-1el7x86_64rpm&quot;&gt;oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm&lt;/h2&gt;

&lt;p&gt;I have this file on the /mnt/g driver I was just mentioning. I suspect there is a way to get it with yum, but I already have it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\# yum -y localinstall oracle-database-preinstall-19c-1.0-1.el7.x86\_64.rpm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It completed without issues. One of the things it did was create the “oracle” user account and most of the groups listed in the documentation; however, I’m going to install the grid software standalone per the 19c recommendation and so will be walking through that document adding the grid user and any missing groups.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\# yum update -y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Per this link &lt;em&gt;https://oracle-base.com/articles/19c/oracle-db-19c-installation-on-oracle-linux-7&lt;/em&gt;, I ran through the following yum install commands. Some of them were not already present, but many were.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yum install -y bc 
yum install -y binutils 
yum install -y compat-libcap1 
yum install -y compat-libstdc++-33 
#yum install -y dtrace-modules 
#yum install -y dtrace-modules-headers 
#yum install -y dtrace-modules-provider-headers 
yum install -y dtrace-utils 
yum install -y elfutils-libelf 
yum install -y elfutils-libelf-devel 
yum install -y fontconfig-devel 
yum install -y glibc 
yum install -y glibc-devel 
yum install -y ksh 
yum install -y libaio 
yum install -y libaio-devel 
yum install -y libdtrace-ctf-devel 
yum install -y libXrender 
yum install -y libXrender-devel 
yum install -y libX11 
yum install -y libXau 
yum install -y libXi 
yum install -y libXtst 
yum install -y libgcc 
yum install -y librdmacm-devel 
yum install -y libstdc++ 
yum install -y libstdc++-devel 
yum install -y libxcb 
yum install -y make 
yum install -y net-tools # Clusterware 
yum install -y nfs-utils # ACFS 
yum install -y python # ACFS 
yum install -y python-configshell # ACFS 
yum install -y python-rtslib # ACFS 
yum install -y python-six # ACFS 
yum install -y targetcli # ACFS 
yum install -y smartmontools 
yum install -y sysstat 
yum install -y unixODBC
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Oracle Base document also recommends adding a password to the “oracle” account (and I presume “grid” account), setting secure Linux to permissive and stopping the firewall. Since these things are not discussed in the main documentation, I’m unsure about them. I’m going to skip and come back to them if I run into trouble that might be due to them.&lt;/p&gt;

&lt;h1 id=&quot;google-chrome&quot;&gt;Google Chrome&lt;/h1&gt;

&lt;p&gt;Although Firefox is fine, it has some quirks. Sometimes I would rather have Google Chrome. Installing it turned out to be ridiculously difficult, but I finally stumbled through some posts with enough information to figure it out.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 Downloads\]# wget https://dl.google.com/linux/direct/google-chrome-stable\_current\_x86\_64.rpm 
\[root@linux2 Downloads\]# wget http://mirror.centos.org/centos/7/os/x86\_64/Packages/vulkan-filesystem-1.1.97.0-1.el7.noarch.rpm 
\[root@linux2 Downloads\]# wget http://mirror.centos.org/centos/7/os/x86\_64/Packages/vulkan-1.1.97.0-1.el7.x86\_64.rpm 
\[root@linux2 Downloads\]# yum -y install libappindicator-gtk3-12.10.0-13.el7.x86\_64 
\[root@linux2 Downloads\]# yum -y install redhat-lsb libXScrnSaver 
\[root@linux2 Downloads\]# yum -y install liberation-fonts-1.07.2-16.el7.noarch 
\[root@linux2 Downloads\]# yum install -y liberation-narrow-fonts-1.07.2-16.el7.noarch 
\[root@linux2 Downloads\]# yum -y install vulkan-filesystem-1.1.97.0-1.el7.noarch.rpm 
\[root@linux2 Downloads\]# yum install -y vulkan-1.1.97.0-1.el7.x86\_64.rpm 
\[root@linux2 Downloads\]# yum install -y google-chrome-stable\_current\_x86\_64.rpm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;installing-oracle-grid&quot;&gt;Installing Oracle Grid&lt;/h1&gt;

&lt;p&gt;Going through the checklist starting with section 2 (Checking and Configuring Server Hardware for Oracle Database). &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/checking-server-hardware-and-memory-configuration.html&quot;&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/checking-server-hardware-and-memory-configuration.html#GUID-DC04ABB6-1822-444A-AB1B-8C306079439C&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have no issues.&lt;/p&gt;

&lt;p&gt;We already did the preinstall rpm.&lt;/p&gt;

&lt;p&gt;I do not have a support subscription, so skip Ksplice.&lt;/p&gt;

&lt;p&gt;We need to install cvuqdisk, but it is in the grid package. We need to unzip that package. Before we can do that we need to create the “grid” user and some more groups that were not created automatically by the preinstall rpm. Then we can install the cvuqdisk rpm from there. We will come back to this.&lt;/p&gt;

&lt;p&gt;The transparent huge page setting was taken care of for us I presume by the preinstall rpm:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 yum.repos.d\]# cat /sys/kernel/mm/transparent\_hugepage/enabled 
always madvise \[never\]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My disk has the correct scheduler for ASM:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 yum.repos.d\]# cat /sys/block/sda/queue/scheduler 
noop \[deadline\] cfq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;create-users-and-groups-for-grid&quot;&gt;Create users and groups for grid&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# /usr/sbin/groupadd -g 54327 asmdba 
\[root@linux2 ~\]# /usr/sbin/groupadd -g 54328 asmoper 
\[root@linux2 ~\]# /usr/sbin/groupadd -g 54329 asmadmin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The rest of the groups already exist from the preinstall rpm.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# /usr/sbin/useradd -u 54331 -g oinstall -G dba,asmdba,backupdba,dgdba,kmdba,racdba,asmoper,asmadmin grid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Although the “oracle” user already exists, edit /etc/group and add “oracle” to the list of users for the group “asmdba” or you can use “usermod” command as below.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# usermod -a -G asmdba oracle
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the users “grid” and “oracle” edit the .bash_profile and add the following two lines:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# vim ~grid/.bash\_profile ~oracle/.bash\_profile 2 files to edit

umask 022 
export ORACLE\_HOSTNAME=$(hostname)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Add the following lines to &lt;em&gt;/etc/security/limits.conf&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;oracle hard nofile 65536 
oracle soft stack 10240 
oracle hard stack 32768 
grid hard nofile 65536 
grid soft stack 10240 
grid hard stack 32768
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Exit from the user grid/oracle and do “sudo su - grid” again to check the limits:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[grid@linux2 ~\]$ ulimit -Sn 
1024 
\[grid@linux2 ~\]$ ulimit -Hn 
65536 
\[grid@linux2 ~\]$ ulimit -Su 
4096 
\[grid@linux2 ~\]$ ulimit -Hu 
61856 
\[grid@linux2 ~\]$ ulimit -Ss 
10240 
\[grid@linux2 ~\]$ ulimit -Hs 
32768
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;copying-grid-install-software&quot;&gt;Copying grid install software&lt;/h2&gt;

&lt;p&gt;Create a directory for data files. Not sure we will use this and it is in the / partition, so we will see. Later I’m going to create a recovery area in /home.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# mkdir -p /u01/oradata 
\[root@linux2 ~\]# chown grid:oinstall /u01/oradata 
\[root@linux2 ~\]# chmod 775 /u01/oradata
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m following section 10 “Installing Oracle Grid Infrastructure for a Standalone Server with a New Database Installation:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# mkdir -p /u01/app/oracle 
\[root@linux2 ~\]# mkdir -p /u01/app/oraInventory 
\[root@linux2 ~\]# chown -R oracle:oinstall /u01/app/oracle 
\[root@linux2 ~\]# chown -R grid:oinstall /u01/app/oraInventory 
\[root@linux2 ~\]# chmod -R 775 /u01/app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that the instructions say to do this as user “oracle”, but I’m going to be using user “grid”. I still want all of the directories above “grid” to be owned by “oracle”. I have my Oracle grid zip file on /mnt/g/lee. Yours will be somewhere else.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 oracle\]# mkdir -p /u01/app/oracle/product/19.0.0/grid 
\[root@linux2 oracle\]# chmod -R 775 /u01/app/oracle/product 
\[root@linux2 oracle\]# chown -R oracle:oinstall /u01/app/oracle/product 
\[root@linux2 oracle\]# chown grid:oinstall /u01/app/oracle/product/19.0.0/grid 
\[root@linux2 bin\]# mkdir /u01/app/grid 
\[root@linux2 bin\]# chmod 775 /u01/app/grid 
\[root@linux2 bin\]# chown grid:oinstall /u01/app/grid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now switch over to user grid.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[grid@linux2 ~\]$ cd /u01/app/oracle/product/19.0.0/grid 
\[grid@linux2 grid\]$ unzip -q /mnt/g/lee/LINUX.X64\_193000\_grid\_home.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we have the zip file unpacked we can go back to the step where we need the cvq rpm and install that as user “root”:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 rpm\]# cd /u01/app/oracle/product/19.0.0/grid/cv/rpm 
\[root@linux2 rpm\]# CVUQDISK\_GRP=oinstall; export CVUQDISK\_GRP 
\[root@linux2 rpm\]# rpm -iv cvuqdisk-1.0.10-1.rpm 
Preparing packages... 
cvuqdisk-1.0.10-1.x86\_64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configure-asm-disks-with-grid&quot;&gt;Configure asm disks with grid&lt;/h2&gt;

&lt;p&gt;This is done as “root”. Note that if you wind up needing to reinstall after this step (like if you run deinstall/deinstall), you may need to run a command to wipe out the disk header (dd if=/dev/zero of=/dev/sda4 bs=1024 count=100). Be very careful!!!!! But if you just follow the grid deinstall instructions without wiping out the header, and then try again, it won’t find the partitions.&lt;/p&gt;

&lt;p&gt;Something I’m not sure about, but I had trouble otherwise, is to use “udev” to configure the ownership of the device files for my disk partitions. I see hints that suggest ASM should be taking care of this, but I just don’t know. I’m creating the following file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# vim /etc/udev/rules.d/99-oracleasm.rules

KERNEL==&quot;sda4&quot;, SUBSYSTEM==&quot;block&quot;, OWNER=&quot;grid&quot;, GROUP=&quot;oinstall&quot;, MODE=&quot;0660&quot; 
KERNEL==&quot;sda5&quot;, SUBSYSTEM==&quot;block&quot;, OWNER=&quot;grid&quot;, GROUP=&quot;oinstall&quot;, MODE=&quot;0660&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then run the following command to verify the rule:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\# udevadm test $(udevadm info --query=path --name=sda4)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then to reload the rules and trigger them:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# udevadm control --reload-rules 
\[root@linux2 ~\]# udevadm trigger 
\[root@linux2 ~\]# ls -lal /dev/sda\* 
brw-rw----. 1 root disk 8, 0 May 7 23:53 /dev/sda 
brw-rw----. 1 root disk 8, 1 May 7 23:53 /dev/sda1 
brw-rw----. 1 root disk 8, 2 May 7 23:53 /dev/sda2 
brw-rw----. 1 root disk 8, 3 May 7 23:53 /dev/sda3 
brw-rw----. 1 grid oinstall 8, 4 May 7 23:53 /dev/sda4 
brw-rw----. 1 grid oinstall 8, 5 May 7 23:53 /dev/sda5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That will hold up across reboots.&lt;/p&gt;

&lt;p&gt;Now to label the partitions. We are back to using root.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 rpm\]# export ORACLE\_HOME=/u01/app/oracle/product/19.0.0/grid 
\[root@linux2 rpm\]# export ORACLE\_BASE=/tmp 
\[root@linux2 rpm\]# cd /u01/app/oracle/product/19.0.0/grid/bin 
\[root@linux2 bin\]# fdisk -l # to find the partition name of my ASM allocation 
##### CAREFUL!!!! 
\[root@linux2 bin\]# ./asmcmd afd\_label DATA1 /dev/sda4 --init 
\[root@linux2 bin\]# ./asmcmd afd\_label DATA2 /dev/sda5 --init 
\[root@linux2 bin\]# ./asmcmd afd\_lslbl /dev/sda4 
-------------------------------------------------------------------------------- 
Label Duplicate Path 
================================================================================ 
DATA1 /dev/sda4 
\[root@linux2 bin\]# ./asmcmd afd\_lslbl /dev/sda5 
-------------------------------------------------------------------------------- 
Label Duplicate Path 
================================================================================ 
DATA2 /dev/sda5 
\[root@linux2 bin\]#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we switch to user “grid” (sudo su - grid). Recall that we already updated the profile setting environment as directed. If you get a message&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;_ERROR: Unable to verify the graphical display setup. This application requires X display. Make sure that xdpyinfo exist under PATH variable_
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;then you have two choices. You can run “xhost +” in a terminal of your original login user to allow X11 clients run by other users to connect to your gnome desktop (which I’m displaying via remote desktop protocol on my laptop), or you can set the DISPLAY variable to point to your local Xserver where you have done “xhost +” or started without authentication. Either way we need to set the DISPLAY variable. I echo the DISPLAY variable in a terminal window I launched from gnome and see “:11.0”. I’ll use that.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[grid@linux2 grid\]$ pwd /u01/app/oracle/product/19.0.0/grid 
\[grid@linux2 grid\]$ export DISPLAY=:11.0 
\[grid@linux2 grid\]$ ./gridSetup.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Oops! The grid installer does not display correctly in my remote desktop client. Drats! It is probably an xresource or colormap thing or something. I don’t feel the urge to try to figure that out right now. OK, plan B. I run my local VcXsrv X11 server on my PC which I started with the “Disable access control” checked. Now I set my DISPLAY variable to point there and try again:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[grid@linux2 grid\]$ export DISPLAY=192.168.2.162:0.0 
\[grid@linux2 grid\]$ ./gridSetup.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This takes a little while to prepare and give you the next screen. You can look in the /tmp directory and below for logs if you get worried. I found mine in &lt;em&gt;/tmp/GridSetupActions2020-05-07_08-37-33AM/gridSetupActions2020-05-07_08-37-33AM.out&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I’m doing a standalone Server (Oracle Restart).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It eventually gives me this window showing the partitions I labeled with asmcmd. I’m chosing “External” redundancy because I have no need of mirroring on this setup (and no separate disk to put it on). I select my disks. I was going to try to use ASM Filter Driver, but with two partitions instead of one (or perhaps something else I did since I last tried it), it tells me it is not supported. Based on other posts I’ve seen, it looked like trouble anyway.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I don’t need separate passwords and am not worried about the password strength here so it scolds me.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m not registering with any cloud.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m happy with the user ids it finds.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The next screen shows the “grid_base” directory that if you keep it will cause trouble with oraInventory. Change this to the /u01/app/grid directory that we created in preparation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here is same screen with the directory name corrected.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If it suggests creating an oraInventory beneath the product directory, that will be bad. That is going to be under oracle base for the database installation. Make sure it is under /u01/app instead. If you had left the default grid base location earlier, you would. have had the wrong directory here which causes trouble on the Oracle install. It is fixable later though.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m going to run the configuration scripts manually rather than try to give it a way to do it with root.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac9.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac10.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Press the Install button.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac11.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac12.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 bin\]# /u01/app/oraInventory/orainstRoot.sh 
Changing permissions of /u01/app/oraInventory. 
Adding read,write permissions for group. 
Removing read,write,execute permissions for world. 
# 
Changing groupname of /u01/app/oraInventory to oinstall. 
The execution of the script is complete.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now the tricky one. Cross fingers. I got past the part I failed on OEL 8 and also on 7.8 when I had a greater than 2TB partition for ASM. This runs a while.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[\[root@linux2 bin\]# /u01/app/oracle/product/19.0.0/grid/root.sh 
Performing root user operation. 
# 
The following environment variables are set as: 
ORACLE\_OWNER= grid 
ORACLE\_HOME= /u01/app/oracle/product/19.0.0/grid 
# 
Enter the full pathname of the local bin directory: \[/usr/local/bin\]: 
The contents of &quot;dbhome&quot; have not changed. No need to overwrite. 
The contents of &quot;oraenv&quot; have not changed. No need to overwrite. 
The contents of &quot;coraenv&quot; have not changed. No need to overwrite. 
# 
# 
Creating /etc/oratab file... 
Entries will be added to the /etc/oratab file as needed by 
Database Configuration Assistant when a database is created 
Finished running generic part of root script. 
Now product-specific root actions will be performed. 
Using configuration parameter file: /u01/app/oracle/product/19.0.0/grid/crs/install/crsconfig\_params 
The log of current session can be found at: /u01/app/grid/crsdata/linux2/crsconfig/roothas\_2020-05-08\_10-41-18AM.log 
LOCAL ADD MODE 
Creating OCR keys for user 'grid', privgrp 'oinstall'.. 
Operation successful. 
LOCAL ONLY MODE 
Successfully accumulated necessary OCR keys. 
Creating OCR keys for user 'root', privgrp 'root'.. 
Operation successful. 
CRS-4664: Node linux2 successfully pinned. 
2020/05/08 10:42:17 CLSRSC-330: Adding Clusterware entries to file 'oracle-ohasd.service' 
# 
linux2 2020/05/08 10:45:13 /u01/app/grid/crsdata/linux2/olr/backup\_20200508\_104513.olr 724960844 
2020/05/08 10:45:15 CLSRSC-327: Successfully configured Oracle Restart for a standalone server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hooray! Click OK back on the installer window.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac13.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This takes a long time. I see something going on in /tmp/hsperfdata_grid.&lt;/p&gt;

&lt;p&gt;And now I get a failure.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac14.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Oracle Net Listener Startup: 
The information provided for this listener is currently in use by other software on this computer. 
Check the trace file for details: /u01/app/oracle/product/19.0.0/grid\_base/cfgtoollogs/netca/trace\_OraGI19Home1-2005079AM2649.log 
Oracle Net Services configuration failed. The exit code is 1 
Oracle Net Configuration Assistant failed.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And in the trace log file mentioned above I find:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[main\] \[ 2020-05-07 10:01:43.447 EDT \] \[ConfigureListener.isPortFree:1292\] Creating ServerSocket on Port:1521, IP Address: linux2.localdomain/92.242.140.21 
\[main\] \[ 2020-05-07 10:01:43.447 EDT \] \[ConfigureListener.isPortFree:1328\] Cannot assign requested address (Bind failed) 
java.net.PlainSocketImpl.socketBind(Native Method) 
java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387) 
java.net.ServerSocket.bind(ServerSocket.java:375) 
java.net.ServerSocket.&amp;lt;init&amp;gt;(ServerSocket.java:237) 
oracle.net.ca.ConfigureListener.isPortFree(ConfigureListener.java:1294) 
oracle.net.ca.ConfigureListener.startOrStopListener(ConfigureListener.java:1380) 
oracle.net.ca.ConfigureListener.typicalConfigure(ConfigureListener.java:369) 
oracle.net.ca.SilentConfigure.performSilentConfigure(SilentConfigure.java:212) 
oracle.net.ca.InitialSetup.&amp;lt;init&amp;gt;(NetCA.java:4325) 
oracle.net.ca.NetCA.main(NetCA.java:460) 
\[main\] \[ 2020-05-07 10:01:43.447 EDT \] \[ConfigureListener.isPortFree:1341\] Returning is Port 1521 free: false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After a little googling and remembering advice I had read in the Oracle Base document, I found I had failed to update /etc/hosts with the name of this computer and the IP address. I added it to /etc/hosts:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 bin\]# cat /etc/hosts 
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 
192.168.2.81 linux2.localdomain linux2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After this it completed the net configuration assistant, but failed the ASM assistant:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac15.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[FATAL\] \[DBT-30002\] Disk group DATA creation failed. 
ORA-15018: diskgroup cannot be created 
ORA-15031: disk specification '/dev/sda5' matches no disks 
ORA-15025: could not open disk &quot;/dev/sda5&quot; 
ORA-27041: unable to open file 
Automatic Storage Management Configuration Assistant failed.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some google searching leads me to believe that we have to manually assign ownership of the device files for the ASM partitions. Wish that had been mentioned in the documentation (or if it was, that I had noticed it). I proceeded with the “udev” rule to change the ownership of the device files as mentioned above. You should have already done that. At that point the /dev/sda[45] files were changed to grid:oinstall which takes care of it.&lt;/p&gt;

&lt;p&gt;Pressed continue and it finished. Yeaaaaaa!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0058_installorac16.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;testing-grid-installation&quot;&gt;Testing Grid Installation&lt;/h1&gt;

&lt;p&gt;The next section in the install document is &lt;em&gt;Testing the Oracle Automatic Storage Management Installation&lt;/em&gt;. I presume the sid I want is +ASM. When I look in the grid home dbs directory I see dbs/init.ora with a db_name=’ORCL’, but in the dbs directory I see some files with ‘+ASM’ in the name. I have zero experience with grid yet, so I’m guessing.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[grid@linux2 grid\]$ export ORACLE\_HOME=/u01/app/oracle/product/19.0.0/grid 
\[grid@linux2 grid\]$ export ORACLE\_SID=+ASM 
\[grid@linux2 grid\]$ $ORACLE\_HOME/bin/asmcmd lsdg 
State Type Rebal Sector Logical\_Sector Block AU Total\_MB Free\_MB Req\_mir\_free\_MB Usable\_file\_MB Offline\_d 
MOUNTED EXTERN N 512 512 4096 4194304 2097152 2097040 0 2097040
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Must have guessed right. That looks like both my 1 TB partitions were combined into a single group as expected. The ASM instance must be runing (a ps listing shows processes with +ASM in the name as well as a tnslsnr).&lt;/p&gt;

&lt;p&gt;I can also fire up $ORACLE_HOME_/bin/asmca_ and look around.&lt;/p&gt;

&lt;p&gt;The next section of the document informs us that every time we do any OS upgrades we need to relink the grid executables and provides some instructions. That’s beyond the scope for now, but try to remember this for the future.&lt;/p&gt;

&lt;p&gt;I won’t be using ACFS which is storage on stuff other than the database. And I don’t have access to support, so I don’t have any patches to apply.&lt;/p&gt;

&lt;p&gt;That’s it for grid.&lt;/p&gt;

&lt;h1 id=&quot;installing-oracle-database&quot;&gt;Installing Oracle Database&lt;/h1&gt;

&lt;p&gt;I already have to directories listed in step 2 of running the wizard, so all I need to do is create dbhome_1. I &lt;em&gt;sudo su - oracle&lt;/em&gt;, and unpack my zip file.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[oracle@linux2 19.0.0\]$ mkdir -p /u01/app/oracle/product/19.0.0/dbhome\_1 
\[oracle@linux2 19.0.0\]$ cd /u01/app/oracle/product/19.0.0/dbhome\_1 
\[oracle@linux2 dbhome\_1\]$ unzip -q /mnt/g/lee/LINUX.X64\_193000\_db\_home.zip 
\[oracle@linux2 dbhome\_1\]$ export DISPLAY=192.168.2.162:0.0 # you may be going local, but you will still need to set it and perhaps do xhost+ first
\[oracle@linux2 dbhome\_1\]$ ./runInstaller 
Launching Oracle Database Setup Wizard...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ll be creating the single instance database.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After looking around a little bit I decided the “Desktop class” installation would be too limiting. I want to be able to play here which includes connecting from other machines. I picked “Server class”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Again, because I’m using this database to learn about big iron stuff, I picked Enterprise Edition. I don’t “need” it, but I want to be able to explore it. YMMV.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that the next section describes an issue I subsequently fixed in the grid install process. I had made a mistake on the oracle inventory location. I leave this here in case you find yourself in the same situation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Hmm. Is this a permission issue?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 19.0.0\]# ls -l 
total 8 
drwxr-xr-x. 68 oracle oinstall 4096 May 7 19:27 dbhome\_1 
drwxr-x---. 85 root oinstall 4096 May 7 09:18 grid 
drwxr-xr-x. 7 grid oinstall 84 May 7 10:32 grid\_base 
drwxrwx---. 5 grid oinstall 92 May 7 19:20 oraInventory 
\[root@linux2 19.0.0\]# chown grid grid 
\[root@linux2 19.0.0\]# chmod 775 \* 
\[root@linux2 19.0.0\]# ls -l 
total 8 
drwxrwxr-x. 68 oracle oinstall 4096 May 7 19:27 dbhome\_1 
drwxrwxr-x. 85 grid oinstall 4096 May 7 09:18 grid 
drwxrwxr-x. 7 grid oinstall 84 May 7 10:32 grid\_base 
drwxrwxr-x. 5 grid oinstall 92 May 7 19:20 oraInventory
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I had to exit the installer and start it again. This time when I got to the step for Oracle base it did not complain immediately, but did give a popup when I continued.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I see plenty of questions about this but don’t see an explanation I can follow other than the grid install was supposed to put the inventory in /u01/app/oraInventory. I’m not sure why it did not the first time (In my latest attempt as documented in this blog I correctly located it at /u01/app/oraInventory). I found this link on how to move oraInventory (&lt;a href=&quot;https://logic.edchen.org/how-to-move-central-inventory/&quot;&gt;https://logic.edchen.org/how-to-move-central-inventory/&lt;/a&gt;). I changed /etc/OraInst.loc:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 19.0.0\]# cat /etc/oraInst.loc 
#inventory\_loc=/u01/app/oracle/product/19.0.0/oraInventory 
inventory\_loc=/u01/app/oraInventory 
inst\_group=oinstall
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The existing files are owned by grid, but since the group is oinstall we should be good. You may need to make sure any existing log directories are group writeable first. As user grid::&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[grid@linux2 oraInventory\]$ cp -rp /u01/app/oracle/product/19.0.0/oraInventory /u01/app 
\[grid@linux2 oraInventory\]$ pwd 
/u01/app/oraInventory 
\[grid@linux2 oraInventory\]$ cp /etc/oraInst.loc . 
\[grid@linux2 grid\]$ cd/u01/app/oracle/product/19.0.0/grid
\[grid@linux2 grid\]$ cp /etc/oraInst.loc .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now back as oracle user I try the installer again. We are past that hurdle and continuing. Note that since doing this I did deinstall of both the database and grid and started over.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m customizing my database name and also going to create it as a container database. Total overkill, but I want to play with pluggable database containers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It defaults to NOT enabling automatic memory management because there is more than 4GB of memory. Not sure I understand that. Note that you have to move the slider to get close to 4.096 before you can edit the text box. I don’t know why.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Although I have no plans to use anything but ascii, I’ll go with the default because that is what most large corporations do, I think.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac9.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’ll go ahead and install the sample schemas. They often make a good base for doing examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac10.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I will be using ASM.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac11.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;No cloud for me.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac12.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I will not be putting anything on this database I can’t afford to lose. In fact, I expect to tear it down and start over many times. But I tried it without recovery being set and wound up with an error later. I’ll enable recovery.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac13.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It found my ASM disc group.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac14.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Yes, I’m a bad person with passwords on this database.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac15.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac16.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Just like with the grid install, I prefer to run the root scripts myself separately.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac17.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac18.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac19.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 ~\]# /u01/app/oracle/product/19.0.0/dbhome\_1/root.sh 
Performing root user operation. 
# 
The following environment variables are set as: 
ORACLE\_OWNER= oracle 
ORACLE\_HOME= /u01/app/oracle/product/19.0.0/dbhome\_1 
# 
Enter the full pathname of the local bin directory: \[/usr/local/bin\]: 
The contents of &quot;dbhome&quot; have not changed. No need to overwrite. 
The contents of &quot;oraenv&quot; have not changed. No need to overwrite. 
The contents of &quot;coraenv&quot; have not changed. No need to overwrite. 
# 
Entries will be added to the /etc/oratab file as needed by Database Configuration Assistant when a database is created
Finished running generic part of root script. 
Now product-specific root actions will be performed. 
Oracle Trace File Analyzer (TFA - Standalone Mode) is available at : /u01/app/oracle/product/19.0.0/dbhome\_1/bin/tfactl 
# 
Note : 
1. tfactl will use TFA Service if that service is running and user has been granted access 
2. tfactl will configure TFA Standalone Mode only if user has no access to TFA Service or TFA is not installed 
# 
\[root@linux2 ~\]#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pressed the OK button on the root dialog.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac20.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Running the command “iotop -o -d 5” I can see the database is writing to disk. At least that is better than just watching the installer sit there with “In Progress”. As a side note the Applications/System Tools/System Monitor app on gnome is not very impressive. There has to be a better tool.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac21.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I found some logs I could watch&lt;/p&gt;

&lt;p&gt;[ oracle@linux2 cfgtoollogs]$ pwd /u01/app/oracle/product/19.0.0/dbhome_1/cfgtoollogs 
   [oracle@linux2 cfgtoollogs]$ tail -f oracle.server_2020-05-08_09-30-54-AM.log 
   INFO: Skipping line: 38% complete 
   INFO: Skipping line: 38% complete 
   INFO: Skipping line: 42% complete 
   INFO: Skipping line: 42% complete 
   INFO: Skipping line: 45% complete 
   INFO: Skipping line: 45% complete 
   INFO: Skipping line: 48% complete 
   INFO: Skipping line: 48% complete 
   INFO: Skipping line: Completing Database Creation 
   INFO: Skipping line: Completing Database Creation&lt;/p&gt;

&lt;p&gt;Finally it finishes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/050920_0129_installorac22.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But Firefox complains with that address - something about security. I found links to solutions having to do with XDB or private certificates or some such. I spent several hours looking without success. Someone with access to support might be able to figure it out. I might come back to that. I don’t need it though.&lt;/p&gt;

&lt;p&gt;The database is installed and functioning. Shutting down and starting up Linux brings the database down and up gracefully.&lt;/p&gt;

&lt;h1 id=&quot;grid-and-oracle-login-profiles&quot;&gt;grid and oracle login profiles&lt;/h1&gt;

&lt;p&gt;I configured the .bash_profile of the oracle user with the following lines.&lt;/p&gt;

&lt;p&gt;export PATH umask 022 export ORACLE_HOSTNAME=$(hostname) export ORAENV_ASK=NO export ORACLE_SID=leedb . oraenv -s&lt;/p&gt;

&lt;p&gt;Now I can do:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sqlplus / as sysdba 
# or 
sqlplus system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;without having to set any more variables.&lt;/p&gt;

&lt;p&gt;I configured the .bash_profile of the grid user with the following lines:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;umask 022 
export ORACLE\_HOSTNAME=$(hostname) 
export ORACLE\_HOME=/u01/app/oracle/product/19.0.0/grid 
export ORACLE\_SID=&quot;+ASM&quot; 
PATH=${PATH}:${ORACLE\_HOME}/bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now when I log in as user grid I can issue srvctl commands. See below.&lt;/p&gt;

&lt;h1 id=&quot;starting-and-stopping&quot;&gt;Starting and Stopping&lt;/h1&gt;

&lt;p&gt;If you have not already done so, read &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/configuring-automatic-restart-of-an-oracle-database.html&quot;&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/configuring-automatic-restart-of-an-oracle-database.html#GUID-EA0104A2-B256-4866-9C8A-9B69F8D0F24E&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you use sqlplus to shutdown your database you will be in for a surprise when you try to start it again. You can recover. After thinking I had lost the diskgroup I found I could get it all going again by logging in as “grid” and running a few commands:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;srvctl start diskgroup -diskgroup data 
srvctl start database -db leedb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In general you can let the normal Linux shudtown and startup take care of things. When you are ready, you need to learn about srvctl which is kind of the point of setting up the gird “restart” structure.&lt;/p&gt;

&lt;h1 id=&quot;sqldeveloper&quot;&gt;Sqldeveloper&lt;/h1&gt;

&lt;p&gt;You may be able to use yum to get sqldeveloper, but I downloaded the rpm. Before installing it you need the java sdk.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\[root@linux2 jvm\]# yum install java-1.8.0-openjdk-devel ... 
\[root@linux2 jvm\]# yum install sqldeveloper-19.1.0.094.2042.noarch.rpm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It installs in /usr/local/bin. The first time you run sqldeveloper it prompts for the java sdk location. There are some soft links that take care of adjusting for upgrades. Answer with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/usr/lib/jvm/java-openjdk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which will point to the latest and greatest that you just installed.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;This was much, much harder than it should have been. I spent more hours on this than I am willing to admit. Granted, I was learning some Linux things I didn’t know and some Oracle administration things I didn’t know as I was going, but even with that background this was a very hard slog.&lt;/p&gt;

&lt;p&gt;Access to Oracle support would have made it a little easier, but not that much. I do not know why it is such a cluster (pun intended).&lt;/p&gt;

&lt;p&gt;I hope a few people might find this useful.&lt;/p&gt;
</description>
        <pubDate>Sat, 09 May 2020 01:00:00 -0400</pubDate>
        <link>https://lee-lindley.github.io/linux/oracle/2020/05/09/install-linux-oracle.html</link>
        <guid isPermaLink="true">https://lee-lindley.github.io/linux/oracle/2020/05/09/install-linux-oracle.html</guid>
        
        <category>oracle</category>
        
        <category>linux</category>
        
        <category>installation</category>
        
        <category>windows 10</category>
        
        
        <category>linux</category>
        
        <category>oracle</category>
        
      </item>
    
  </channel>
</rss>
