<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Oracle & More]]></title><description><![CDATA[Oracle & More]]></description><link>https://blog.neilfernandez.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 07 Apr 2026 20:14:36 GMT</lastBuildDate><atom:link href="https://blog.neilfernandez.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[SQLcl Projects - Dynamic Property File Location Definition]]></title><description><![CDATA[Building on the previous topic of leveraging Liquibase properties to modify APEX installations, we will delve into a companion technique. This one enables users to define as many property files as they wish in the artifact to set it when deploying. T...]]></description><link>https://blog.neilfernandez.com/sqlcl-projects-dynamic-property-file-location-definition</link><guid isPermaLink="true">https://blog.neilfernandez.com/sqlcl-projects-dynamic-property-file-location-definition</guid><category><![CDATA[sqlcl-projects]]></category><category><![CDATA[sqlcl]]></category><category><![CDATA[orclapex]]></category><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Wed, 04 Jun 2025 04:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749127502588/4712ac97-da09-4854-a62e-0bf639625b69.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Building on the previous topic of leveraging Liquibase properties to modify APEX installations, we will delve into a companion technique. This one enables users to define as many property files as they wish in the artifact to set it when deploying. This technique comes in handy when dealing with APEX details such as workspace, app_id, and alias which differ between schemas/environments. If you are restricted to only able to work on one database where you seperate instances by schema, this will help you!  </p>
<p>If you are storing private information such as credentials and such, I will provide another method later once supported where you can define a location outside of the artifact. This can include some cloud storage, or just anywhere you define inside a container perhaps.</p>
<h3 id="heading-step-1-define-multiple-property-files"><strong>Step 1: Define Multiple Property Files</strong></h3>
<p>As a first step, under the <code>utils</code> folder, you can freely create as many property files as you wish. I made a new folder <code>utils/properties</code>. Each file will incorporate a different configuration and can be directly tied to specific environments such as staging or production. This automatically eliminates the hassle of having to modify property details manually and regenerate a seperate artifact whenever a deployment between environments is done.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749085629890/22903c77-c938-43ac-8618-64eab19fc214.png" alt class="image--center mx-auto" /></p>
<p>Example of <code>utils/properties/stage.properties</code></p>
<pre><code class="lang-bash">apex.200.workspace:NEIFERNA
apex.200.appId:400
</code></pre>
<h3 id="heading-step-2-modify-installsql"><strong>Step 2: Modify install.sql</strong></h3>
<p>Next, make modifications on your <code>install.sql</code> script. To ensure that the defaults parameter reads from the appropriate property file, add <code>-defaults-file &amp;DEFAULTS_FILE</code> into the <code>lb update</code> line. We are going to define this later.</p>
<pre><code class="lang-bash">prompt <span class="hljs-string">"Installing/updating schemas"</span>
lb update -<span class="hljs-built_in">log</span> -changelog-file releases/main.changelog.xml -search-path <span class="hljs-string">"."</span> -defaults-file &amp;DEFAULTS_FILE
</code></pre>
<h3 id="heading-step-3-generate-artifact"><strong>Step 3: Generate Artifact</strong></h3>
<p>Call <code>project gen-artifact</code>. This scenario only allows you to interact with property files that live inside of your artifact. Personally I am not storing sensitive data in here. Just app IDs and workspace names.</p>
<p>For reference, my current APEX app changeset values without modification are as follows:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749085988430/35d6b3d4-ae14-42b7-8f14-b86f964f29f7.png" alt class="image--center mx-auto" /></p>
<p>App ID: 200 and Workspace: DEMO are my default values.</p>
<h3 id="heading-step-4-connect-to-target"><strong>Step 4: Connect to Target</strong></h3>
<p>Following successful artifact generation, connect to your target environment.</p>
<h3 id="heading-step-5-define-default-property-file-location"><strong>Step 5: Define Default Property File location</strong></h3>
<p>Next step is to specify your defaults file. This can be achieved by calling <code>DEFINE DEFAULTS_FILE=utils/properties/stage.properties</code> within SQLcl.</p>
<h3 id="heading-step-6-deploy-your-project"><strong>Step 6: Deploy Your Project</strong></h3>
<p>Finally, deploy your project by calling <code>project deploy</code>. The deployment process will use the artifacts generated earlier, applying the configurations you defined in your default property file to your APEX installation.</p>
<h3 id="heading-full-runthrough">Full runthrough</h3>
<p>Here is an example of the full set of commands after modifying <code>install.sql</code> and adding your property files.</p>
<pre><code class="lang-bash">SQL&gt; project gen-artifact -name demo
Your artifact has been generated demo-1.0.0.zip
SQL&gt; conn neiferna/apex@//localhost:2307/ga_release_242
Connected.
SQL&gt; DEFINE DEFAULTS_FILE=utils/properties/stage.properties
SQL&gt; project deploy -file artifact/demo-1.0.0.zip 
Starting the migration...
Running Changeset: releases/apex/f200/f200.xml::INSTALL_200::SQLCL-Generated
PL/SQL procedure successfully completed.
</code></pre>
<h3 id="heading-future">Future</h3>
<p>This is not ideal for storing sensitive information such as credentials, secrets, etc. Once supported I will show how to define property files that live outside of the artifact. This can be useful for pulling from a vault, somewhere in a container, etc. Hope this helps till then. Cheers!</p>
]]></content:encoded></item><item><title><![CDATA[SQLcl Projects - Using Liquibase properties to modify your APEX installation]]></title><description><![CDATA[Prereqs

Latest SQLcl

Existing SQLcl Project with an APEX app


Note
On average, I prefer to keep the same Schema name, APEX Application ID, Workspace Name, and Workspace ID across environments and have separate databases. I understand that this isn...]]></description><link>https://blog.neilfernandez.com/sqlcl-projects-using-liquibase-properties-to-modify-your-apex-installation</link><guid isPermaLink="true">https://blog.neilfernandez.com/sqlcl-projects-using-liquibase-properties-to-modify-your-apex-installation</guid><category><![CDATA[sqlcl]]></category><category><![CDATA[orclapex]]></category><category><![CDATA[sqlcl-projects]]></category><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Fri, 07 Mar 2025 15:34:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741360717606/454c7cbb-d83f-4294-b425-b38d5511c20b.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-prereqs">Prereqs</h2>
<ul>
<li><p>Latest <a target="_blank" href="https://www.oracle.com/database/sqldeveloper/technologies/sqlcl/download/">SQLcl</a></p>
</li>
<li><p>Existing <a target="_blank" href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/24.4/sqcug/project-command.html#GUID-0511D1C7-4CB7-48B6-824D-55C739198F40">SQLcl Project</a> with an APEX app</p>
</li>
</ul>
<h2 id="heading-note">Note</h2>
<p>On average, I prefer to keep the same Schema name, APEX Application ID, Workspace Name, and Workspace ID across environments and have separate databases. I understand that this isn't the case for everyone, and even with separate instances, you may still have valid reasons to change the Workspace name, Application ID, etc.</p>
<h2 id="heading-dynamic-apex-install-demo">Dynamic APEX Install Demo</h2>
<p>If you have been using SQLcl Projects, you might have noticed some interesting things in your APEX application's autogenerated changeset file: <code>dist/releases/apex/f&lt;xxx&gt;/f&lt;xxx&gt;.xml</code>. Below is an example of my APEX application export (f100.sql) and staged changes for my SQLcl Project.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">databaseChangeLog</span>
        <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.liquibase.org/xml/ns/dbchangelog"</span>
        <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>
        <span class="hljs-attr">xmlns:n0</span>=<span class="hljs-string">"http://www.oracle.com/xml/ns/dbchangelog-ext"</span>
        <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">changeSet</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"INSTALL_100"</span> <span class="hljs-attr">author</span>=<span class="hljs-string">"SQLCL-Generated"</span>  <span class="hljs-attr">failOnError</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">runAlways</span>=<span class="hljs-string">"true"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">n0:runApexScript</span> <span class="hljs-attr">objectName</span>=<span class="hljs-string">"install"</span> <span class="hljs-attr">objectType</span>=<span class="hljs-string">"SCRIPT"</span> <span class="hljs-attr">ownerName</span>=<span class="hljs-string">"ADMIN"</span> <span class="hljs-attr">sourceType</span>=<span class="hljs-string">"STRING"</span>  <span class="hljs-attr">relativeToChangelogFile</span>=<span class="hljs-string">"true"</span> &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">n0:source</span>&gt;</span>&lt;![CDATA[
 declare 
 -- sqlcl version      = 24.4.1.0 
 -- override_schema    = ${apex.100.schema}
 -- override_alias     = ${apex.100.alias}
 -- override_workspace = ${apex.100.workspace}
 -- override_app_id    = ${apex.100.appId}

 l_app_id varchar2(255) := q'[100]';  
 l_workspace varchar2(255) := q'[DEMO]'; 

 l_override_workspace varchar2(255) := q'[${apex.100.workspace}]'; 
 l_override_schema varchar2(255) := q'[${apex.100.schema}]'; 
 l_override_alias varchar2(255) := q'[${apex.100.alias}]'; 
 l_override_app_id varchar2(255) := q'[${apex.100.appId}]'; 
 l_generate_offset boolean := false;

 function get_prop_default(p_prop_name varchar2)
 return varchar2 is
 begin  
   return '${apex.100.' || p_prop_name || '}';
 end;  

 begin  
  apex_application_install.clear_all(); 

  -- set workspace 
     if (l_override_workspace != l_workspace and l_override_workspace != get_prop_default('workspace')) then
      apex_application_install.set_workspace(l_override_workspace);
  else 
      apex_application_install.set_workspace(l_workspace);         
  end if; 
  commit;  

  -- set app id 
  if (l_override_app_id != l_app_id and l_override_app_id != get_prop_default('appId')) then
     apex_application_install.set_application_id(l_override_app_id);
    l_generate_offset := true;
  else  
      apex_application_install.set_application_id(l_app_id);
  end if; 

 if (l_override_schema != get_prop_default('schema')) then
    apex_application_install.set_schema(l_override_schema);
  end if;

  -- set alias 
  if (l_override_alias != get_prop_default('alias')) then
    apex_application_install.set_application_alias(l_override_alias);
   l_generate_offset := true;
 end if;

  -- generate offset if necessary
 if (l_generate_offset)  then
    apex_application_install.generate_offset();
 end if;
end;  
/
-- sqlcl_checksum  1740173850613
@f100.sql
        ]]&gt;<span class="hljs-tag">&lt;/<span class="hljs-name">n0:source</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">n0:runApexScript</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">changeSet</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">databaseChangeLog</span>&gt;</span>
</code></pre>
<p>You can now set the following properties without having to hand write custom code:</p>
<ul>
<li><p><code>override_schema    = ${apex.&lt;app_id&gt;.schema}</code></p>
</li>
<li><p><code>override_alias     = ${apex.&lt;app_id&gt;.alias}</code></p>
</li>
<li><p><code>override_workspace = ${apex.&lt;app_id&gt;.workspace}</code></p>
</li>
<li><p><code>override_app_id    = ${apex.&lt;app_id&gt;.appId}</code></p>
</li>
</ul>
<h2 id="heading-how-to-set-property-values">How to set property values</h2>
<p>In Liquibase there is the concept of properties that you can set multiple ways:</p>
<ol>
<li><p>Via a properties file</p>
</li>
<li><p>SQLcl command line argument</p>
</li>
<li><p>Directly in the change log (XML/JSON/YAML)</p>
</li>
<li><p>Environment variables</p>
</li>
</ol>
<p>I am going to show point 1 today and may show the other 3 points in the future.</p>
<h3 id="heading-properties-file">Properties File</h3>
<p>Here is my repo after creating my SQLcl Project, exporting, staging, and creating a release. I have one APEX application in my DEV environment with the app ID 100. This will be its unique identifer.</p>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2025/03/Screenshot-2025-03-06-at-2.44.41-PM.png" alt /></p>
<p>Next, create a properties file. I created one named <code>liquibase.properties</code> and placed in my <code>dist/utils/</code> folder.</p>
<pre><code class="lang-java">apex.<span class="hljs-number">100.</span>alias = NEIL_TEST_APP
apex.<span class="hljs-number">100.</span>appId = <span class="hljs-number">12345678</span>
apex.<span class="hljs-number">100.</span>workspace = TEST
apex.<span class="hljs-number">100.</span>schema = WKSP_TEST
</code></pre>
<p>Next modify <code>dist/install.sql</code> and add the <code>-defaults-file utils/liquibase.properties</code> option to your <code>lb update</code> command. This will tell Liquibase to use that file to drive our custom parameter/property values. Below is in example of the modified section in our <code>dist/install.sql</code></p>
<pre><code class="lang-java">-- Kick off Liquibase
prompt <span class="hljs-string">"Installing/updating schemas"</span>
lb update -log -changelog-file releases/main.changelog.xml -search-path <span class="hljs-string">"."</span> -defaults-file utils/liquibase.properties
</code></pre>
<p>Next generate your artifact. Remember the assumption was that you have already done all the previous steps of exporting, staging, and creating release.</p>
<pre><code class="lang-bash">neiferna@neiferna-mac sqlcl_apex_bug % sql /nolog


SQLcl: Release 24.4 Production on Fri Mar 07 09:54:46 2025

Copyright (c) 1982, 2025, Oracle.  All rights reserved.

SQL&gt; project gen-artifact -version 1.0
Your artifact has been generated demo-1.0.zip
</code></pre>
<p>Next it is time to deploy!</p>
<pre><code class="lang-bash">SQL&gt; conn -name demo
Connected.
SQL&gt; project deploy -file artifact/demo-1.0.zip
Starting the migration...
</code></pre>
<p>Voila, it got installed into a different workspace, with a different alias, and a new Application ID.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741360342718/fc46018e-8e17-4679-86a9-54d846459c71.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-future">Future</h2>
<p>I have another blog post coming soon that will show you how to add custom scripts to make the installer for APEX installation parameters even more configurable. This will include switching and modifying authentication schemes using substitution values and custom scripts. The example above includes some built-in parameters, and there may be more flexibility in the future.</p>
]]></content:encoded></item><item><title><![CDATA[SQLcl: Export and Import data with newlines]]></title><description><![CDATA[Situation
Recently I was given a seed data file that was generated from SQLcl with a bunch of INSERT statements, and it worked fine in SQL Developer when ran but would fail in SQLcl with the following:
SQL> @DEMO_DATA_TABLE.sql

Error starting at lin...]]></description><link>https://blog.neilfernandez.com/sqlcl-load-unload-with-newlines</link><guid isPermaLink="true">https://blog.neilfernandez.com/sqlcl-load-unload-with-newlines</guid><category><![CDATA[sqlcl]]></category><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Tue, 23 Jan 2024 13:45:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741310211523/9bfd053d-d292-4550-8fbd-9746048551fa.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-situation">Situation</h2>
<p>Recently I was given a seed data file that was generated from SQLcl with a bunch of INSERT statements, and it worked fine in SQL Developer when ran but would fail in SQLcl with the following:</p>
<pre><code class="lang-bash">SQL&gt; @DEMO_DATA_TABLE.sql

Error starting at line : 3 File @ file:/Users/neiferna/bug/DEMO_DATA_TABLE.sql In <span class="hljs-built_in">command</span> - Insert into DEMO (ID,DISPLAY_NAME) values (2,<span class="hljs-string">'multiline feedback

Error at Command Line : 3 Column : 46 File @ file:/Users/neiferna/bug/DEMO_DATA_TABLE.sql Error report - SQL Error: ORA-01756: quoted string not properly terminated 01756. 00000 - "quoted string not properly terminated" *Cause: A quoted string must be terminated with a single quote mark ('</span>). *Action: Insert the closing quote and retry the statement.

More Details : https://docs.oracle.com/error-help/db/ora-01756/ 1* Insert into DEMO (ID,DISPLAY_NAME) values (2,<span class="hljs-string">'multiline feedback SP2-0734: unknown command beginning "2'</span>)...<span class="hljs-string">" - rest of line ignored.

SP2-0223: No lines in SQL Buffer.SQL&gt;</span>
</code></pre>
<p>It is common to track seed/sample data scripts, as opposed dump files. These scripts are used in releases and easy to maintain for the developer and tracked.</p>
<p>I went ahead and created a base case for the failure and realized that it was failing on any statements which had newlines in the INSERT. Here is a sample file named <code>DEMO_DATA_TABLE.sql</code> base case:</p>
<pre><code class="lang-bash">REM INSERTING into DEMO SET DEFINE OFF; 

Insert into DEMO (ID,DISPLAY_NAME) values (2,<span class="hljs-string">'multiline feedback

1

2'</span>);
</code></pre>
<p>/</p>
<h2 id="heading-solution">Solution</h2>
<p>Shoutout to <a target="_blank" href="https://hashnode.com/@martindsouza">Martin D'Souza</a> for the tip. All you need to do is the following:</p>
<p><code>set sqlblanklines on;</code></p>
<p>Problem solved.</p>
<p>Here is an example of the working insert with newlines:</p>
<pre><code class="lang-bash">SQL&gt; <span class="hljs-built_in">set</span> sqlblanklines on; 

SQL&gt; @DEMO_DATA_TABLE.sql

1 row inserted.

SQL&gt; commit;

Commit complete.

SQL&gt;
</code></pre>
<h2 id="heading-other-format-considerations">Other format considerations</h2>
<ul>
<li><p>CSV</p>
</li>
<li><p>JSON and other formats not supported by LOAD</p>
</li>
</ul>
<h3 id="heading-csv">CSV</h3>
<p>Using SQLcl unload/load with CSV yielded a similar error due to the newlines:</p>
<pre><code class="lang-bash">SQL&gt; <span class="hljs-built_in">set</span> loadformat csv SQL&gt; unload demo

format csv

column_names on delimiter , enclosures <span class="hljs-string">""</span> encoding UTF8 row_terminator default

** UNLOAD Start ** at 2024.01.16-17.09.51 Export Separate Files to /Users/neiferna/bug DATA TABLE DEMO File Name: /Users/neiferna/bug/DEMO_DATA_TABLE.csv Number of Rows Exported: 1 ** UNLOAD End ** at 2024.01.16-17.09.52 SQL&gt; truncate table demo;

Table DEMO truncated.

SQL&gt; load table demo DEMO_DATA_TABLE.csv

Load data into table WKSP_PRODUCTREFERENCES.DEMO

csv column_names on delimiter , enclosures <span class="hljs-string">""</span> double off encoding UTF8 row_limit off row_terminator default skip_rows 0 skip_after_names

<span class="hljs-comment">#ERROR Insert failed for row 1 </span>
<span class="hljs-comment">#ERROR --Line contains invalid enclosed character data or delimiter at position 21 </span>
<span class="hljs-comment">#ERROR Row 1 data follows: 2,"multiline feedback </span>
<span class="hljs-comment">#ERROR Insert failed for row 5 </span>
<span class="hljs-comment">#ERROR --Line contains invalid enclosed character data or delimiter at position 2 </span>
<span class="hljs-comment">#ERROR Row 5 data follows: "2, </span>
<span class="hljs-comment">#INFO Number of rows processed: 5 </span>
<span class="hljs-comment">#INFO Number of rows in error: 2 </span>
<span class="hljs-comment">#INFO No rows committed </span>

WARNING: Processed with errors
</code></pre>
<p>I played around with different delimeter and loadformat settings but still no luck. I will update once I hear a solution on this one.</p>
<h3 id="heading-jsonother-formats">JSON/Other formats</h3>
<p>You can UNLOAD to JSON and some other formats, but there is no native way to LOAD back in yet. Only CSV/DELIMITED can. And INSERT format can also just be ran since it is a .sql file that is produced.</p>
<pre><code class="lang-bash">SQL&gt; <span class="hljs-built_in">help</span> <span class="hljs-built_in">set</span> loadformat

  default        : Load format properties <span class="hljs-built_in">return</span> to default values 
  csv            : comma separated values
  delimited      : (CSV synonym) delimited format, comma separated values by default  
  html           : UNLOAD only, Hypertext Markup Language
  insert         : UNLOAD only, SQL insert statements
  json           : UNLOAD only, Java Script Object Notation
  json-formatted : UNLOAD only, <span class="hljs-string">"pretty"</span> formatted JSON
  loader         : UNLOAD only, Oracle SQLLoader format
  t2             : UNLOAD only, T2 Metrics
  xml            : UNLOAD only, Extensible Markup Language
  xls            : Excel 97- Excel 2003 Workbook format
  xlsx           : Excel format <span class="hljs-keyword">for</span> version 2007 or higher

<span class="hljs-built_in">where</span> options represents the following clauses:
  COLUMN_NAMES|COLUMNNAMES|NAMES {ON|OFF}  : Header row with column names
  DELIMITER {separator}  : Delimiter separating fields <span class="hljs-keyword">in</span> the record 
  DOUBLE [OFF]  : (Import only) Right enclosures embedded <span class="hljs-keyword">in</span> the data are doubled.  OFF indicates right enclosures embedded <span class="hljs-keyword">in</span> the data are not doubled and single embedded right enclosures can lead to unexpected results.
  ENCLOSURES {enclosures|OFF}  : Optional left and right enclosure.
    OFF indicates no enclosures
    If 1 character is specified, sets left and right enclosure to this value.
    If 2 or more characters are specified, sets left to the first character,
    the right to the second character and ignores the remaining characters.
    To <span class="hljs-built_in">set</span> multiple character enclosures, use Set ENCLOSURE_LEFT and ENCLOSURE_RIGHT
  ENCODING {encoding|OFF|<span class="hljs-string">""</span>}  : Encoding of load file.  OFF and <span class="hljs-string">""</span> reset to default encoding <span class="hljs-keyword">for</span> environment
  LEFT|ENCLOSURE_LEFT|ENCLOSURELEFT {enclosure|OFF} : Set a 1 or more character left enclosure.  If no ENCLOSURE_RIGHT is specified, it is used <span class="hljs-keyword">for</span> both left and right.
    OFF indicates no enclosures
    RIGHT|ENCLOSURE_RIGHT|ENCLOSURERIGHT {enclosure|OFF} : Set a 1 or more character right enclosure.
    OFF indicates no right enclosure
  ROW_LIMIT|ROWLIMIT|LIMIT} {number_of_rows|OFF|<span class="hljs-string">""</span>}  : Max number of rows to <span class="hljs-built_in">read</span>, including header.  OFF and <span class="hljs-string">""</span> <span class="hljs-built_in">set</span> to not <span class="hljs-built_in">limit</span>.
  SKIP|SKIP_ROWS|SKIPROWS {number_of_rows|OFF|<span class="hljs-string">""</span>} : Number of rows to skip
    [[SKIP_AFTER_NAMES|SKIPAFTERNAMES|AFTER]|[SKIP_BEFORE_NAMES|SKIPBEFORENAMES|BEFORE]]  : Skip the rows before or after the (header) Column Names row
  TERM|ROW_TERMINATOR {terminator|<span class="hljs-string">""</span>|DEFAULT|CR|CRLF|LF} : Character(s) indicating end of row.  If the file contains standard line end characters, the line_end does not need to be specified.
    <span class="hljs-string">""</span> or DEFAULT specifies the default (any standard terminator) <span class="hljs-keyword">for</span> LOAD
    <span class="hljs-string">""</span> or DEFAULT specifies the environment default <span class="hljs-keyword">for</span> UNLOAD
    CRLF specifies WINDOWS terminator, generally <span class="hljs-keyword">for</span> UNLOAD
    LF specifies UNIX terminator, generally <span class="hljs-keyword">for</span> UNLOAD
    CR specifies MAC terminator, generally <span class="hljs-keyword">for</span> UNLOAD

Examples:  
   <span class="hljs-built_in">set</span> loadformat delimited 
       7369,<span class="hljs-string">"SMITH"</span>,<span class="hljs-string">"CLERK"</span>,7902,17-DEC-80,800,,20,5555555555554444 
   <span class="hljs-built_in">set</span> loadformat delimited enclosures &lt;&gt; line_end {eol} 
       7369,&lt;SMITH&gt;,&lt;CLERK&gt;,7902,17-DEC-80,800,,20,5555555555554444{eol} 
   <span class="hljs-built_in">set</span> loadformat delimited 
       7369,<span class="hljs-string">"SMITH"</span>,<span class="hljs-string">"CLERK"</span>,7902,17-DEC-80,800,,20,5555555555554444 
   <span class="hljs-built_in">set</span> loadformat default  (restore default settings) 
       7369,<span class="hljs-string">"SMITH"</span>,<span class="hljs-string">"CLERK"</span>,7902,17-DEC-80,800,,20,5555555555554444 
   <span class="hljs-built_in">set</span> loadformat xls 
       Set load format to old Excel versions (Excel 97-2003)
   <span class="hljs-built_in">set</span> loadformat xlsx 
       Set load format to newer Excel versions (Excel 2007 or higher)
</code></pre>
<p>Quite often developers track seed/sample data, and JSON may be preferred for some. Maybe this will be a native load format eventually that can be loaded in with LOAD - but for now here is <strong>one</strong> of the many methods to load that file in. In the past I have used a bash script that takes the JSON file locally, builds an array of strings .sql file that eventually created a clob and you can work with that clob in SQL. But below is something small and a little simpler to get going.</p>
<p>Here is an example of what gets produced from <code>set loadformat json</code><br />Example JSON format from UNLOAD (with 1 row)</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"results"</span>: [
    {
      <span class="hljs-attr">"columns"</span>: [
        { <span class="hljs-attr">"name"</span>: <span class="hljs-string">"ID"</span>, <span class="hljs-attr">"type"</span>: <span class="hljs-string">"NUMBER"</span> },
        { <span class="hljs-attr">"name"</span>: <span class="hljs-string">"DISPLAY_NAME"</span>, <span class="hljs-attr">"type"</span>: <span class="hljs-string">"VARCHAR2"</span> }
      ],
      <span class="hljs-attr">"items"</span>: [{ <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">"display_name"</span>: <span class="hljs-string">"multiline feedback\n\n1\n\n2"</span> }]
    }
  ]
}
</code></pre>
<h3 id="heading-inserting-using-json">Inserting using JSON</h3>
<p>There are various ways to get a file into the database. Examples: PUT into a RESTful ORDS service and store in a table, etc. The following example shows how to use an OCI bucket as a temporary data storage to load the file from you desktop then read in the database.</p>
<ol>
<li>Load the export into a Bucket in Object Storage (examples can be found <a target="_blank" href="https://www.thatjeffsmith.com/archive/2020/10/sqlcl-version-20-3-and-oci-support">here</a>)</li>
</ol>
<pre><code class="lang-bash">SQL&gt; <span class="hljs-built_in">set</span> loadformat json 
SQL&gt; oci profile PERSONAL Region <span class="hljs-built_in">set</span> to: us-ashburn-1 OCI Profile <span class="hljs-built_in">set</span> to PERSONAL Transfer method <span class="hljs-built_in">set</span> to oci 
SQL&gt; cs https://objectstorage.us-ashburn-1.oraclecloud.com/n//b/demo-image-upload DBMS_CLOUD Credential: Not Set OCI Profile: PERSONAL Transfer Method: oci URI as specified: https://objectstorage.us-ashburn-1.oraclecloud.com/n//b/demo-image-upload 
SQL&gt; unload demo CS

DBMS_CLOUD Credential: Not Set OCI Profile: PERSONAL Transfer Method: oci URI as specified: https://objectstorage.us-ashburn-1.oraclecloud.com/n//b/demo-image-upload Final URI: https://objectstorage.us-ashburn-1.oraclecloud.com/n//b/demo-image-upload

format json

** UNLOAD Start ** at 2024.01.22-23.17.10 Export Separate Files to https://objectstorage.us-ashburn-1.oraclecloud.com/n//b/demo-image-upload DATA TABLE DEMO File Name: DEMO_DATA_TABLE_2.json Number of Rows Exported: 1 ** UNLOAD End ** at 2024.01.22-23.17.11
</code></pre>
<ol start="2">
<li>Pull the file with <code>apex_web_service.make_rest_request</code> and insert with the help of <code>JSON_TABLE</code></li>
</ol>
<pre><code class="lang-sql"><span class="hljs-keyword">declare</span> l_clob <span class="hljs-keyword">clob</span>; 
<span class="hljs-keyword">begin</span> 
    l_clob := apex_web_service.make_rest_request( 
        p_url =&gt; <span class="hljs-string">'https://objectstorage.us-ashburn-1.oraclecloud.com/n//b/demo-image-upload/DEMO_DATA_TABLE_2.json'</span>, 
        p_http_method =&gt; <span class="hljs-string">'GET'</span>
    );

<span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> demo( <span class="hljs-keyword">id</span>, display_name ) <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> json_table(l_clob, <span class="hljs-string">'$.results[].items[]'</span> <span class="hljs-keyword">columns</span>( <span class="hljs-keyword">id</span> <span class="hljs-built_in">number</span> <span class="hljs-keyword">path</span> <span class="hljs-string">'$.id'</span>, display_name <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">4000</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.display_name'</span> ) ); <span class="hljs-keyword">end</span>;
</code></pre>
<p>You just need to modify the columns to fit your needs, and I am sure this insert/select can be dynamically created based on the <code>results.columns</code> in the JSON.</p>
]]></content:encoded></item><item><title><![CDATA[APEX spinner download indicator using Vanilla JavaScript (async/await)]]></title><description><![CDATA[There are plenty of good resources explaining how to create custom download links in APEX so I will skip over that part and list a couple here:
- https://oracle-base.com/articles/misc/apex-tips-file-download-from-a-button-or-link
- https://joelkallma...]]></description><link>https://blog.neilfernandez.com/spinner-while-download</link><guid isPermaLink="true">https://blog.neilfernandez.com/spinner-while-download</guid><category><![CDATA[orclapex]]></category><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Wed, 08 Mar 2023 04:34:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741310214389/b6f579ba-6014-4646-82dc-f8514a18d5e0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are plenty of good resources explaining how to create custom download links in APEX so I will skip over that part and list a couple here:</p>
<p>- <a target="_blank" href="https://oracle-base.com/articles/misc/apex-tips-file-download-from-a-button-or-link">https://oracle-base.com/articles/misc/apex-tips-file-download-from-a-button-or-link</a></p>
<p>- <a target="_blank" href="https://joelkallman.blogspot.com/2014/03/yet-another-post-how-to-link-to.html">https://joelkallman.blogspot.com/2014/03/yet-another-post-how-to-link-to.html</a></p>
<p>These examples go into good detail on how to generate a custom link to download a file. But what about how to indicate that a file is downloading if it is taking a long time in the backend?</p>
<h2 id="heading-problem">Problem</h2>
<p>Some custom download processes can take quite sometime depending on what the process is doing in the backend (generation/business logic). It can be quite confusing to the end user that a process is occurring and for them to even know that they are waiting on downloading a file at all if this is the case.</p>
<p>If it takes a long time to run the process that ultimately downloads the file - then just setting a spinner on page submit or via apex.server.process will not necessarily achieve the desired outcome. Usually the apex engine is killed when you dedicate a page/process to download the file, and subsequently you can not easily signal that the download process is done and to take action upon that to kill a spinner if you had one going.</p>
<p>There are a few plugins out there that you can use in combination with each other as well. Lets just assume that you have a link already set up to download your file and that it kicks off some custom process to do so. Here is a quick Vanilla JavaScript approach to achieve a spinner while the download process is occurring and to remove it when it is done.</p>
<h2 id="heading-solution">Solution</h2>
<ol>
<li>Declare the following download function:</li>
</ol>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2023/03/Screenshot-2023-03-07-at-10.23.06-PM.png" alt /></p>
<p>Page Properties -&gt; Function and Global Variable Declaration</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> download = <span class="hljs-keyword">async</span> (url, filename) =&gt; {
  <span class="hljs-comment">// add spinner</span>
  <span class="hljs-keyword">var</span> lSpinner$ = apex.util.showSpinner();
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fetch(url);
  <span class="hljs-keyword">const</span> blob = <span class="hljs-keyword">await</span> data.blob();
  <span class="hljs-keyword">const</span> objectUrl = URL.createObjectURL(blob);
  <span class="hljs-keyword">const</span> link = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"a"</span>);

  <span class="hljs-comment">// set link download url/filename</span>
  link.setAttribute(<span class="hljs-string">"href"</span>, objectUrl);
  link.setAttribute(<span class="hljs-string">"download"</span>, filename);
  link.style.display = <span class="hljs-string">"none"</span>;

  <span class="hljs-comment">// add link and download file</span>
  <span class="hljs-built_in">document</span>.body.appendChild(link);
  link.click();
  URL.revokeObjectURL(blob);
  link.remove();

  <span class="hljs-comment">// remove spinner</span>
  lSpinner$.remove();
};
</code></pre>
<p>This is utilizing async and await to make us wait for the promise to return a result. Majority of the waiting done is on the fetch here in my case as my process running takes over 5 seconds before it even sends a file back to the client. This allows us to confidently set a spinner before we wait for the result, and then remove the spinner after all of our code is done running.</p>
<p>2. Declare a <strong>Dynamic Action</strong> on button click to download the file</p>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2023/03/Screenshot-2023-03-07-at-10.28.02-PM-5.png" alt /></p>
<p>Dynamic Action -&gt; Code</p>
<p>My download link runs a process that takes over 5 seconds to generate my file. I am using page items to hold the url and filename, so I used this:</p>
<p><code>download(apex.items.P1_DOWNLOAD_URL.value, apex.items.P1_FILENAME.value)</code></p>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2023/03/Monosnap-screencast-2023-03-07-22-47-02.gif" alt /></p>
<p>That is it. This concept can be used also to fire other events after a custom file generation process is done as well and not just to get a spinner. Hope this helps any of you that have long running download processes so that the end user is aware that something is occurring. Cheers!</p>
]]></content:encoded></item><item><title><![CDATA[Quick Tip: json_mergepatch]]></title><description><![CDATA[In honor of #JoelKallmanDay. I wanted to share a quick tip. PL/SQL Object Types for JSON have now been around for a bit, since Oracle Database 12c. Ever since then, this has been the primary way I tend to work with JSON. The main PL/SQL JSON object t...]]></description><link>https://blog.neilfernandez.com/quick-tip-jsonmergepatch</link><guid isPermaLink="true">https://blog.neilfernandez.com/quick-tip-jsonmergepatch</guid><category><![CDATA[orclapex]]></category><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Tue, 12 Oct 2021 04:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741321260303/6da48f38-bc5a-4b66-80e1-861206bcf07a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In honor of #JoelKallmanDay. I wanted to share a quick tip. PL/SQL Object Types for JSON have now been around for a bit, since Oracle Database 12c. Ever since then, this has been the primary way I tend to work with JSON. The main PL/SQL JSON object types are <code>JSON_ELEMENT_T</code>, <code>JSON_OBJECT_T</code>, <code>JSON_ARRAY_T</code>, and <code>JSON_SCALAR_T</code>. Oracle Database 21c has introduced the new <code>JSON</code> data type, but the <code>JSON_OBJECT_T</code> constructor supports the new <code>JSON</code> data type and I still plan to use PL/SQL JSON object types.</p>
<p>One scenario I have ran into more than once is the need to merge two json objects. Maybe we have JSON stored in the DB, and are getting a PUT to update that row and the JSON in that row. Sometimes we need to convert it to <code>JSON_OBJECT_T</code> to handle some business logic before merging. Then we try merging - for example:</p>
<pre><code class="lang-javascript">p1 = {
    <span class="hljs-string">"firstName"</span>: <span class="hljs-string">"Neal"</span>,
    <span class="hljs-string">"address"</span>: <span class="hljs-string">"123 Nevermind Drive"</span>,
    <span class="hljs-string">"animals"</span>: {<span class="hljs-string">"dog1"</span>:<span class="hljs-string">"Fido"</span>}
}

p2 = {
    <span class="hljs-string">"firstName"</span>: <span class="hljs-string">"Neil"</span>,
    <span class="hljs-string">"lastName"</span>: <span class="hljs-string">"Fernandez"</span>,
    <span class="hljs-string">"animals"</span>: {<span class="hljs-string">"dog2"</span>:<span class="hljs-string">"Fetch"</span>}
}

<span class="hljs-comment">/* expected merge output when merging p2 into p1

{
    "firstName": "Neil",
    "lastName": "Fernandez"
    "address": "123 Nevermind Drive",
    "animals":{"dog1":"Fido","dog2":"Fetch"}
}
*/</span>
</code></pre>
<p>In the past I have looped through the keys in <code>p2</code>, see if it exists in <code>p1</code>, and if it does we replace it, if it doesn't we add it. Something along the lines of this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">declare</span>
    l_p1 json_object_t;
    l_p2 json_object_t;
    l_p2_key_list  json_key_list;
<span class="hljs-keyword">begin</span>
    <span class="hljs-comment">-- Assuming you are dealing with JSON_OBJECT_T already</span>
    l_p1 := json_object_t.parse(<span class="hljs-string">'{
        "firstName": "Neal",
        "address": "123 Nevermind Drive",
        "animals": {"dog1":"Fido"}
    }'</span>);

    l_p2 := json_object_t.parse('{
        "firstName": "Neil",
        "lastName": "Fernandez",
        "animals": {"dog2":"Fetch"}
    }');

    l_p2_key_list := l_p2.get_keys;

    &lt;&lt; p2_keys_loop &gt;&gt;
    for i in 1 .. l_p2_key_list.count loop
        l_p1.put(l_p2_key_list(i), l_p2.get(l_p2_key_list(i)));   
    <span class="hljs-keyword">end</span> <span class="hljs-keyword">loop</span>;

    dbms_output.put_line(l_p1.to_clob);
<span class="hljs-keyword">end</span>;

<span class="hljs-comment">-- Output</span>
<span class="hljs-comment">/*
{
    "address":"123 Nevermind Drive",
    "firstName":"Neil",
    "lastName":"Fernandez",
    "animals":{"dog2":"Fetch"}
}
*/</span>
</code></pre>
<p>There is one obvious issue though. This is not really a true merge. Take a look at the <code>animals</code> nested object. If there are nested objects or arrays, it actually is just replacing/inserting those objects or arrays with this method and not actually merging. This only works if you do not have nested arrays or objects. There is more work to be done to make this work properly for that scenario.</p>
<h2 id="heading-there-is-an-easier-way-jsonmergepatch"><strong>There is an easier way: JSON_MERGEPATCH</strong></h2>
<p>JSON_MERGEPATCH has been the easiest way for me to handle merging JSON and it does an actual merge patch. There is no PL/SQL support for direct assignments, so for now you have to <code>select into from dual</code> and convert your PL/SQL JSON Object types into CLOBs before calling it.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">declare</span>
    l_p1 json_object_t;
    l_p2 json_object_t;
    l_p1_clob clob;
    l_p2_clob clob;
    l_merge clob;
    l_merge_json json_object_t;
<span class="hljs-keyword">begin</span>
    <span class="hljs-comment">-- Assuming you are dealing with JSON_OBJECT_T already</span>
    l_p1 := json_object_t(<span class="hljs-string">'{
        "firstName": "Neal",
        "address": "123 Nevermind Drive",
        "animals": {"dog1":"Fido"}
    }'</span>);

    l_p2 := json_object_t('{
        "firstName": "Neil",
        "lastName": "Fernandez",
        "animals": {"dog2":"Fetch"}
    }');

    <span class="hljs-comment">-- Convert to clob to use in SQL</span>
    l_p1_clob := l_p1.to_clob();
    l_p2_clob := l_p2.to_clob();

    <span class="hljs-comment">-- Merge using SQL</span>
    <span class="hljs-comment">-- There is no PL/SQL support for direct assignments using the JSON_MERGEPATCH function.</span>
    <span class="hljs-keyword">select</span> json_mergepatch(l_p1_clob, l_p2_clob)
    <span class="hljs-keyword">into</span> l_merge
    <span class="hljs-keyword">from</span> dual;

    dbms_output.put_line(l_merge);

    <span class="hljs-comment">-- Back to JSON_OBJECT_T after merging to continue any other logic</span>
    l_merge_json := json_object_t(l_merge);
<span class="hljs-keyword">end</span>;
<span class="hljs-comment">-- Output</span>
<span class="hljs-comment">/*
{
    "firstName":"Neil",
    "address":"123 Nevermind Drive",
    "animals":{"dog1":"Fido","dog2":"Fetch"},
    "lastName":"Fernandez"
}
*/</span>
</code></pre>
<p>That's it. Hope this can be helpful to anyone dealing with JSON in the Oracle Database that runs into a similar situation.</p>
<p>Here is some more info on JSON_MERGEPATCH: <a target="_blank" href="https://oracle-base.com/articles/19c/json_mergepatch-19c">https://oracle-base.com/articles/19c/json_mergepatch-19c</a><br />**  </p>
<p>#JoelKallmanDay**</p>
]]></content:encoded></item><item><title><![CDATA[REST Data Sources/Synchronization deployment tips in APEX 20.2]]></title><description><![CDATA[So finally we are using some of APEX 20.2 new features like the cool new Data Synchronization for REST Data Sources. I assume most companies deploy from command line so if you do, here are some pointers we have learned from the process and some work ...]]></description><link>https://blog.neilfernandez.com/rest-data-sourcessynchronization-deployment-tips-in-apex-202</link><guid isPermaLink="true">https://blog.neilfernandez.com/rest-data-sourcessynchronization-deployment-tips-in-apex-202</guid><category><![CDATA[orclapex]]></category><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Fri, 18 Jun 2021 04:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741357484252/3fa3c833-1874-49af-97ef-bd86708a83b0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So finally we are using some of APEX 20.2 new features like the cool new Data Synchronization for REST Data Sources. I assume most companies deploy from command line so if you do, here are some pointers we have learned from the process and some work arounds for issues that we found.</p>
<h2 id="heading-how-to-set-the-remote-servercredentials-different-for-for-deployments-to-other-environments"><strong>How to set the remote server/credentials different for for deployments to other environments.</strong></h2>
<p>You may have a different endpoint you want to sync for your DEV/TEST/PROD environments. Make sure to go to Edit Remote Server and set "Prompt on Install".</p>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2021/06/remote_server-1.png" alt /></p>
<p>Check off Prompt on Install</p>
<p>Only issue is that this will cause an error when deploying from command line as it is expecting user input to set the remote server on install. To fix this, add the following to your deployment script before calling the app export:</p>
<pre><code class="lang-sql">apex_application_install.set_remote_server(
  p_static_id =&gt; 'YOUR_SERVER_STATIC_ID', 
  p_base_url =&gt; 'https://newbase.url:1234/'
);
</code></pre>
<p>If doing this, you may also have different credentials to set. You can do so by calling the following after you have set your security group and everything, but before installing the app is fine.</p>
<pre><code class="lang-sql">apex_credential.set_persistent_credentials (
  p_credential_static_id  =&gt; 'CREDENTIAL',
  p_client_id             =&gt; 'username',
  p_client_secret         =&gt; 'password'
);
</code></pre>
<p>That is all that is needed to have a different set of endpoints and credentials for your environments, without having to manually set them on deployments.</p>
<p>After doing this we noticed that our REST Data Source Synchronization was never enabled in our new environments. The synchronization schedule was there, but the log was gone, and it was not syncing after our deployments. I feel like this is a bug and maybe it is fixed in 21.1, but I have not tested there yet.</p>
<h3 id="heading-how-to-enable-data-synchronization-after-deploying"><strong>How to enable data synchronization after deploying</strong></h3>
<p>There are some calls not yet documented in the official API docs for APEX, and we found this to solve the problem. After deploying, you want to make the following call for each of your data sources if you would like the job that syncs in the background to be enabled.</p>
<pre><code class="lang-sql">apex_rest_source_sync.enable(
  p_application_id   =&gt; 100000,
  p_module_static_id =&gt; 'name_of_rest_data_source'
);
</code></pre>
<p>After calling this, you will see it resume its schedule which you had set, and the job will be running.</p>
<p>If you apply all of the above calls to your release, you should be set to not have to manually change anything after your deployments.</p>
<p>Please let me know if anyone else has ran into this. I could not find anything in the docs on this occurring during deployments/imports, and am wondering if it is fixed, or planned to be fixed in future releases. This may be by design, and would like to know why if that is the case.</p>
]]></content:encoded></item><item><title><![CDATA[Loop through all page items in APEX]]></title><description><![CDATA[This blog has been sitting idle for too long. So starting off the New Year with a quick find!
Here is the problem:

We need to loop through all page items to apply some logic on demand, and preferably we want to do it dynamically.

Taking a look at a...]]></description><link>https://blog.neilfernandez.com/loop-through-all-page-items-in-apex</link><guid isPermaLink="true">https://blog.neilfernandez.com/loop-through-all-page-items-in-apex</guid><category><![CDATA[orclapex]]></category><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Mon, 04 Jan 2021 23:00:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741310211270/4879efa8-a04c-4ddc-8842-a483a5141255.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This blog has been sitting idle for too long. So starting off the New Year with a quick find!</p>
<p>Here is the problem:</p>
<blockquote>
<p>We need to loop through all page items to apply some logic on demand, and preferably we want to do it dynamically.</p>
</blockquote>
<p>Taking a look at <em>apex.page</em> there is a function called <em>forEachPageItem.</em></p>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2021/01/image.png" alt /></p>
<p>Function</p>
<p>This is not documented in <a target="_blank" href="https://apex.oracle.com/jsapi">https://apex.oracle.com/jsapi</a> but it is public. This returns a function. Here is the internal function signature and comment inside of <em>page.js</em> as of APEX 20.2:  </p>
<p>    // For the given form call callback once for each page item in that form. This includes disabled and unchecked items.
    // Page item elements are identified by having a name attribute that is not one of the well known names
    //   input type=image not supported
    // See comments in ajaxSubmit as well
    function forEachPageItem( form$, callback, allElements )</p>
<p>Function Signature</p>
<p>It looks like can you pass a specific form as a parameter, but I am using what they use inside the <em>page.js</em> when the form parameter is null in other spots. This gives us back all page items on the page.</p>
<p>    allItems = apex.page.forEachPageItem;
    allItems( $( "#wwvFlowForm" ), function( el, name ) {
            console.log(el);
            console.log(name);
        }, true );</p>
<p>Solution</p>
<p>Hope this can help someone as the majority of solutions I see online regarding this revolve already looking at the metadata views via PLSQL.</p>
]]></content:encoded></item><item><title><![CDATA[APEX 20.1 Login Screen Upgrade]]></title><description><![CDATA[APEX 20.1 is now live on apex.oracle.com. I took some time to dig around some features, but one that stuck out right away was the new template options for the login screen.

On your login page, go to Template Options:


Login Page Appearence Section
...]]></description><link>https://blog.neilfernandez.com/apex-20-login-screen-upgrade</link><guid isPermaLink="true">https://blog.neilfernandez.com/apex-20-login-screen-upgrade</guid><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Thu, 26 Mar 2020 16:43:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741310214339/d5cf9a24-8563-48b9-8be3-09dd39a23061.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>APEX 20.1 is now live on <a target="_blank" href="https://apex.oracle.com">apex.oracle.com</a>. I took some time to dig around some features, but one that stuck out right away was the new template options for the login screen.</p>
<blockquote>
<p>On your login page, go to Template Options:</p>
</blockquote>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2020/03/Page-Designer-2020-03-26-12-29-07.png" alt /></p>
<p>Login Page Appearence Section</p>
<blockquote>
<p>Change Page Layout -&gt; Split</p>
</blockquote>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2020/03/Page-Designer-2020-03-26-12-31-01-1.png" alt /></p>
<p>Login Page Template Options</p>
<p>Note: The different page backgrounds you can choose are dynamic to the theme roller.</p>
<p>Voila! You have a new slick login page that even has a nice slide in animation. Also check out the new friendly urls 😉</p>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2020/03/login-screen.gif" alt /></p>
]]></content:encoded></item><item><title><![CDATA[iFrame  Developer Toolbar Wrong Page Fix]]></title><description><![CDATA[Some times I have had a client request to use iFrames of other APEX pages. One issue I always have is that when doing this, the developer toolbar points to whatever page is being used in the iFrame.
For example, I declared an iFrame on page 1 of my a...]]></description><link>https://blog.neilfernandez.com/iframe-developer-toolbar-fix</link><guid isPermaLink="true">https://blog.neilfernandez.com/iframe-developer-toolbar-fix</guid><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Wed, 06 Nov 2019 05:42:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741310213923/7d8ff957-753a-4c82-8759-a7cc026f4122.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Some times I have had a client request to use iFrames of other APEX pages. One issue I always have is that when doing this, the developer toolbar points to whatever page is being used in the iFrame.</p>
<p>For example, I declared an iFrame on page 1 of my app which has page 2 of my app as its source:</p>
    <iframe src="&amp;P1_IFRAME_URL.">
    </iframe>

<p>With P1_IFRAME_URL computed as:</p>
<p>    apex_util.prepare_url( 'f?p=' || :APP_ID || ':2:' || :APP_SESSION ||'::NO:::');</p>
<p>So when I run page 1 of my app, my developer toolbar now shows the following:</p>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2019/11/Screen-Shot-2019-11-04-at-9.39.33-PM-3.png" alt /></p>
<p>Developer toolbar is pointing to Page 2 when we are on Page 1</p>
<p>Notice that the developer toolbar shows "Edit Page 2" instead of "Edit Page 1". This has to do with how APEX renders modal dialogs. They are iFrames, and APEX is picking this up as the same. This can get very annoying when editing a page as you lose "Quick Edit" and the "Edit Page" functionality.</p>
<h2 id="heading-here-is-a-quick-fix">Here is a quick fix:</h2>
<ol>
<li>Make a copy of the page template that you want to use and do not unsubscribe your theme.</li>
<li>Edit the template. Remove the #DEVELOPER_TOOLBAR# substitution from the Footer.</li>
</ol>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2019/11/Screen-Shot-2019-11-03-at-9.10.10-PM-2.png" alt /></p>
<p>Modifying the footer of our Standard page template copy</p>
<p>3.   Assign it to the iFrame page.</p>
<p>That is it! Now your developer toolbar will be fine and pointing to the parent page. Just note that you want to do this only for pages that will be strictly used as iFrames, otherwise you lose the developer toolbar for them if they are used as a standalone page. Shout out to <a target="_blank" href="https://twitter.com/rimblas">@jrimblas</a> for showing me this tip!</p>
<p><img src="https://s3-us-east-2.amazonaws.com/neilfernandez-blog/2019/11/Screen-Shot-2019-11-04-at-9.37.54-PM-2.png" alt /></p>
<p>Developer toolbar is now pointing to Page 1 when we are on Page 1</p>
]]></content:encoded></item><item><title><![CDATA[Hello World]]></title><description><![CDATA[I just want to take the time to introduce myself. A little bit about me:
    {
        "name": "Neil Fernandez",
        "current_employer": "Insum Solutions",
        "email": "neilfernandezdev@gmail.com",
        "started_with_apex": "2016",
      ...]]></description><link>https://blog.neilfernandez.com/hello-world</link><guid isPermaLink="true">https://blog.neilfernandez.com/hello-world</guid><dc:creator><![CDATA[Neil Fernandez]]></dc:creator><pubDate>Fri, 01 Nov 2019 01:33:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741310211605/c123cafa-3ba2-4638-bf9d-711e5c845bda.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I just want to take the time to introduce myself. A little bit about me:</p>
<p>    {
        "name": "Neil Fernandez",
        "current_employer": "Insum Solutions",
        "email": "neilfernandezdev@gmail.com",
        "started_with_apex": "2016",
        "interests": [
            "Oracle APEX",
            "PL/SQL",
            "RESTFul Web Services",
            "Integrating APEX with 3rd party APIs",
            "Web Development",
            "Electronics/Gadgets",
            "Craft beer",
            "Cooking",
            "Brazilian Jiu Jitsu, Boxing, MMA (combat sports)",
            "Real Estate",
            "Entrepreneurship"
        ]
    }</p>
<hr />
<p>I started working for Insum Solutions almost 4 years ago as an intern in January of 2016. I joined Insum fresh out of college and it has been quite the experience being able to work alongside some of the best Oracle APEX experts in the world.</p>
<p>Insum Solutions is a world leader when it comes to Oracle technologies with expertise in Oracle Application Express (APEX). Working alongside such amazing leaders has really inspired me to pursue blogging. It is hard not to be inspired when you are surrounded by these indiviudals!</p>
<p>I also am a part-time graduate student at Georgia Tech currently enrolled in the OMSCS program. I love challenges and this program has been one of my greatest challenges so far. I have been dabbling in a variety of classes and am planning to specialize in Interactive Intelligence or Machine Learning within the next year.</p>
<p>Outside of work and school I spend most of my time with my wife, dog, cats, and training mixed martial arts (MMA). Most of that time is dedicated to Brazilian Jiu Jitsu, boxing, and kick boxing, with majority of my time doing BJJ. I would consider myself a people-enthusiast. I like good company and conversation and even better beer.</p>
<p>I am hoping with this blog that I am able to refer to past problems I have solved and that they are useful to you all as well.</p>
<p>I plan to blog mainly about web development, Oracle APEX, and technology but I also may share some of my personal ventures.</p>
<p>Thank you to my mentors and collegues for all the lessons and inspiration thus far!</p>
<p><strong>Note</strong>: My views are that of my own and do not represent those of my employer.</p>
]]></content:encoded></item></channel></rss>