<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7925752970520996999</id><updated>2011-12-13T08:08:45.646-08:00</updated><title type='text'>Duncan Mein's Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>38</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-5238220052548503262</id><published>2011-12-07T06:04:00.000-08:00</published><updated>2011-12-13T08:07:19.258-08:00</updated><title type='text'>We're Hiring</title><content type='html'>We are looking for an APEX developer for an initial 3 month contract with definite scope for long term extension for a role in Hampshire (UK).&lt;br /&gt;&lt;br /&gt;Candidates must be SC cleared or willing to undergo clearance to work on a UK MoD site.&lt;br /&gt;&lt;br /&gt;Any interested parties, please send me an up to date copy of your CV with availability and rate to: duncanmein@gmail.com&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-5238220052548503262?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/5238220052548503262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=5238220052548503262' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5238220052548503262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5238220052548503262'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2011/12/were-hiring.html' title='We&apos;re Hiring'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-5551445202196479678</id><published>2011-06-14T01:07:00.000-07:00</published><updated>2011-06-14T01:41:44.287-07:00</updated><title type='text'>Native String Aggregation in 11gR2</title><content type='html'>A fairly recent requirement meant that we had to send a bulk email to all users of each department from within our APEX application. We have 5000 records in our users table and the last thing we wanted to do was send 5000 distinct emails (one email per user) for both performance and to be kind on the mail queue / server.&lt;br /&gt;&lt;br /&gt;In essence, I wanted to to perform a type of string aggregation where I could group by department and produce a comma delimited sting of all email address of users within that department. With a firm understanding of the requirement, so began the hunt for a solution. Depending on what version of the database you are running, the desired result can be achieved in a couple of ways.&lt;br /&gt;&lt;br /&gt;Firstly, the example objects.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;CREATE TABLE app_user&lt;br /&gt;(id NUMBER&lt;br /&gt;,dept VARCHAR2 (255)&lt;br /&gt;,username VARCHAR2(255)&lt;br /&gt;,email VARCHAR2(255)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;INSERT INTO app_user (id, dept, username, email)&lt;br /&gt;VALUES (1,'IT','FRED','fred@mycompany.com');&lt;br /&gt;&lt;br /&gt;INSERT INTO app_user (id, dept, username, email)&lt;br /&gt;VALUES (2,'IT','JOE','joe@mycompany.com');&lt;br /&gt;&lt;br /&gt;INSERT INTO app_user (id, dept, username, email)&lt;br /&gt;VALUES (3,'SALES','GILL','gill@mycompany.com');&lt;br /&gt;&lt;br /&gt;INSERT INTO app_user (id, dept, username, email)&lt;br /&gt;VALUES (4,'HR','EMILY','emily@mycompany.com');&lt;br /&gt;&lt;br /&gt;INSERT INTO app_user (id, dept, username, email)&lt;br /&gt;VALUES (5,'HR','BILL','bill@mycompany.com');&lt;br /&gt;&lt;br /&gt;INSERT INTO app_user (id, dept, username, email)&lt;br /&gt;VALUES (6,'HR','GUS','gus@mycompany.com');&lt;br /&gt;&lt;br /&gt;COMMIT;&lt;/pre&gt;&lt;br /&gt;If you are using 11gR2, you can expose the new &lt;a href="http://download.oracle.com/docs/cd/E14072_01/server.112/e10592/functions087.htm"&gt;LISTAGG &lt;/a&gt;function as follows to perform your string aggregation natively:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT dept&lt;br /&gt;    ,LISTAGG(email,',') WITHIN GROUP (ORDER BY dept) email_list&lt;br /&gt;FROM app_user&lt;br /&gt;GROUP BY dept;&lt;br /&gt;&lt;br /&gt;DEPT     EMAIL_LIST&lt;br /&gt;-----------------------------------------------------------------&lt;br /&gt;HR       emily@mycompany.com,bill@mycompany.com,gus@mycompany.com&lt;br /&gt;IT       fred@mycompany.com,joe@mycompany.com&lt;br /&gt;SALES    gill@mycompany.com&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If running 11g or earlier, you can achieve the same result using &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions215.htm"&gt;XMLAGG&lt;/a&gt; as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  SELECT au.dept&lt;br /&gt;        ,LTRIM&lt;br /&gt;         (EXTRACT&lt;br /&gt;          (XMLAGG&lt;br /&gt;           (XMLELEMENT&lt;br /&gt;            ("EMAIL",',' || email)),'/EMAIL/text()'), ','&lt;br /&gt;        ) email_list&lt;br /&gt;    FROM app_user au&lt;br /&gt;GROUP BY au.dept;&lt;br /&gt;&lt;br /&gt;DEPT     EMAIL_LIST&lt;br /&gt;-----------------------------------------------------------------&lt;br /&gt;HR       emily@mycompany.com,bill@mycompany.com,gus@mycompany.com&lt;br /&gt;IT       fred@mycompany.com,joe@mycompany.com&lt;br /&gt;SALES    gill@mycompany.com&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The introduction of native string aggregation into 11gR2 is a real bonus and a function that has already proved to have had huge utility within our applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-5551445202196479678?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/5551445202196479678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=5551445202196479678' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5551445202196479678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5551445202196479678'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2011/06/native-string-aggregation-in-11gr2.html' title='Native String Aggregation in 11gR2'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-6177853489147044570</id><published>2011-06-08T14:31:00.000-07:00</published><updated>2011-06-10T01:13:32.266-07:00</updated><title type='text'>A Right Pig's Ear of a Circular Reference</title><content type='html'>If you have ever used a self referencing table within Oracle to store &lt;a href="http://www.adp-gmbh.ch/ora/data_samples/hierarchic_yahoo.html"&gt;hierarchical data&lt;/a&gt; (e.g. an organisations structure), you will have undoubtedly used CONNECT BY PRIOR to build your results tree. This is something we use on pretty much every project as the organisation is very hierarchy based.&lt;br /&gt;&lt;br /&gt;Recently, the support cell sent the details of a recent call they received asking me to take a look. Looking down the call, I noticed that the following Oracle Error Message was logged:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;"ORA-01436: CONNECT BY loop in user data"&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;A quick look at the explanation of &lt;a href="http://ora-01436.ora-code.com/"&gt;-01436&lt;/a&gt; and it was clear that there was a circular reference in the organisation table i.e. &lt;strong&gt;ORG_UNIT1&lt;/strong&gt; was the &lt;strong&gt;PARENT&lt;/strong&gt; of &lt;strong&gt;ORG_UNIT2&lt;/strong&gt; and &lt;strong&gt;ORG_UNIT2&lt;/strong&gt; was the &lt;strong&gt;PARENT&lt;/strong&gt; of &lt;strong&gt;ORG_UNIT1&lt;/strong&gt;. In this example, both ORG_UNITS were the child and parent of each other. Clearly this was an issue which was quickly resolved by the addition of a application and server side validation to prevent this from re-occurring.&lt;br /&gt;&lt;br /&gt;The outcome of this fix was a useful script that I keep to identify if there are any circular references within a self referencing table. The example below shows this script in action:&lt;br /&gt;&lt;pre&gt;CREATE TABLE ORGANISATIONS (ORG_ID NUMBER&lt;br /&gt;, PARENT_ORG_ID NUMBER&lt;br /&gt;, NAME VARCHAR2(100));&lt;br /&gt;&lt;br /&gt;INSERT INTO ORGANISATIONS VALUES (1, NULL, 'HQ');&lt;br /&gt;INSERT INTO ORGANISATIONS VALUES (2, 1, 'SALES');&lt;br /&gt;INSERT INTO ORGANISATIONS VALUES (3, 1, 'RESEARCH');&lt;br /&gt;INSERT INTO ORGANISATIONS VALUES (4, 1, 'IT');&lt;br /&gt;INSERT INTO ORGANISATIONS VALUES (5, 2, 'EUROPE');&lt;br /&gt;INSERT INTO ORGANISATIONS VALUES (6, 2, 'ASIA');&lt;br /&gt;INSERT INTO ORGANISATIONS VALUES (7, 2, 'AMERICAS');&lt;br /&gt;&lt;br /&gt;COMMIT;&lt;/pre&gt;&lt;br /&gt;A quick tree walk query shows the visual representation of the hierarchy with no errors:&lt;br /&gt;&lt;pre&gt;SELECT LPAD ('*', LEVEL, '*') || name tree&lt;br /&gt;      ,LEVEL lev&lt;br /&gt;    FROM organisations&lt;br /&gt;START WITH org_id = 1&lt;br /&gt;CONNECT BY PRIOR org_id = parent_org_id;&lt;br /&gt;&lt;br /&gt;TREE           LEV&lt;br /&gt;---------------------&lt;br /&gt;*HQ            1&lt;br /&gt;**SALES        2&lt;br /&gt;***EUROPE      3&lt;br /&gt;***ASIA        3&lt;br /&gt;***AMERICAS    3&lt;br /&gt;**RESEARCH     2&lt;br /&gt;**IT           2&lt;/pre&gt;&lt;br /&gt;Now lets create a circular reference so that EUROPE is the parent of SALES:&lt;br /&gt;&lt;pre&gt;UPDATE ORGANISATIONS SET parent_org_id = 5 WHERE NAME = 'SALES';&lt;br /&gt;COMMIT;&lt;/pre&gt;&lt;br /&gt;Re-running the query from the very top of the tree completes but gives an incorrect&lt;br /&gt;and incomplete result set:&lt;br /&gt;&lt;pre&gt;TREE         LEV&lt;br /&gt;---------------------&lt;br /&gt;*HQ          1&lt;br /&gt;**RESEARCH   2&lt;br /&gt;**IT         2&lt;/pre&gt;&lt;br /&gt;If you running Oracle Database 9i and backwards, this &lt;a href="http://www.experts-exchange.com/Database/Oracle/9.x/Q_23974646.html"&gt;Experts Exchange&lt;/a&gt; article provides a nice procedural solution and a quick mod to the PL/SQL gave me exactly the information I needed:&lt;br /&gt;&lt;pre&gt;SET serveroutput on size 20000&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt; l_n   NUMBER;&lt;br /&gt;BEGIN&lt;br /&gt; FOR rec IN (SELECT org_id FROM organisations)&lt;br /&gt; LOOP&lt;br /&gt;    BEGIN&lt;br /&gt;       SELECT COUNT ( * )&lt;br /&gt;         INTO l_n&lt;br /&gt;         FROM (    SELECT LPAD ('*', LEVEL, '*') || name tree&lt;br /&gt;                 , LEVEL lev&lt;br /&gt;                     FROM organisations&lt;br /&gt;               START WITH org_id = rec.org_id&lt;br /&gt;               CONNECT BY PRIOR org_id = parent_org_id);&lt;br /&gt;    EXCEPTION&lt;br /&gt;       WHEN OTHERS&lt;br /&gt;       THEN&lt;br /&gt;          IF SQLCODE = -1436&lt;br /&gt;          THEN&lt;br /&gt;             DBMS_OUTPUT.put_line &lt;br /&gt;            (&lt;br /&gt;              rec.org_id &lt;br /&gt;              || ' is part of a Circular Reference'&lt;br /&gt;            );&lt;br /&gt;          END IF;&lt;br /&gt;    END;&lt;br /&gt; END LOOP;&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;As Buzz Killington pointed out in the comments section, Oracle Database 10g onwards introduces &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/queries003.htm"&gt;CONNECT BY NOCYCLE&lt;/a&gt; which will instruct Oracle to return rows even if it is involved in a self referencing loop. When used with the &lt;span style="font-weight: bold;"&gt;CONNECT_BY_ISCYCLE&lt;/span&gt; pseudocolumn, you can easily identify erroneous relationships via SQL without the need to switch to PL/SQL. An example of this can be seen by executing the following query:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;WITH&lt;br /&gt;  my_data&lt;br /&gt;AS&lt;br /&gt;(  SELECT  ORG.PARENT_ORG_ID&lt;br /&gt;         ,ORG.ORG_ID&lt;br /&gt;         ,ORG.NAME org_name&lt;br /&gt;    FROM  organisations org&lt;br /&gt;)&lt;br /&gt;SELECT&lt;br /&gt;  SYS_CONNECT_BY_PATH (org_name,'/') tree&lt;br /&gt;  ,PARENT_ORG_ID&lt;br /&gt;  ,ORG_ID&lt;br /&gt;  ,CONNECT_BY_ISCYCLE err&lt;br /&gt;FROM&lt;br /&gt;  my_data&lt;br /&gt;CONNECT BY NOCYCLE PRIOR org_id = parent_org_id&lt;br /&gt;ORDER BY 4 desc;&lt;br /&gt;&lt;br /&gt;TREE                    PARENT_ORG_ID ORG_ID ERR &lt;br /&gt;------------------------------------------------&lt;br /&gt;/SALES/EUROPE           2             5      1&lt;br /&gt;/EUROPE/SALES           5             2      1&lt;br /&gt;/EUROPE                 2             5      0&lt;br /&gt;/EUROPE/SALES/ASIA      2             6      0&lt;br /&gt;/EUROPE/SALES/AMERICAS  2             7      0&lt;br /&gt;/ASIA                   2             6      0&lt;br /&gt;/AMERICAS               2             7      0&lt;br /&gt;/SALES                  5             2      0&lt;br /&gt;/SALES/ASIA             2             6      0&lt;br /&gt;/SALES/AMERICAS         2             7      0&lt;br /&gt;/HQ                     1             0&lt;br /&gt;/HQ/RESEARCH            1             3      0&lt;br /&gt;/HQ/IT                  1             4      0&lt;br /&gt;/RESEARCH               1             3      0&lt;br /&gt;/IT                     1             4      0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Any value &gt; 0 in the ERR column indicates that the row is involved in a self referencing join. This is a much neater and more performant way to achieve to desired result. Thanks to Buzz for pointing out a much better way to original PL/SQL routine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-6177853489147044570?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/6177853489147044570/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=6177853489147044570' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6177853489147044570'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6177853489147044570'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2011/06/right-pigs-ear-of-circular-reference.html' title='A Right Pig&apos;s Ear of a Circular Reference'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-4285108653183524436</id><published>2011-06-08T05:13:00.001-07:00</published><updated>2011-06-08T05:18:34.871-07:00</updated><title type='text'>Pen Test Tool for APEX</title><content type='html'>Just a quick plug for a cool Penetration Test tool that we have been using on-site for a few months now. The application is called: &lt;a href="https://secure.recx.co.uk/apexsec/index.jsp"&gt;Application Express Security Console&lt;/a&gt; and developed by a company called &lt;a href="http://www.recx.co.uk/"&gt;Recx Ltd&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This can be used to identify areas of you APEX applications that are vulnerable to:&lt;br /&gt;SQL Injection, XSS as well as inadequate access control etc. It kindly suggests ways in which the vulnerability can be addressed as well.&lt;br /&gt;&lt;br /&gt;We have built the use of this into our formal release process now and has definitely proved value for money to organisation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-4285108653183524436?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/4285108653183524436/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=4285108653183524436' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/4285108653183524436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/4285108653183524436'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2011/06/pen-test-tool-for-apex.html' title='Pen Test Tool for APEX'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-7206558951964660417</id><published>2010-11-03T05:29:00.000-07:00</published><updated>2010-11-04T02:43:49.050-07:00</updated><title type='text'>Beware of the Byte</title><content type='html'>Recently our test department raised a bug against one our applications that occurred when trying to insert a record into a table.&lt;br /&gt;&lt;br /&gt;The error message encountered was a fairly innocuous "&lt;b&gt;ORA-01704: string literal too long&lt;/b&gt;".&lt;br /&gt;&lt;br /&gt;Following the test case to the letter, I successfully generated the same error and located the table that the APEX form was inserting into. A quick check of the Data Dictionary confirmed that the column in question was of type VARCHAR2(10). At this stage, I though the obvious cause was that there was no limit on the APEX form item (a Text Area) of 10 characters. Having checked the item in question, not only was there a “&lt;span style="font-weight: bold;"&gt;maxWidth&lt;/span&gt;” value of 10, the text area had been created with a “&lt;span style="font-weight: bold;"&gt;Character Counter&lt;/span&gt;”. Strange then how a form item accepting 10 characters was erroring whilst inserting into a column of VARCHAR2(10).&lt;br /&gt;&lt;br /&gt;A little while later...... (after some head scratching and several discussions with our DBA’s and a colleague) the problem was all too clear. Somewhere between Database Character Sets, VARCHAR2 column definitions and non ASCII characters lay the answer.&lt;br /&gt;&lt;br /&gt;Please forgive the rather verbose narrative but allow me to delve a little deeper.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Firstly the facts&lt;/u&gt;&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;1. The character set of our database is set to AL32UTF8&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT VALUE&lt;br /&gt;  FROM v$nls_parameters&lt;br /&gt; WHERE parameter = 'NLS_CHARACTERSET';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2. Definition of table causing the error:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE TABLE nls_test&lt;br /&gt;(&lt;br /&gt; col1 VARCHAR2(10 BYTE)&lt;br /&gt;);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;3. SQL Statement causing the error:&lt;br /&gt;&lt;br /&gt;INSERT INTO nls_test VALUES ('““““““““““');&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NB: 10 individual characters.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The character used in this insert is typical of a double quote produced by MS Word (it was in fact a copy and paste from a Word document into our Text Area that caused our error).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Explanation&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The reason we encountered the error was all to do with the attempt to insert a 'Multi Byte' character (a double quote from word in our case) into our table as opposed to typical single byte characters (A-Z, 0-9 etc).&lt;br /&gt;&lt;br /&gt;Performing a simple LENGTHB to return the number of bytes this character uses demonstrated this perfectly:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT lengthb('“') from dual;&lt;br /&gt;&lt;br /&gt;LENGTHB('“')&lt;br /&gt;------------&lt;br /&gt;           3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Because our column definition is of type VARCHAR2(10 BYTE), we are only permitted to store values that do not exceed 10 bytes in length. &lt;br /&gt;&lt;br /&gt;Beware, 1 character in our case definitely does not = 1 byte. As already proved, our single character (a Word double quote) occupies 3 bytes so the maximum number of this Multi Byte Character we could possible insert according to our table definition is worked out simply as:&lt;br /&gt;&lt;br /&gt;10 bytes (Column Definition) / 3 (length in bytes of our character) = &lt;b&gt;&lt;u&gt;3&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;So whilst the APEX form item does not distinguish between single and multi byte characters and will allow you to input the full 10 characters, Oracle Database will bounce it back in our case as the total bytes in our string is 30 hence the error.&lt;br /&gt;&lt;br /&gt;One solution suggested was to alter the Data Type to be of type VARCHAR2(10 CHAR) instead of BYTE. This in theory would force the database to respect the actual number of characters entered and not worry too much about single vs. multi byte occupancy. This would allow us resolve our immediate issue of 10 multi byte characters inserting into our table however there are further considerations.&lt;br /&gt;&lt;br /&gt;As it turns out, even when you define your columns to use CHAR over BYTE, Oracle still enforces a hard limit of up to 4000 BYTES (given a mixed string of single and multi byte characters, it implicitly works out the total bytes of the string).&lt;br /&gt;&lt;br /&gt;So beware that even if your column for example accepts only 3000 CHAR and you supply 2001 multi byte characters in an insert statement, it may still fail as it will convert your sting into BYTES enforcing an upper limit of 4000 BYTES.&lt;br /&gt;&lt;br /&gt;Sorry for the really long post but it was a much for my own documentation as anything else.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-7206558951964660417?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/7206558951964660417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=7206558951964660417' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7206558951964660417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7206558951964660417'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2010/11/beware-of-byte.html' title='Beware of the Byte'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-963951469227270651</id><published>2010-02-23T02:10:00.000-08:00</published><updated>2010-02-23T02:18:38.464-08:00</updated><title type='text'>APEX - Identify Report Columns Vulnerable to XSS</title><content type='html'>The following query is a very simple way of identifying all report columns within your APEX application that may be exposed by Cross Site Scripting (XSS).&lt;br /&gt;&lt;br /&gt;XSS allows an attacker to inject web script (JavaScript) into an application and when this is rendered in the report, the script is interpreted rather than rendered as text.&lt;br /&gt;&lt;br /&gt;To safe guard against this attack, APEX provides a "Display as Text (escape special characters)" report column attribute that can be applied to classic and Interactive Reports. This causes the script text to be displayed as text rather than interpreted by the browser. If you have any markup (HTML) within your query that the report is based on, this markup will also be displayed as text and not interpreted. I personally think this is a good by product as you should not really be coding look and feel into your raw SQL.&lt;br /&gt;&lt;br /&gt;Anyway I digress. Here is the query that will identify all vulnerable report columns within your APEX application:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  SELECT application_id,&lt;br /&gt;         application_name,&lt;br /&gt;         page_id,&lt;br /&gt;         region_name,&lt;br /&gt;         column_alias,&lt;br /&gt;         display_as&lt;br /&gt;    FROM apex_application_page_rpt_cols&lt;br /&gt;   WHERE display_as NOT IN&lt;br /&gt;               ('Display as Text (escape special characters, does not save state)',&lt;br /&gt;                'CHECKBOX',&lt;br /&gt;                'Hidden',&lt;br /&gt;                'Text Field')&lt;br /&gt;         AND workspace != 'INTERNAL'&lt;br /&gt;         AND application_id = :APP_ID&lt;br /&gt;ORDER BY 1, 2, 3;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Enjoy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-963951469227270651?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/963951469227270651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=963951469227270651' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/963951469227270651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/963951469227270651'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2010/02/apex-identify-report-columns-vulnerable.html' title='APEX - Identify Report Columns Vulnerable to XSS'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-6981291649013149711</id><published>2010-02-19T03:40:00.001-08:00</published><updated>2010-02-19T03:58:16.593-08:00</updated><title type='text'>Oracle SQL Developer on OS X Snow Leopard</title><content type='html'>I have been using Oracle SQL Developer Data Modeller for a while now within a Windows XP environment. It seems pretty good (albeit a little slow but hey show some an Oracle Java client application that is quick. Oracle Directory Manager?, OWB Design Centre? I shall labour this point no more) and I was looking forward to trying it out on my new 27" iMac.&lt;br /&gt;&lt;br /&gt;I promptley downloaded the software from &lt;a href="http://www.oracle.com/technology/products/database/datamodeler/index.html"&gt;OTN&lt;/a&gt; and a quick read of the instructions suggested I need to do no more other than run the datamodeler.sh shell script since I already had Java SE 6 installed.&lt;br /&gt;&lt;br /&gt;As it turns out, the datamodeler.sh script in the root location does little more than call another script called datamodeler.sh found in the /datamodeler/bin directory which is the once you actually need to execute to fire up SQL Data Modeler&lt;br /&gt;&lt;br /&gt;When this script runs, it prompts you for a the full J2SE file path (which I had no idea where it was) before it will run. After a quick look around google and I came across the command: java_home which when executed like:&lt;br /&gt;&lt;br /&gt;cd /usr/libexec&lt;br /&gt;./java_home&lt;br /&gt;&lt;br /&gt;prints the full path value that you need to open SQL Data Modeler&lt;br /&gt;&lt;br /&gt;e.g.  /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home&lt;br /&gt;&lt;br /&gt;Now we are armed with the full path needed, opening up SQL Data Modeller from a virgin command window goes like this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;cd Desktop/datamodeler/bin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;. ./datamodeler.sh&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Oracle SQL Developer Data Modeler&lt;br /&gt; Copyright (c) 1997, 2009, Oracle and/or its affiliates.All rights reserved. &lt;br /&gt;&lt;br /&gt;Type the full pathname of a J2SE installation (or Ctrl-C to quit), the path will be stored in ~/jdk.conf&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;and hey presto, SQL Data Modeller is up and running.&lt;br /&gt;&lt;br /&gt;Once you have pointed the shell script at your J2SE installation, you wont have to do it again.&lt;br /&gt; &lt;br /&gt;Now I can finally use Data Modeler on my 27" Screen :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-6981291649013149711?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/6981291649013149711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=6981291649013149711' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6981291649013149711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6981291649013149711'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2010/02/oracle-sql-developer-on-os-x-snow.html' title='Oracle SQL Developer on OS X Snow Leopard'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-6295926156252123551</id><published>2009-08-25T06:57:00.001-07:00</published><updated>2009-08-25T07:20:09.438-07:00</updated><title type='text'>Generate Days in Month (PIPELINED Functions)</title><content type='html'>This cool example is not one I can take the credit for but since it is used pretty heavily in our organisation, I thought I would share it as it's not only pretty cool buy also demonstrates how useful Oracle Pipelined functions can be.&lt;br /&gt;&lt;br /&gt;In essence a Pipeline table function (introduced in 9i) allow you use a PL/SQL function as the source of a query rather than a physical table. This is really useful in our case to generate all the days in a calendar month via PL/SQL and query them back within our application.&lt;br /&gt;&lt;br /&gt;To see this in operation, simply create the following objects:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE OR REPLACE TYPE TABLE_OF_DATES IS TABLE OF DATE;&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION GET_DAYS_IN_MONTH &lt;br /&gt;(&lt;br /&gt; pv_start_date_i IN DATE&lt;br /&gt;) &lt;br /&gt; RETURN TABLE_OF_DATES PIPELINED &lt;br /&gt;IS&lt;br /&gt; lv_working_date DATE;&lt;br /&gt; lv_days_in_month NUMBER;&lt;br /&gt; lv_cnt NUMBER;&lt;br /&gt;BEGIN&lt;br /&gt;   &lt;br /&gt;  lv_working_date := TO_DATE(TO_CHAR(pv_start_date_i, 'RRRRMM') || '01', 'RRRRMMDD'); &lt;br /&gt;  lv_days_in_month := TRUNC(LAST_DAY(lv_working_date)) - TRUNC(lv_working_date);&lt;br /&gt;    &lt;br /&gt;  PIPE ROW(lv_working_date);&lt;br /&gt;  &lt;br /&gt;  FOR lv_cnt IN 1..lv_days_in_month&lt;br /&gt;  LOOP&lt;br /&gt;    lv_working_date := lv_working_date + 1;&lt;br /&gt;    PIPE ROW (lv_working_date);&lt;br /&gt;  END LOOP;&lt;br /&gt;  &lt;br /&gt;  RETURN;&lt;br /&gt;&lt;br /&gt;END GET_DAYS_IN_MONTH;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once your objects are successfully complied, you can generate all the days in a month by executing the following query:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT column_value the_date&lt;br /&gt;     , TO_CHAR(column_value, 'DAY') the_day &lt;br /&gt;  FROM TABLE (get_days_in_month(sysdate));&lt;br /&gt;&lt;br /&gt;THE_DATE        THE_DAY&lt;br /&gt;------------------------&lt;br /&gt;01-AUG-09 SATURDAY &lt;br /&gt;02-AUG-09 SUNDAY   &lt;br /&gt;03-AUG-09 MONDAY   &lt;br /&gt;04-AUG-09 TUESDAY  &lt;br /&gt;05-AUG-09 WEDNESDAY&lt;br /&gt;06-AUG-09 THURSDAY &lt;br /&gt;07-AUG-09 FRIDAY   &lt;br /&gt;08-AUG-09 SATURDAY &lt;br /&gt;09-AUG-09 SUNDAY   &lt;br /&gt;10-AUG-09 MONDAY   &lt;br /&gt;11-AUG-09 TUESDAY  &lt;br /&gt;12-AUG-09 WEDNESDAY&lt;br /&gt;13-AUG-09 THURSDAY &lt;br /&gt;14-AUG-09 FRIDAY   &lt;br /&gt;15-AUG-09 SATURDAY &lt;br /&gt;16-AUG-09 SUNDAY   &lt;br /&gt;17-AUG-09 MONDAY   &lt;br /&gt;18-AUG-09 TUESDAY  &lt;br /&gt;19-AUG-09 WEDNESDAY&lt;br /&gt;20-AUG-09 THURSDAY &lt;br /&gt;21-AUG-09 FRIDAY   &lt;br /&gt;22-AUG-09 SATURDAY &lt;br /&gt;23-AUG-09 SUNDAY   &lt;br /&gt;24-AUG-09 MONDAY   &lt;br /&gt;25-AUG-09 TUESDAY  &lt;br /&gt;26-AUG-09 WEDNESDAY&lt;br /&gt;27-AUG-09 THURSDAY &lt;br /&gt;28-AUG-09 FRIDAY   &lt;br /&gt;29-AUG-09 SATURDAY &lt;br /&gt;30-AUG-09 SUNDAY   &lt;br /&gt;31-AUG-09 MONDAY&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I hope someone finds this example as useful as we do. The credits go to &lt;a href="http://simonhunt.blogspot.com"&gt;Simon Hunt&lt;/a&gt; on this one as it was "borrowed" from one of his apps. Since I offered to buy him a beer he has promised not to make too big a deal out it :)&lt;br /&gt;&lt;br /&gt;As always, you can read up on this topic &lt;a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14261/tuning.htm#sthref2345"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-6295926156252123551?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/6295926156252123551/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=6295926156252123551' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6295926156252123551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6295926156252123551'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2009/08/generate-days-in-month-pipelined.html' title='Generate Days in Month (PIPELINED Functions)'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-2307848275306474755</id><published>2009-05-26T06:51:00.000-07:00</published><updated>2009-05-26T07:44:10.900-07:00</updated><title type='text'>Extolling the Virtues of Apple Products (MacBook's, Time Capsule et al)</title><content type='html'>I have long been a fan of Apple products ever since I took the plunge and bough a MacBook Pro about 3 years ago. More recently my girlfriend decided to leave the world of Windows behind and bought herself one of the new MacBook's (which i might add is not massively dissimilar to the specification of my 3 year old MBP). Not only are the build quality of their products very impressive but you hear several anecdotal tails of things that "Just Work" on Mac's. It is this point that I want to pursue during the rest of this post.&lt;br /&gt;&lt;br /&gt;1. I added a wireless HP Printer (HP Photoshop C6180) to my network at home and started to install the drivers supplied by HP on a Thinkpad Windows XP laptop. The install took about 30 minuets and added around 400MB of binaries to the Program Files directory. Only after this laborious install could I see the printer as a Network Printer. I might add that none of the remote programs such as Scan or Copy have ever worked even though I have updated the drivers to the latest ones provided by HP.&lt;br /&gt;&lt;br /&gt;Compare this to the setup on my MacBook Pro which involved opening System Preferences, clicking on Print &amp; Fax. My printer was automatically detected and 10 seconds after clicking on the Add button, I was ready to go. (Note that no drivers were needed to perform the most basic of actions.... PRINTING). I did however have to install the software to carry out remote Scan / Copy operations but at least they work after installation.&lt;br /&gt;&lt;br /&gt;2. After my Linksys router died (WRT54G) I attempted to replace it with another Linksys (WRT160N) which was probably a mistake as I have had nothing but trouble with the original Linksys. Anyway thinking that they must have moved on and stability MUST be greatly improved on the new models, I handed over the £70 at PC World for the WRT160N. &lt;br /&gt;&lt;br /&gt;My expectation at this stage was that I could plug the new router in, plug my Cable Broadband into the appropriately named "Internet" port and hey presto wireless broadband. Alas, this was a far cry from what Linksys expect you to go through during router setup. Without wanting to labour the point, 30 minutes and 2 configuration wizards later a report popped up informing me that the router was successfully set up but no Internet connection could be detected.&lt;br /&gt;&lt;br /&gt;This battle has been ongoing until I bought a 500GB Time Capsule.&lt;br /&gt;&lt;br /&gt;It's setup involved plugging it in, hooking the Broadband into the back and plugging an ethernet cable into my MBP. An automated wizard fired up, updated the Time Capsule firmware and configured the Wireless with WPA2 encryption etc in about 2 minuets. Furthermore, all machines were wirelesslesy connected within 5 minutes and are now fully working as expected.&lt;br /&gt;&lt;br /&gt;This is the level of service I have become accustomed to with Apple products and I guess I am pretty spoilt but when you pay the money you do for their kit, you expect it to work right out of the box.... which 99 times out of 100 it does. Bottom line is that Apple kit does cost more but the quality of components, build and functionality is second to none.&lt;br /&gt;&lt;br /&gt;And Finally......&lt;br /&gt;&lt;br /&gt;I rather embarrassingly spilt a cup of boiling hot tea over my MBP about a year ago. This shorted it out and I though it was a goner. When I got it home, I removed the keyboard (which seemed to collect 99% of the liquid) and washed it under some cold water and left it to air dry. I dried the Motherboard with a can of compressed air and re-assembled it. Fearing the worst, I pressed the power button and to my amazement, the machine powered back up at the 1st time of asking and everything (and I mean everything) was fully functional. I can't begin to tell you have relieved I was that I had not shafted a £2500 laptop with a cup of tea. This machine is still in full use today (I am blogging on it right now) and I am thinking of writing to Apple and imploring them to change their company strap line to.....&lt;br /&gt;&lt;br /&gt;"Apple, Products that just work........ even when soaked in Tea"&lt;br /&gt;&lt;br /&gt;:)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-2307848275306474755?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/2307848275306474755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=2307848275306474755' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/2307848275306474755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/2307848275306474755'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2009/05/extolling-virtues-of-apple-products.html' title='Extolling the Virtues of Apple Products (MacBook&apos;s, Time Capsule et al)'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-2669985398391916054</id><published>2009-05-22T04:01:00.000-07:00</published><updated>2009-05-22T04:25:51.161-07:00</updated><title type='text'>VMWare ESXi Hypervisor</title><content type='html'>Server virtualisation is something I have been using for a few years now both at work and at home. I mainly use VMWare Fusion on my MacBook Pro and VMWare Workstation / Player on an old Windows Laptop. For everyday tasks these products are amazing as i can run an XP VM on my MacBook Pro for all those times I need to open MS Project or run TOAD.&lt;br /&gt;&lt;br /&gt;On my server at home, I didn't want to install a host OS and then install VMWare Workstation / Server to host several Linux VM's. Instead I explored the option of a "Bare Metal" hypervisor from VMWare (ESXi 3.5 Update 3). A hypervisor is a very small linux kernel that runs natively against your servers hardware without the burden of having to install a host OS. From here you can create and manage all your VM's remotely using the VMWare Client tool.&lt;br /&gt;&lt;br /&gt;For more information in ESXi, check out VMWare's website &lt;a href="http://www.vmware.com/products/esxi/"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Reading the documentation suggested that ESXi was very particular about the hardware it supports and so began the quest to build a "White Box" ESXi server at home. Whilst this is not supported by VMWare, I wanted to share the process and components utilised in case anyone else was thinking of building there own ESXi Server.&lt;br /&gt;&lt;br /&gt;Step 1. Download the ESXi 3.5 ISO from the VMWare &lt;a href="https://www.vmware.com/tryvmware/index.php?p=free-esxi&amp;lp=1"&gt;Website&lt;/a&gt; (You will need to register for an account)&lt;br /&gt;&lt;br /&gt;Step 3. Create a Bootable USB Stick (yes you can boot the Hypervisor from a USB stick if your Motherboard supports it) by following this concise &lt;a href="http://blog.mechanised.com/2008/07/how-to-create-your-own-bootable-esxi.html"&gt;guide&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Step 3. Plug your USB stick into your server and configure the IP Address, DNS Server, Hostname and Gateway.&lt;br /&gt;&lt;br /&gt;Step 4. Open a Browser window on a client machine and navigate to: http://ip address to download the VMWare Client tool.&lt;br /&gt;&lt;br /&gt;Step 5. Enjoy using ESXi&lt;br /&gt;&lt;br /&gt;My Server Configuration:&lt;br /&gt;&lt;br /&gt;CPU: Inter Core i7 920&lt;br /&gt;MOBO: Gigabyte GA-EX58-UD5&lt;br /&gt;Memory: 12GB of Corsair DDR3 XMS3 INTEL I7 PC10666 1333MHZ (3X2GB)&lt;br /&gt;SATA Controller: Sweex PU102&lt;br /&gt;NIC: 3Com 3c90x&lt;br /&gt;&lt;br /&gt;The following sites list loads of compatible hardware with notes and issues encountered:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://ultimatewhitebox.com/index.php"&gt;http://ultimatewhitebox.com/index.php&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.vm-help.com/esx/esx3.5/Whiteboxes_SATA_Controllers_for_ESX_3.5_3i.htm"&gt;http://www.vm-help.com/esx/esx3.5/Whiteboxes_SATA_Controllers_for_ESX_3.5_3i.htm&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;With the setup outlined above, I can run 5 Linux machines running Oracle Database, Application Server, APEX and OBIEE without any issue.&lt;br /&gt;&lt;br /&gt;If this is something you are interested in evaluating, i can recommend spending a few hundred pounds on the components as its beats spending thousands on a supported Server from HP.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-2669985398391916054?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/2669985398391916054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=2669985398391916054' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/2669985398391916054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/2669985398391916054'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2009/05/vmware-esxi-hypervisor.html' title='VMWare ESXi Hypervisor'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-2941018196181005376</id><published>2009-03-29T04:49:00.000-07:00</published><updated>2009-03-29T05:15:42.840-07:00</updated><title type='text'>Sum to Parent Nodes in Hierarchy Queries: CONNECT_BY_ROOT</title><content type='html'>In one of our Applicaitons, the table DEPT contains a self referncing join (Pigs Ear) as it models our organsational department hierarchy.&lt;br /&gt;&lt;br /&gt;For example:&lt;pre&gt;&lt;br /&gt;CREATE TABLE DEPT&lt;br /&gt;(&lt;br /&gt; DEPT_ID NUMBER NOT NULL&lt;br /&gt;,PARENT_ID NUMBER&lt;br /&gt;,DEPT_NAME VARCHAR2 (100) NOT NULL&lt;br /&gt;,EMPLOYEES NUMBER NOT NULL&lt;br /&gt;,CONSTRAINT DEPT_PK PRIMARY KEY (DEPT_ID)&lt;br /&gt;,CONSTRAINT DEPT_FK01 FOREIGN KEY (PARENT_ID) &lt;br /&gt; REFERENCES DEPT (DEPT_ID)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;INSERT INTO DEPT VALUES (1,NULL,'IT', 100);&lt;br /&gt;INSERT INTO DEPT VALUES (2,1,'DEVELOPMENT', 12);&lt;br /&gt;INSERT INTO DEPT VALUES (3,1,'SUPPORT', 15);&lt;br /&gt;INSERT INTO DEPT VALUES (4,1,'TEST', 25);&lt;br /&gt;&lt;br /&gt;INSERT INTO DEPT VALUES (5,2,'PL/SQL', 2);&lt;br /&gt;INSERT INTO DEPT VALUES (6,2,'Java', 1);&lt;br /&gt;INSERT INTO DEPT VALUES (7,2,'SQL', 11);&lt;br /&gt;INSERT INTO DEPT VALUES (8,2,'C++', 3);&lt;br /&gt;&lt;br /&gt;INSERT INTO DEPT VALUES (9,4,'Functional', 3);&lt;br /&gt;INSERT INTO DEPT VALUES (10,4,'Non Functional', 5);&lt;br /&gt;&lt;br /&gt;COMMIT;&lt;br /&gt;&lt;/pre&gt;A quick tree walk using CONNECT BY PRIOR shows you the Parent / Child relationships between all departments and the number of employees in each department:&lt;br /&gt;&lt;pre&gt;SELECT rpad( ' ', 1*level, ' ' ) || dept_name dept_name&lt;br /&gt;      ,employees&lt;br /&gt;  FROM dept&lt;br /&gt; START WITH parent_id is null&lt;br /&gt;CONNECT BY PRIOR dept_id = parent_id;&lt;br /&gt;&lt;br /&gt;DEPT_NAME             EMPLOYEES&lt;br /&gt;-------------------- ----------&lt;br /&gt; IT                         100&lt;br /&gt;  DEVELOPMENT                12&lt;br /&gt;   PL/SQL                     2&lt;br /&gt;   Java                       1&lt;br /&gt;   SQL                       11&lt;br /&gt;   C++                        3&lt;br /&gt;  SUPPORT                    15&lt;br /&gt;  TEST                       25&lt;br /&gt;   Functional                 3&lt;br /&gt;   Non Functional             5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We had a requirment to work out the total number of employees at each parent level in the Organisational Hierarchy. For example, the report sum up all employees in the parent node and all its children. &lt;br /&gt;&lt;br /&gt;Taking the department TEST as an example, the report should sum the figures 25 (employees in the TEST department), 3 and 5 (employees in the Functional / Non Functional child departments) to give a figure of 33.&lt;br /&gt;&lt;br /&gt;This can easily be achieved by using CONNECT_BY_ROOT. Straight from the Oracle Documentation:&lt;br /&gt;&lt;br /&gt;"&lt;span style="font-style:italic;"&gt;CONNECT_BY_ROOT is a unary operator that is valid only in hierarchical queries. When you qualify a column with this operator, Oracle returns the column value using data from the root row. This operator extends the functionality of the CONNECT BY [PRIOR] condition of hierarchical queries.&lt;br /&gt;&lt;br /&gt;Restriction on CONNECT_BY_ROOT You cannot specify this operator in the START WITH condition or the CONNECT BY condition.&lt;/span&gt;"&lt;br /&gt;&lt;br /&gt;To meet our requirement, CONNECT_BY_ROOT was utilised as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;select dept_name, employees, tot_employees &lt;br /&gt;from (select &lt;br /&gt;           employees,&lt;br /&gt;           dept_name,&lt;br /&gt;           level lev,&lt;br /&gt;           sum(employees) over(partition by connect_by_root&lt;br /&gt;                                           (dept_id)&lt;br /&gt;                               ) tot_employees&lt;br /&gt;      from dept&lt;br /&gt;    connect by prior dept_id = parent_id)&lt;br /&gt;    where lev=1;&lt;br /&gt;&lt;br /&gt;DEPT_NAME             EMPLOYEES TOT_EMPLOYEES&lt;br /&gt;-------------------- ---------- -------------&lt;br /&gt;IT                          100           177&lt;br /&gt;DEVELOPMENT                  12            29&lt;br /&gt;SUPPORT                      15            15&lt;br /&gt;TEST                         25            33&lt;br /&gt;PL/SQL                        2             2&lt;br /&gt;Java                          1             1&lt;br /&gt;SQL                          11            11&lt;br /&gt;C++                           3             3&lt;br /&gt;Functional                    3             3&lt;br /&gt;Non Functional                5             5&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-2941018196181005376?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/2941018196181005376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=2941018196181005376' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/2941018196181005376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/2941018196181005376'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2009/03/sum-to-parent-nodes-in-hierarchy.html' title='Sum to Parent Nodes in Hierarchy Queries: CONNECT_BY_ROOT'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-3652663731150410787</id><published>2009-03-24T07:14:00.000-07:00</published><updated>2009-03-24T07:24:49.382-07:00</updated><title type='text'>Check All / Uncheck All Checkbox</title><content type='html'>There is a really cool JavaScript function in Apex called: $f_CheckFirstColumn that allows you to Check / Uncheck all checkboxes that exist in the 1st column position of a Tabular Form / Report.&lt;br /&gt;&lt;br /&gt;To implement this, all you need do is add the following HTML to the Column Heading of the 1st Column in your Tabular Form (i.e. the Checkbox Column):&lt;br /&gt;&lt;br /&gt;&amp;lt;input type="Checkbox" onclick="$f_CheckFirstColumn(this)"&amp;gt;&lt;br /&gt;&lt;br /&gt;Check out an example &lt;a href="http://apex.oracle.com/pls/otn/f?p=35606:11"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-3652663731150410787?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/3652663731150410787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=3652663731150410787' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3652663731150410787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3652663731150410787'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2009/03/check-all-uncheck-all-checkbox.html' title='Check All / Uncheck All Checkbox'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-8151105965056020331</id><published>2008-09-26T02:07:00.000-07:00</published><updated>2008-09-26T03:39:12.843-07:00</updated><title type='text'>Web Cache Compression and MOD_GZIP</title><content type='html'>Some of my colleagues are working on a project where bandwidth is massively limited (64k). One suggestion to increase application response time was to use MOD_GZIP (an open source compressor extension to Apache) to compress the outbound HTTP traffic. The only drawback is that MOD_GZIP is not supported by Oracle.&lt;br /&gt;&lt;br /&gt;Since we are using Oracle Application Server, Web Cache achieves exactly the same by simply adding a compression rule to Web Cache for the URL Regular Expression /pls/apex/.*$&lt;br /&gt;&lt;br /&gt;We noticed that without any compression of the HTTP outbound traffic, our test page took 30 seconds to fully render on a 64k link. Turning on compression reduced the rendering time to 7 seconds. Very impressive.&lt;br /&gt;&lt;br /&gt;Navigating through an application with compression turned on was noticeably quicker than one without compression.&lt;br /&gt;&lt;br /&gt;To test if your outbound HTTP traffic is compressed, I would grab the Live HTTP Headers extension to Firefox and you are looking for a line like: Content-Encoding: gzip in the outbound response.&lt;br /&gt;&lt;br /&gt;I configured both APEX and Discoverer Viewer to use compression by following the metalink article: 452837.1&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-8151105965056020331?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/8151105965056020331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=8151105965056020331' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/8151105965056020331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/8151105965056020331'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2008/09/web-cache-compression-and-modgzip.html' title='Web Cache Compression and MOD_GZIP'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-6935217582134599363</id><published>2008-07-24T07:25:00.000-07:00</published><updated>2008-07-26T01:52:57.218-07:00</updated><title type='text'>Reset the APEX internal password</title><content type='html'>I noticed in one of the comments of Dimitri Gielis &lt;a href="http://dgielis.blogspot.com/2007/04/reset-internal-password-in-apex-30.html"&gt;articles&lt;/a&gt; that &lt;a href="http://jornica.blogspot.com/"&gt;Jornica&lt;/a&gt; pointed out a script called apxXEpwd.sql&lt;br /&gt;&lt;br /&gt;I ran this as the SYS user and sure enough, it allows you to reset the ADMIN password for the internal workspace.&lt;br /&gt;&lt;br /&gt;This script can be found in the root apex directory that you download from OTN.&lt;br /&gt;&lt;br /&gt;Very useful when you forget what that password is!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-6935217582134599363?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/6935217582134599363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=6935217582134599363' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6935217582134599363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6935217582134599363'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2008/07/reset-apex-internal-password.html' title='Reset the APEX internal password'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-9169566244887477649</id><published>2008-07-14T11:43:00.000-07:00</published><updated>2008-07-14T11:55:45.923-07:00</updated><title type='text'>Import APEX application via SQL Developer</title><content type='html'>I will be honest and admit that I have not been SQL Developers biggest fan since it's release a few years ago. Having always used 3rd party products such as TOAD and PL/SQL Developer, I found certain things a little irritating and not overwhelmingly obvious when forced to use SQL Developer.&lt;br /&gt;&lt;br /&gt;Recently however, I found an absolute god send of a feature in SQL Developer and that was the ability to import / export APEX applications. This means that the Web GUI for such actions is no longer required.&lt;br /&gt;&lt;br /&gt;Very simply, you connect to your parsing schema's database account via SQL Developer, right click on the Application Express tree directory and select Import Application. This opens a simple wizard and off you go.&lt;br /&gt;&lt;br /&gt;One other cool feature of this is that you can open the details window and see exactly what your import is doing, something that is not possible when using the web GUI.&lt;br /&gt;&lt;br /&gt;Download SQL Developer from: &lt;a href="http://www.oracle.com/technology/software/products/sql/index.html"&gt;OTN&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-9169566244887477649?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/9169566244887477649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=9169566244887477649' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/9169566244887477649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/9169566244887477649'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2008/07/import-apex-application-via-sql.html' title='Import APEX application via SQL Developer'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-3215839783601586534</id><published>2008-06-10T08:19:00.000-07:00</published><updated>2008-06-10T08:57:54.475-07:00</updated><title type='text'>APEX 3.1.1 Released</title><content type='html'>Just upgraded from APEX 3.1 to 3.1.1 on an Oracle Enterpise Linux 4 Update 4 platform.&lt;br /&gt;&lt;br /&gt;Intall took: 5:39 and termintaed without error.&lt;br /&gt;&lt;br /&gt;The patch can be downloaded from &lt;a href="http://metalink.oracle.com"&gt;metalink&lt;/a&gt; (patch number 7032837)&lt;br /&gt;&lt;br /&gt;All in all, a very simple upgrade and now onto the task of regression testing our current 3.1 apps&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-3215839783601586534?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/3215839783601586534/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=3215839783601586534' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3215839783601586534'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3215839783601586534'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2008/06/apex-311-released.html' title='APEX 3.1.1 Released'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-7739378117961780399</id><published>2008-04-29T05:32:00.000-07:00</published><updated>2008-04-30T01:04:58.663-07:00</updated><title type='text'>Hide Show Regions on an Apex Page</title><content type='html'>One of the really nice features of APEX is the ability to hide / show regions when editing item or page attributes. This is really useful if you have to update the same attribute for multiple items.&lt;br&gt;&lt;br /&gt;If you want to implement this type of approach on a page you simply need to:&lt;br&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Create a New Region Template&lt;/u&gt;&lt;/b&gt;&lt;br&gt;&lt;br /&gt;Either create a new region template (or copy an existing one) and add a DIV tag with the id="XXX#REGION_STATIC_ID#" around the Definition Template.&lt;br&gt;&lt;br /&gt;Don’t forget to close the DIV at the end of the template. In this example I copied our region called “Reports Region” and named it “Reports Region 680 Width (Static ID)”&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_iJIfjmflR6Y/SBcWGX9gyuI/AAAAAAAAAAw/auugCBER8go/s1600-h/1.png"&gt;&lt;img id="BLOGGER_PHOTO_ID_5194644993962199778" style="CURSOR: pointer" alt="" src="http://bp2.blogger.com/_iJIfjmflR6Y/SBcWGX9gyuI/AAAAAAAAAAw/auugCBER8go/s400/1.png" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Add the following JavaScript to your Page Header: &lt;a href="http://apex.oracle.com/pls/otn/f?p=35606:3:3336641291028915:::::"&gt;view&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Create several regions on your page and assign a unique Static ID i.e. REGION1, REGION2 etc. Set the Region Template to the one you created in the 1.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_iJIfjmflR6Y/SBcWUH9gyvI/AAAAAAAAAA4/o8ekCvpdtfI/s1600-h/1.png"&gt;&lt;img id="BLOGGER_PHOTO_ID_5194645230185401074" style="CURSOR: pointer" alt="" src="http://bp1.blogger.com/_iJIfjmflR6Y/SBcWUH9gyvI/AAAAAAAAAA4/o8ekCvpdtfI/s400/1.png" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Create a button for each Region you defined in the previous stage and add the following JavaScript call as the URL target of each button:&lt;br /&gt;&lt;br /&gt;javascript:hideDiv(‘XXXREGION1’); // For the button called Region 1&lt;br /&gt;javascript:hideDiv(‘XXXREGION2’); // For the button called Region 2&lt;br /&gt;javascript:hideDiv(‘ALL’); // For the button called Show All&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_iJIfjmflR6Y/SBcWjX9gywI/AAAAAAAAABA/MULpNUUpALQ/s1600-h/1.png"&gt;&lt;img id="BLOGGER_PHOTO_ID_5194645492178406146" style="WIDTH: 283px; CURSOR: pointer; HEIGHT: 117px" alt="" src="http://bp2.blogger.com/_iJIfjmflR6Y/SBcWjX9gywI/AAAAAAAAABA/MULpNUUpALQ/s400/1.png" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Create a hidden item on the page called CURRENT_DIV&lt;br /&gt;&lt;br /&gt;Finally edit the page attributes and place the following command in the “Page HTML Body Attributes” section:&lt;br /&gt;&lt;br /&gt;onload="hideDiv('&amp;amp;CURRENT_DIV.')";&lt;br /&gt;&lt;br /&gt;And that’s it. The key thing to note here is that once a region is hidden, all form items are still active in the DOM and will be submitted along with all the visible form items. This is a great way to break out long, complex forms and enhance the user experience.&lt;br /&gt;&lt;br /&gt;An exmaple can be seen &lt;a href="http://apex.oracle.com/pls/otn/f?p=35606:3:3336641291028915:::::"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-7739378117961780399?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/7739378117961780399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=7739378117961780399' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7739378117961780399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7739378117961780399'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2008/04/hide-show-regions-on-apex-page.html' title='Hide Show Regions on an Apex Page'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_iJIfjmflR6Y/SBcWGX9gyuI/AAAAAAAAAAw/auugCBER8go/s72-c/1.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-7223625296057134740</id><published>2008-04-21T03:45:00.001-07:00</published><updated>2008-04-21T03:50:51.309-07:00</updated><title type='text'>Tab Order of Elements on an APEX Page</title><content type='html'>To set the TAB order of your form, you simply need to add the attribute: TABINDEX="1" to the "HTML Form Element Attributes" of your items replacing the number with the desired sequence.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bp0.blogger.com/_iJIfjmflR6Y/SAxxLdQFQVI/AAAAAAAAAAo/B0hYIv9dTRc/s1600-h/img.png"&gt;&lt;img id="BLOGGER_PHOTO_ID_5191648912095920466" style="CURSOR: hand" alt="" src="http://bp0.blogger.com/_iJIfjmflR6Y/SAxxLdQFQVI/AAAAAAAAAAo/B0hYIv9dTRc/s400/img.png" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is a great way of enhancing the usability of your forms.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-7223625296057134740?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/7223625296057134740/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=7223625296057134740' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7223625296057134740'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7223625296057134740'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2008/04/tab-order-or-elements-on-apex-page.html' title='Tab Order of Elements on an APEX Page'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_iJIfjmflR6Y/SAxxLdQFQVI/AAAAAAAAAAo/B0hYIv9dTRc/s72-c/img.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-6756543911565469984</id><published>2008-04-19T04:39:00.001-07:00</published><updated>2008-04-19T04:42:20.831-07:00</updated><title type='text'>JavaScript API Documentation</title><content type='html'>I have just been reading the Apex 3.1 &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;API&lt;/span&gt; documentation and noticed that the JavaScript &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;API&lt;/span&gt; that are commonly used in Apex are now documented:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/E10513_01/doc/appdev.310/e10499/api.htm#CHDBJJDC"&gt;http://download.oracle.com/docs/cd/E10513_01/doc/appdev.310/e10499/api.htm#CHDBJJDC&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There is some very &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_2"&gt;useful&lt;/span&gt; stuff in there!!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-6756543911565469984?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/6756543911565469984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=6756543911565469984' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6756543911565469984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6756543911565469984'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2008/04/javascript-api-documentation.html' title='JavaScript API Documentation'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-3017662972293753083</id><published>2008-03-28T05:56:00.000-07:00</published><updated>2008-03-28T06:01:00.874-07:00</updated><title type='text'>Multiple Interactive Reports on One Page</title><content type='html'>If you have been using Interactive Reports since Apex 3.1 landed, you are probably as impressed with them as I am.&lt;br /&gt;&lt;br /&gt;The other day I tried to create more than 1 IR on a page and the Wizard prevented me saying "Only 1 Interactive Report can be declared on this page"&lt;br /&gt;&lt;br /&gt;I then tired to copy a region that contained an Interactive Report and sure enough I suddenly had 2 IR's on the one page!&lt;br /&gt;&lt;br /&gt;Whilst this is probably not supported or suggested, my requirement meant that only 1 IR was to be shown at once (i.e. I had a conditional diaply on both IR)&lt;br /&gt;&lt;br /&gt;So if you need to create multiple IR's on the one page but will only display one at run time, copy of a region appears to work!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-3017662972293753083?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/3017662972293753083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=3017662972293753083' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3017662972293753083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3017662972293753083'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2008/03/multiple-interactive-reports-on-one.html' title='Multiple Interactive Reports on One Page'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-719329940343716165</id><published>2007-12-25T13:40:00.000-08:00</published><updated>2007-12-25T14:25:59.523-08:00</updated><title type='text'>Add / Delete a row from a SQL based Tabular Form (Static ID)</title><content type='html'>The current application I am working on involves a re-write to a 12 screen wizard that was written 18 months ago. Several of the screens make use of manually built tabular forms (SQL report regions) and collections to hold the values entered. Some of the screens in the wizard have multiple tabular forms on them as well.&lt;br /&gt;&lt;br /&gt;Currently all tabular forms have 15 lines which cannot be added to or deleted from. In the new version, we removed this limit and allow the user to add as many rows as he / she needs. Furthermore, specific rows can now be removed from the Tabular form. Since all entered data is written into collections, we wanted to avoid "&lt;strong&gt;line by line&lt;/strong&gt;" processing i.e. submitting the form for each time, updating the collection and branching back to the page. By utilising some simple JavaScript and the new "&lt;strong&gt;Static ID&lt;/strong&gt;" of the Reports Region new to APEX 3.0, all requirements could be met.&lt;br /&gt;&lt;br /&gt;The Static ID attribute of the reports region allow us to add our own (unique) ID to a report region. From here we can simpley navigate down the DoM, clone a row in the form using cloneNode and append it to the table using appendChild.&lt;br /&gt;&lt;br /&gt;The JavaScript will work even if you have multiple report regions on the same page providing each report region has a unique&lt;strong&gt; Static ID&lt;/strong&gt; value.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Method:&lt;/strong&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create a new region on your page using the following details: &lt;/li&gt;&lt;/ul&gt;&lt;ol&gt;&lt;li&gt;Region Type: &lt;strong&gt;Report&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Report Implementation: &lt;strong&gt;SQL Report&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Title: &lt;strong&gt;Add Row to Report&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Region Template: &lt;strong&gt;Reports Region&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;SQL Query: &lt;a href="http://apex.oracle.com/pls/otn/f?p=35606:1:314280370743558:::::"&gt;view&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Report Template: Standard&lt;/li&gt;&lt;/ol&gt;&lt;ul&gt;&lt;li&gt;Add the following JavaScript to your Page Header: &lt;a href="http://apex.oracle.com/pls/otn/f?p=35606:1:314280370743558:::::"&gt;view&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Copy the region template: &lt;strong&gt;Reports Region&lt;/strong&gt; and name it &lt;strong&gt;Reports Region (Static ID)&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Edit the region template: &lt;strong&gt;Reports Region (Static ID)&lt;/strong&gt; and replace the Substitution String &lt;strong&gt;#REGION_ID#&lt;/strong&gt; with &lt;strong&gt;#REGION_STATIC_ID#&lt;/strong&gt; in the &lt;strong&gt;Definition&lt;/strong&gt; section&lt;/li&gt;&lt;/ul&gt;&lt;p&gt; &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Edit the region: &lt;strong&gt;Add Rows to Report&lt;/strong&gt; and insert the value: &lt;strong&gt;REPORT1&lt;/strong&gt; into the &lt;strong&gt;Static ID&lt;/strong&gt; textbox found in the &lt;strong&gt;Identification&lt;/strong&gt; seciton. Note that the values entered into the Static ID textbox must be unique to the page if using multiple report regions where you are specifying a Static ID. Then change the template of the region to use the newley created &lt;strong&gt;Reports Region (Static ID)&lt;/strong&gt; template&lt;/li&gt;&lt;/ul&gt;&lt;p&gt; &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Copy the report template: &lt;strong&gt;Standard&lt;/strong&gt; and name it &lt;strong&gt;Standard (Static ID)&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Edit the report template: &lt;strong&gt;Standard (Static ID)&lt;/strong&gt; and replace the text: &lt;strong&gt;id="#REGION_ID#"&lt;/strong&gt; with: &lt;strong&gt;id="datatable_#REGION_ID#"&lt;/strong&gt; in the &lt;strong&gt;before rows&lt;/strong&gt; section.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt; &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Edit the report attributes and change the report template to use the newley created: &lt;strong&gt;Standard (Static ID)&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Add a button to the page using the following details:&lt;/li&gt;&lt;/ul&gt;&lt;ol&gt;&lt;li&gt;Select a Region for the Button: &lt;strong&gt;Add Rows to Report&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Position: &lt;strong&gt;Create a button in a region position&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Button Name: &lt;strong&gt;ADD_ROW&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Label: &lt;strong&gt;Add Row&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Button Type: &lt;strong&gt;HTML Button&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Action: &lt;strong&gt;Redirect to URL without submitting page&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Target is a: &lt;strong&gt;URL&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;URL Target: &lt;strong&gt;javascript:addRow('REPORT1');&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Please note that &lt;strong&gt;REPORT1 &lt;/strong&gt;refers to the &lt;strong&gt;Static ID&lt;/strong&gt; of the region you want to add your row to&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Test your Add and Delete a row functionality&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;An example with all the source code can be seen &lt;a href="http://apex.oracle.com/pls/otn/f?p=35606:1:314280370743558:::::"&gt;here&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-719329940343716165?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/719329940343716165/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=719329940343716165' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/719329940343716165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/719329940343716165'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/12/add-delete-row-from-sql-based-tabular.html' title='Add / Delete a row from a SQL based Tabular Form (Static ID)'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-5156341825473970926</id><published>2007-12-16T10:58:00.000-08:00</published><updated>2007-12-16T11:06:59.480-08:00</updated><title type='text'>Mac OSX, Bootcamp and a Missing Hash Key</title><content type='html'>I bought a Mac Book Pro about 8 months ago as my main business Laptop. Coupled with a copy of Parallels, I built my Oracle Server (Database and Apps Server) on a Windows VM environment which left Mac OSX free for Development using SQL Developer, Dreamweaver etc&lt;br /&gt;&lt;br /&gt;A couple of weeks back I decided to upgrade to OSX Leopard and install windows natively using Bootcamp to utilise both core's on the CPU and all 3 Gig of memory. All well and good until i tried to use the Hask key (Alt + 3 in OSX) when working on some APEX templates. After much research on the web, it appears that most OSX key mappings are installed when using bootcamp but in order to print the hash (#) symbol, you must use Ctrl + Alt + 3&lt;br /&gt;&lt;br /&gt;Simple when you know how&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-5156341825473970926?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/5156341825473970926/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=5156341825473970926' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5156341825473970926'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5156341825473970926'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/12/mac-osx-bootcamp-and-missing-hash-key.html' title='Mac OSX, Bootcamp and a Missing Hash Key'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-263693531013289640</id><published>2007-10-25T00:39:00.000-07:00</published><updated>2007-10-25T01:03:29.016-07:00</updated><title type='text'>Multiple Verison of I.E on one machine</title><content type='html'>After the lastest round of updates were installed from Microsoft, I foolishly forgot to uncheck the "Upgrade to I.E 7" box and hence after a reboot, a fresh new version of Internet Explorer was waiting for me. On face value this appeared ok until I tried to access Mercury Test Director.&lt;br /&gt;&lt;br /&gt;According to the error message, only I.E 6 was supported.&lt;br /&gt;&lt;br /&gt;A quick search on google and I happened upon this website: &lt;a href="http://tredosoft.com/Multiple_IE"&gt;http://tredosoft.com/Multiple_IE&lt;/a&gt;&lt;br /&gt;and downloaded the installer which contained multiple version of I.E from 3.0 to 6.0 that run in standalone.&lt;br /&gt;&lt;br /&gt;Not only has this fixed my problem of accessing applicaitons, it also allows me to test my applications against earlier versions of I.E.&lt;br /&gt;&lt;br /&gt;Very useful indeed&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-263693531013289640?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/263693531013289640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=263693531013289640' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/263693531013289640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/263693531013289640'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/10/multiple-verison-of-ie-on-one-machine.html' title='Multiple Verison of I.E on one machine'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-1787486794615707349</id><published>2007-08-22T00:00:00.000-07:00</published><updated>2007-08-22T00:17:49.193-07:00</updated><title type='text'>I.E is not one of the SELECT few!!!</title><content type='html'>&lt;p class="MsoNormal"&gt;I recently had the requirement to disable several elements in a SELECT list. "No trouble" I thought and promptly altered my code to include: &lt;i&gt;disabled="disabled" &lt;/i&gt;attribute for the selected elements.&lt;br /&gt;&lt;br /&gt;A quick test using Firefox and sure enough all seemed well. That was however until I ran the same web page in Internet Explorer. All of a sudden, the elements I had marked as disabled were anything but.&lt;br /&gt;&lt;br /&gt;Not wanting lambaste Microsoft for "Standards Avoidance", I feel it necessary as &lt;a href="http://www.w3c.org/"&gt;W3C&lt;/a&gt; clearly state that the disabled attribute can be used in the option tag.&lt;br /&gt;&lt;br /&gt;A quick poke around Google and a happened upon &lt;a href="http://www.lattimore.id.au/2005/07/01/select-option-disabled-and-the-javascript-solution/"&gt;this &lt;/a&gt;website which offers a JavaScript workaround.&lt;br /&gt;&lt;br /&gt;All that remains to say is, thanks Internet Explorer. How can the most commonly used browser not conform to some of most basic HTML4 standards?&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;Here is an example so answers on a post card please.&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;/p&gt;&lt;select&gt;&lt;option&gt;Value 1&lt;/option&gt;&lt;option disabled="disabled"&gt;Value 2 (Disabled)&lt;/option&gt;&lt;option&gt;Value 3&lt;/option&gt;&lt;/select&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-1787486794615707349?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/1787486794615707349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=1787486794615707349' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/1787486794615707349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/1787486794615707349'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/08/ie-is-not-one-of-select-few.html' title='I.E is not one of the SELECT few!!!'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-7537417240757929243</id><published>2007-07-01T08:52:00.000-07:00</published><updated>2009-03-22T03:28:57.291-07:00</updated><title type='text'>Custom Authentication / Authorisation Schemes (Part 1)</title><content type='html'>I often see posts on the APEX forum asking how to implement custom Authentication / Authorisation schemes within their applications. The following is something I have used in several apps over the last couple of years and provides a great base for securing your application.&lt;br /&gt;&lt;br /&gt;The code base is largley based on the rather excellent article "&lt;b&gt;Storing Passwords in the Database&lt;/b&gt;" found &lt;a href="http://www.oracle-base.com/articles/9i/StoringPasswordsInTheDatabase9i.php"&gt;here&lt;/a&gt; with a few tweaks and changes.&lt;br /&gt;&lt;br /&gt;Firstly, just to clarify, Authentication Schemes control access to the application and Authorisation Schemes control access to page items / regions and even pages themselves.&lt;br /&gt;&lt;strong&gt;&lt;br /&gt;Implementing your own Authentication&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;I tend to set up an APP_USERS table that stores Username and encrypted passwords that I Authenticate against when page 101 is submitted. All this will be explained in detail as we go.&lt;br /&gt;&lt;strong&gt;&lt;br /&gt;Create the APP_USERS table&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE TABLE APP_USERS &lt;br /&gt;(&lt;br /&gt;USERNAME VARCHAR2(10),&lt;br /&gt;PASSWORD VARCHAR2 (255)&lt;br /&gt;);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;Create the Application Security Package&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE app_security_pkg&lt;br /&gt;AS&lt;br /&gt;PROCEDURE add_user &lt;br /&gt;(&lt;br /&gt; p_username IN VARCHAR2&lt;br /&gt;,p_password IN VARCHAR2&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;PROCEDURE login&lt;br /&gt;(&lt;br /&gt; p_uname IN VARCHAR2&lt;br /&gt;,p_password IN VARCHAR2&lt;br /&gt;,p_session_id IN VARCHAR2&lt;br /&gt;,p_flow_page IN VARCHAR2&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;FUNCTION get_hash &lt;br /&gt;(&lt;br /&gt; p_username IN VARCHAR2&lt;br /&gt;,p_password IN VARCHAR2&lt;br /&gt;)&lt;br /&gt; RETURN VARCHAR2;&lt;br /&gt;&lt;br /&gt;PROCEDURE valid_user2 &lt;br /&gt;(&lt;br /&gt; p_username IN VARCHAR2&lt;br /&gt;,p_password IN VARCHAR2&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;FUNCTION valid_user &lt;br /&gt;(&lt;br /&gt; p_username IN VARCHAR2&lt;br /&gt;,p_password IN VARCHAR2&lt;br /&gt;)&lt;br /&gt; RETURN BOOLEAN;&lt;br /&gt;&lt;br /&gt;END app_security_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;strong&gt;&lt;/strong&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE BODY app_security_pkg&lt;br /&gt;AS&lt;br /&gt;PROCEDURE login &lt;br /&gt;          (&lt;br /&gt;           p_uname IN VARCHAR2&lt;br /&gt;          ,p_password IN VARCHAR2&lt;br /&gt;          ,p_session_id IN VARCHAR2&lt;br /&gt;          ,p_flow_page IN VARCHAR2&lt;br /&gt;          )&lt;br /&gt;IS&lt;br /&gt; lv_goto_page NUMBER DEFAULT 1;&lt;br /&gt;BEGIN&lt;br /&gt; &lt;br /&gt; -- This logic is a demonstration of how to redirect &lt;br /&gt; -- to different pages depending on who successfully &lt;br /&gt; -- authenticates. In my example, it simply demonstrates &lt;br /&gt; -- the ADMIN user going to page 1 and all other users going&lt;br /&gt; -- to page 100. Add you own logic here to detrmin which page &lt;br /&gt; -- a user should be directed to post authentication.&lt;br /&gt; IF UPPER(p_uname) = 'ADMIN'&lt;br /&gt; THEN&lt;br /&gt;  lv_goto_page := 1;&lt;br /&gt; ELSE&lt;br /&gt;  lv_goto_page := 100;&lt;br /&gt; END IF;&lt;br /&gt;&lt;br /&gt; wwv_flow_custom_auth_std.login &lt;br /&gt; (&lt;br /&gt;  p_uname =&gt; p_uname,&lt;br /&gt;  p_password =&gt; p_password,&lt;br /&gt;  p_session_id =&gt; p_session_id,&lt;br /&gt;  p_flow_page =&gt; p_flow_page || ':' || lv_goto_page&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;EXCEPTION&lt;br /&gt;WHEN OTHERS&lt;br /&gt;THEN &lt;br /&gt; RAISE;&lt;br /&gt;END login;&lt;br /&gt;&lt;br /&gt;PROCEDURE add_user &lt;br /&gt;(&lt;br /&gt; p_username IN VARCHAR2&lt;br /&gt;,p_password IN VARCHAR2&lt;br /&gt;)&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;INSERT INTO app_users (username, PASSWORD)&lt;br /&gt;VALUES (UPPER (p_username),&lt;br /&gt;get_hash (TRIM (p_username), p_password));&lt;br /&gt;&lt;br /&gt;COMMIT;&lt;br /&gt;&lt;br /&gt;EXCEPTION&lt;br /&gt;WHEN OTHERS&lt;br /&gt;THEN &lt;br /&gt; ROLLBACK; &lt;br /&gt; RAISE;&lt;br /&gt;END add_user;&lt;br /&gt;&lt;br /&gt;-- Function to Perform a oneway hash of the users &lt;br /&gt;-- passwords. This cannot be reversed. This exmaple &lt;br /&gt;-- is a very week hash and if been used on a production &lt;br /&gt;-- system, you may want to use a stronger hash algorithm.&lt;br /&gt;-- Read the Documentation for more info on DBMS_CRYPTO as &lt;br /&gt;-- this is the supported package from Oracle and &lt;br /&gt;-- DBMS_OBFUSCATION_TOOLKIT is now depricated.&lt;br /&gt;FUNCTION get_hash (p_username IN VARCHAR2, p_password IN VARCHAR2)&lt;br /&gt;RETURN VARCHAR2&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;RETURN DBMS_OBFUSCATION_TOOLKIT.md5 (&lt;br /&gt;input_string =&gt; UPPER (p_username) &lt;br /&gt;                || '/' &lt;br /&gt;                || UPPER (p_password));&lt;br /&gt;END get_hash;&lt;br /&gt;&lt;strong&gt;&lt;/strong&gt;&lt;br /&gt;PROCEDURE valid_user2 (p_username IN VARCHAR2, p_password IN VARCHAR2)&lt;br /&gt;AS&lt;br /&gt;v_dummy VARCHAR2 (1);&lt;br /&gt;BEGIN&lt;br /&gt;SELECT '1'&lt;br /&gt;INTO v_dummy&lt;br /&gt;FROM app_users&lt;br /&gt;WHERE UPPER (username) = UPPER (p_username)&lt;br /&gt;AND PASSWORD = get_hash (p_username, p_password);&lt;br /&gt;EXCEPTION&lt;br /&gt;WHEN NO_DATA_FOUND&lt;br /&gt;THEN raise_application_error (-20000, 'Invalid username / password.');&lt;br /&gt;END valid_user2;&lt;br /&gt;&lt;br /&gt;FUNCTION valid_user (p_username IN VARCHAR2, p_password IN VARCHAR2)&lt;br /&gt;RETURN BOOLEAN&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;valid_user2 (UPPER (p_username), p_password);&lt;br /&gt;RETURN TRUE;&lt;br /&gt;EXCEPTION&lt;br /&gt;WHEN OTHERS&lt;br /&gt;THEN RETURN FALSE;&lt;br /&gt;END valid_user;&lt;br /&gt;&lt;br /&gt;END app_security_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;Create a Test User&lt;/strong&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Log into the APEX Workspace and navigate to the SQL Workshop. If access to the workspace is restricted, this can be done in SQL*Plus providing you log in as the schema owner&lt;/li&gt;&lt;li&gt;Execute the following command:&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;EXECUTE app_security_pkg.add_user (‘testuser’, ‘password’)&lt;br /&gt;/&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Create your own Authentication Scheme&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Navigate to: Shared Components &gt; Authentication Schemes&lt;/li&gt;&lt;li&gt;Click [&lt;strong&gt;Create &gt;&lt;/strong&gt;]&lt;/li&gt;&lt;li&gt;Select “From Scratch” and click [&lt;strong&gt;Next &gt;&lt;/strong&gt;]&lt;/li&gt;&lt;li&gt;Enter “My Auth Scheme” and click [&lt;strong&gt;Create Scheme&lt;/strong&gt;]&lt;/li&gt;&lt;li&gt;Click on the “&lt;strong&gt;My Auth Scheme&lt;/strong&gt;” icon&lt;/li&gt;&lt;li&gt;Enter this into the Authentication Function Section: RETURN APP_SECURITY_PKG.valid_user&lt;/li&gt;&lt;li&gt;Click [&lt;strong&gt;Apply Changes&lt;/strong&gt;]&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Switch on your Custom Scheme&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Click on the “&lt;strong&gt;Change Current&lt;/strong&gt;” link. This can be found under the “&lt;strong&gt;Tasks&lt;/strong&gt;” area&lt;/li&gt;&lt;li&gt;Select “&lt;strong&gt;My Auth Scheme&lt;/strong&gt;” from the drop down list and click [&lt;strong&gt;Next&gt;&lt;/strong&gt;]&lt;/li&gt;&lt;li&gt;Click [&lt;strong&gt;Make Current&lt;/strong&gt;]&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Alter the Login Process on Page 101&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Page &lt;strong&gt;101&lt;/strong&gt; is created by default when you create your application definition. Assuming that the Username and Password text boxes have remained unaltered, their names will be &lt;strong&gt;P101_USERNAME&lt;/strong&gt; and &lt;strong&gt;P101_PASSWORD&lt;/strong&gt; respectfully. If these item names have remained unchanged you will need to do very little. If you have changed them, update the item names in the following procedure call.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Edit Page 101&lt;/li&gt;&lt;li&gt;Navigate to the “&lt;strong&gt;Login&lt;/strong&gt;” process&lt;/li&gt;&lt;li&gt;Replace the procedure call (if one exists) with the following:&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;app_security_pkg.login (P_UNAME =&gt; :P101_USERNAME, P_PASSWORD =&gt; :P101_PASSWORD, P_SESSION_ID =&gt; v('APP_SESSION'), P_FLOW_PAGE =&gt; :APP_ID );&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Click [&lt;strong&gt;Apply Changes&lt;/strong&gt;]&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Test Access to the Application&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;Access to the Application should be only be gained by the Test User account set up in Step 3. I tend to build a screen that allows Users to be created using the &lt;strong&gt;add_user&lt;/strong&gt; procedure executed in step 3.&lt;br /&gt;&lt;br /&gt;This is just the bare bones of a more complete security layer. In previous applications, I have built in password expiration, session timeout and force password change on first login to name but a few. Check back for part 2 which will explain how to implement your own Authorisation Scheme&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-7537417240757929243?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/7537417240757929243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=7537417240757929243' title='25 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7537417240757929243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7537417240757929243'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/07/custom-authentication-authorisation.html' title='Custom Authentication / Authorisation Schemes (Part 1)'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-5965671259823425073</id><published>2007-06-03T03:38:00.000-07:00</published><updated>2007-06-03T03:44:20.173-07:00</updated><title type='text'>Centrally Managed TNSNAMES and SQLNET</title><content type='html'>For a while now I have had 3 Oracle Homes on my Laptop (Database, 10gIDS and 10gBI Tools) and several more on my server at home. It annoyed me that every visit to a new client site requried configuring multiple tnsname entries in all the Client Side apps.&lt;br /&gt;&lt;br /&gt;A collegue at work demonstrated how to centrally manage your tnsnames so that all client apps use the same file.&lt;br /&gt;&lt;br /&gt;Simply save your tnsnames and sqlnet files into a directory on your server (d:\My Documents\TNSNAMES)&lt;br /&gt;&lt;br /&gt;Change the tnsnames and sqlnet entries in all the clients to:&lt;br /&gt;&lt;br /&gt;ifile = d:\My Documents\TNSNAMES\tnsnames.ora&lt;br /&gt;ifile= D:\My Documents\TNSNAMES\sqlnet.ora&lt;br /&gt;&lt;br /&gt;And there you go, any changes to the centrally manages tnsnames / sqlnet files will be visible by all client apps pointed to use them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-5965671259823425073?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/5965671259823425073/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=5965671259823425073' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5965671259823425073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5965671259823425073'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/06/centrally-managed-tnsnames-and-sqlnet.html' title='Centrally Managed TNSNAMES and SQLNET'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-115588798899545167</id><published>2007-05-09T06:30:00.000-07:00</published><updated>2007-05-09T08:18:51.131-07:00</updated><title type='text'>Render Excel Spreadsheet in IE</title><content type='html'>A while back we had a requirement to generate a lot of heavily formatted Excel type reports out of our application. The users also wanted this integrated seamlessly into the application so that when the report was run, it displayed in the same Internet Explorer window. After a bit of research and plenty of goggling, we came up with this solution (this demo uses a copy of the emp table from the Scott schema):&lt;br /&gt;&lt;br /&gt;Appologies in advance if the SQL / PL/SQL or HTML format gets a bit messed up but it should compile ok.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;N.b. I must first point out that this only works in Office 95/2003 and IE. Firefox will generate the report but opens Excel to do so. Please take any of this code and extend it to support Office 2007 and Firefox. It is only meant as a guide / proof of concept.&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;/em&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create this package (I create this in its own schema and have a public execute but creating it in the schema you are working is fine)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;pre&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE excel_lib&lt;br /&gt;IS&lt;/p&gt;&lt;p&gt;&lt;br /&gt;PROCEDURE create_column (&lt;/p&gt;&lt;p&gt;pv_span_i IN VARCHAR2 DEFAULT '1',&lt;br /&gt;pv_style_i IN VARCHAR2);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;PROCEDURE create_cell (&lt;br /&gt;pv_class_i IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;pv_align_i IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;pv_colspan_i IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;pv_rowspan_i IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;pv_value_i IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;pv_value_number_i IN NUMBER DEFAULT NULL,&lt;br /&gt;pv_valign_i IN VARCHAR2 DEFAULT NULL&lt;br /&gt;);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;PROCEDURE create_file_header (&lt;br /&gt;pv_sheetname_i IN VARCHAR2,&lt;br /&gt;pv_send_to_excel_i IN BOOLEAN DEFAULT TRUE&lt;br /&gt;);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;PROCEDURE new_row (&lt;br /&gt;pv_height_i IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;pv_style_i IN VARCHAR2 DEFAULT NULL&lt;br /&gt;);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;PROCEDURE end_row;&lt;br /&gt;PROCEDURE end_file;&lt;br /&gt;END excel_lib;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE BODY excel_lib&lt;br /&gt;IS&lt;br /&gt; PROCEDURE create_column (pv_span_i IN VARCHAR2 DEFAULT '1', pv_style_i IN VARCHAR2)&lt;br /&gt; IS&lt;br /&gt; BEGIN&lt;br /&gt;&lt;br&gt; HTP.p ('&amp;lt;col style=&amp;quot;' || pv_style_i || '&amp;quot; span=&amp;quot;' || pv_span_i || '&amp;quot;&amp;gt;');&lt;br /&gt;&lt;br&gt; END create_column;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; PROCEDURE create_cell (&lt;br /&gt;&lt;br&gt; pv_class_i          IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;&lt;br&gt; pv_align_i          IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;&lt;br&gt; pv_colspan_i        IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;&lt;br&gt; pv_rowspan_i        IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;&lt;br&gt; pv_value_i          IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;&lt;br&gt; pv_value_number_i   IN   NUMBER DEFAULT NULL,&lt;br /&gt;&lt;br&gt; pv_valign_i         IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;&lt;br&gt; )&lt;br /&gt;&lt;br&gt; IS&lt;br /&gt;&lt;br&gt; lv_html_string   VARCHAR2 (500) DEFAULT '&amp;lt;td';&lt;br /&gt;&lt;br&gt; BEGIN&lt;br /&gt;&lt;br&gt; -- Add class attribute&lt;br /&gt;&lt;br&gt; IF pv_class_i IS NOT NULL&lt;br /&gt;&lt;br&gt; THEN&lt;br /&gt;&lt;br&gt; lv_html_string := lv_html_string || ' class=&amp;quot;' || pv_class_i || '&amp;quot;';&lt;br /&gt;&lt;br&gt; END IF;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; -- Add align attribute&lt;br /&gt;&lt;br&gt; IF pv_align_i IS NOT NULL&lt;br /&gt;&lt;br&gt; THEN&lt;br /&gt;&lt;br&gt; lv_html_string := lv_html_string || ' align=&amp;quot;' || pv_align_i || '&amp;quot;';&lt;br /&gt;&lt;br&gt; END IF;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; -- Add x:num attribute&lt;br /&gt;&lt;br&gt; IF pv_value_number_i IS NOT NULL&lt;br /&gt;&lt;br&gt; THEN&lt;br /&gt;&lt;br&gt; lv_html_string :=&lt;br /&gt;&lt;br&gt; lv_html_string || ' x:num=&amp;quot;' || TO_CHAR (pv_value_number_i)&lt;br /&gt;&lt;br&gt; || '&amp;quot;';&lt;br /&gt;&lt;br&gt; END IF;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; -- Add colspan attribute&lt;br /&gt;&lt;br&gt; IF pv_colspan_i IS NOT NULL&lt;br /&gt;&lt;br&gt; THEN&lt;br /&gt;&lt;br&gt; lv_html_string :=&lt;br /&gt;&lt;br&gt; lv_html_string || ' colspan=&amp;quot;' || TO_CHAR (pv_colspan_i)&lt;br /&gt;&lt;br&gt; || '&amp;quot;';&lt;br /&gt;&lt;br&gt; END IF;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; IF pv_rowspan_i IS NOT NULL&lt;br /&gt;&lt;br&gt; THEN&lt;br /&gt;&lt;br&gt; lv_html_string :=&lt;br /&gt;&lt;br&gt; lv_html_string || ' rowspan=&amp;quot;' || TO_CHAR (pv_rowspan_i)&lt;br /&gt;&lt;br&gt; || '&amp;quot;';&lt;br /&gt;&lt;br&gt; END IF;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; &lt;br /&gt;&lt;br&gt; IF pv_valign_i IS NOT NULL&lt;br /&gt;&lt;br&gt; THEN&lt;br /&gt;&lt;br&gt; lv_html_string :=&lt;br /&gt;&lt;br&gt; lv_html_string || ' valign=&amp;quot;' || TO_CHAR (pv_valign_i)&lt;br /&gt;&lt;br&gt; || '&amp;quot;';&lt;br /&gt;&lt;br&gt; END IF;&lt;br /&gt;&lt;br&gt; &lt;br /&gt;&lt;br&gt; -- Finish &amp;lt;td&amp;gt; tag&lt;br /&gt;&lt;br&gt; lv_html_string :=&lt;br /&gt;&lt;br&gt; lv_html_string&lt;br /&gt;&lt;br&gt; || '&amp;gt;'&lt;br /&gt;&lt;br&gt; || REPLACE (SUBSTR (pv_value_i,&lt;br /&gt;&lt;br&gt; 1,&lt;br /&gt;&lt;br&gt; LENGTH (pv_value_i) - LENGTH (LTRIM (pv_value_i))&lt;br /&gt;&lt;br&gt; ),&lt;br /&gt;&lt;br&gt; ' ',&lt;br /&gt;&lt;br&gt; '&amp;amp;nbsp;'&lt;br /&gt;&lt;br&gt; )&lt;br /&gt;&lt;br&gt; || LTRIM (pv_value_i)&lt;br /&gt;&lt;br&gt; || '&amp;lt;/td&amp;gt;';&lt;br /&gt;&lt;br&gt; -- write out the cell details to the page&lt;br /&gt;&lt;br&gt; HTP.p (lv_html_string);&lt;br /&gt;&lt;br&gt; END create_cell;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; PROCEDURE new_row (&lt;br /&gt;&lt;br&gt; pv_height_i   IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;&lt;br&gt; pv_style_i    IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;&lt;br&gt; )&lt;br /&gt;&lt;br&gt; IS&lt;br /&gt;&lt;br&gt; BEGIN&lt;br /&gt;&lt;br&gt; IF pv_height_i IS NULL AND pv_style_i IS NULL&lt;br /&gt;&lt;br&gt; THEN&lt;br /&gt;&lt;br&gt; HTP.p ('&amp;lt;tr&amp;gt;');&lt;br /&gt;&lt;br&gt; ELSE&lt;br /&gt;&lt;br&gt; HTP.p ('&amp;lt;tr height=&amp;quot;' || pv_height_i || '&amp;quot; style=&amp;quot;' || pv_style_i || '&amp;quot;&amp;gt;');&lt;br /&gt;&lt;br&gt; END IF;&lt;br /&gt;&lt;br&gt; END new_row;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; PROCEDURE end_row&lt;br /&gt;&lt;br&gt; IS&lt;br /&gt;&lt;br&gt; BEGIN&lt;br /&gt;&lt;br&gt; HTP.p ('&amp;lt;/tr&amp;gt;');&lt;br /&gt;&lt;br&gt; END end_row;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; PROCEDURE create_file_header (&lt;br /&gt;&lt;br&gt; pv_sheetname_i       IN   VARCHAR2,&lt;br /&gt;&lt;br&gt; pv_send_to_excel_i   IN   BOOLEAN DEFAULT TRUE&lt;br /&gt;&lt;br&gt; )&lt;br /&gt;&lt;br&gt; IS&lt;br /&gt;&lt;br&gt; BEGIN&lt;br /&gt;&lt;br&gt; IF pv_send_to_excel_i&lt;br /&gt;&lt;br&gt; THEN&lt;br /&gt;&lt;br&gt; OWA_UTIL.mime_header ('application/vnd.ms-excel');&lt;br /&gt;&lt;br&gt; END IF;&lt;br /&gt;&lt;br&gt;&amp;nbsp;&lt;br /&gt;&lt;br&gt; HTP.p&lt;br /&gt;&lt;br&gt; (   '&lt;br /&gt;&lt;br&gt;&amp;lt;html xmlns:o=&amp;quot;urn:schemas-microsoft-com:office:office&amp;quot; xmlns:x=&amp;quot;urn:schemas-microsoft-com:office:excel&amp;quot; xmlns=&amp;quot;http://www.w3.org/TR/REC-html40&amp;quot;&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=windows-1252&amp;quot;&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;meta NAME=ProgId content=Excel.Sheet&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;meta NAME=Generator content=&amp;quot;Microsoft Excel 9&amp;quot;&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;style&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;!--table&lt;br /&gt;&lt;br&gt; {mso-displayed-decimal-separator:&amp;quot;\.&amp;quot;;&lt;br /&gt;&lt;br&gt; mso-displayed-thousand-separator:&amp;quot;\,&amp;quot;;&lt;br /&gt;&lt;br&gt; font-family:Arial, sans-serif;&lt;br /&gt;&lt;br&gt; font-size:9pt;&lt;br /&gt;&lt;br&gt; color:black;}');&lt;br /&gt;&lt;br&gt; &lt;br /&gt;&lt;br&gt;htp.p ('@page&lt;br /&gt;&lt;br&gt; {margin:1.0in .75in 1.0in .75in;&lt;br /&gt;&lt;br&gt; mso-header-margin:.5in;&lt;br /&gt;&lt;br&gt; mso-footer-margin:.5in;}&lt;br /&gt;&lt;br&gt;--&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;!--[if gte mso 9]&amp;gt;&amp;lt;xml&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:ExcelWorkbook&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:ExcelWorksheets&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:ExcelWorksheet&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:Name&amp;gt;' || pv_sheetname_i || TO_CHAR (SYSDATE, 'DD-MM-YYYY HH24.MI') || '&amp;lt;/x:Name&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:WorksheetOptions&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:Zoom&amp;gt;75&amp;lt;/x:Zoom&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:Selected/&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:ProtectContents&amp;gt;False&amp;lt;/x:ProtectContents&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:ProtectObjects&amp;gt;False&amp;lt;/x:ProtectObjects&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:ProtectScenarios&amp;gt;False&amp;lt;/x:ProtectScenarios&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;/x:WorksheetOptions&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;/x:ExcelWorksheet&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;/x:ExcelWorksheets&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:WindowHeight&amp;gt;15210&amp;lt;/x:WindowHeight&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:WindowWidth&amp;gt;21900&amp;lt;/x:WindowWidth&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:WindowTopX&amp;gt;480&amp;lt;/x:WindowTopX&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:WindowTopY&amp;gt;120&amp;lt;/x:WindowTopY&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:ProtectStructure&amp;gt;False&amp;lt;/x:ProtectStructure&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;x:ProtectWindows&amp;gt;False&amp;lt;/x:ProtectWindows&amp;gt;&lt;br /&gt;&lt;br&gt; &amp;lt;/x:ExcelWorkbook&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;/xml&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;title&amp;gt;' || pv_sheetname_i || TO_CHAR (SYSDATE, 'DD-MM-YYYY HH24.MI') || '&amp;lt;/title&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;BODY LINK=blue vlink=purple&amp;gt;&lt;br /&gt;&lt;br&gt;&amp;lt;TABLE x:str border=0 cellpadding=0 cellspacing=0 style=&amp;quot;border-collapse:collapse;table-layout:fixed&amp;quot;&amp;gt;'&lt;br /&gt;&lt;br&gt; );&lt;br /&gt;&lt;br&gt; END create_file_header;&lt;br /&gt;&lt;br&gt;PROCEDURE end_file&lt;br /&gt;&lt;br&gt;IS&lt;br /&gt;&lt;br&gt; BEGIN&lt;br /&gt;&lt;br&gt; HTP.p ('&amp;lt;/table&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;');&lt;br /&gt;&lt;br&gt; END end_file;&lt;br /&gt;&lt;br&gt;END Excel_Lib;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2. Create your CSS to format the report&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE OR REPLACE PROCEDURE report_css&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;htp.p ('.xlBlue&lt;br /&gt; {vertical-align:middle;&lt;br /&gt; border:.5pt solid black;&lt;br /&gt; background:aqua;&lt;br /&gt; font-weight:700;}&lt;br /&gt;.xlSilver&lt;br /&gt; {vertical-align:middle;&lt;br /&gt; border:.5pt solid black;&lt;br /&gt; background:silver;&lt;br /&gt; font-weight:700;}&lt;br /&gt;.xlPlain&lt;br /&gt; {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)";&lt;br /&gt; vertical-align:top;}&lt;br /&gt;.xlText&lt;br /&gt; {mso-number-format:"\@";&lt;br /&gt; vertical-align:top;}&lt;br /&gt;.xlYellow&lt;br /&gt; {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)";&lt;br /&gt; background:yellow;&lt;br /&gt; font-weight:700;}&lt;br /&gt;.xlHighlightedNumber&lt;br /&gt; {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)";&lt;br /&gt; background:#FF8080;}&lt;br /&gt;.xlBold&lt;br /&gt; {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)";&lt;br /&gt;   font-weight:700;}&lt;br /&gt;.xlUnderline&lt;br /&gt;    {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)";&lt;br /&gt;    font-weight:700;&lt;br /&gt;    text-decoration:underline;&lt;br /&gt; text-underline-style:single;&lt;br /&gt;}');&lt;br /&gt;&lt;br /&gt;END report_css;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;3. Create your Report. Note that you build up your reports using an HTML Table row / cell layout format&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE OR REPLACE PROCEDURE report1&lt;br /&gt;IS&lt;br /&gt;   CURSOR c_emp&lt;br /&gt;   IS&lt;br /&gt;      SELECT *&lt;br /&gt;        FROM emp;&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;   -- Writes the MIME type etc to allow excel to run in IE&lt;br /&gt;   excel_lib.create_file_header ('My Report ');&lt;br /&gt;   &lt;br /&gt;    report_css; -- Writes your custom CSS to the page&lt;br /&gt;      &lt;br /&gt;   excel_lib.new_row ();&lt;br /&gt;   excel_lib.create_cell (pv_value_i      =&gt; 'Emp No',&lt;br /&gt;                          pv_class_i      =&gt; 'xlYellow',&lt;br /&gt;                          pv_align_i      =&gt; 'center'&lt;br /&gt;                         );&lt;br /&gt;   excel_lib.create_cell (pv_value_i      =&gt; 'Emp Name',&lt;br /&gt;                          pv_class_i      =&gt; 'xlYellow',&lt;br /&gt;                          pv_align_i      =&gt; 'center'&lt;br /&gt;                         );&lt;br /&gt;   excel_lib.create_cell (pv_value_i      =&gt; 'Job',&lt;br /&gt;                          pv_class_i      =&gt; 'xlYellow',&lt;br /&gt;                          pv_align_i      =&gt; 'center'&lt;br /&gt;                         );&lt;br /&gt;   excel_lib.create_cell (pv_value_i      =&gt; 'Manager',&lt;br /&gt;                          pv_class_i      =&gt; 'xlYellow',&lt;br /&gt;                          pv_align_i      =&gt; 'center'&lt;br /&gt;                         );&lt;br /&gt;   excel_lib.create_cell (pv_value_i      =&gt; 'Hire Date',&lt;br /&gt;                          pv_class_i      =&gt; 'xlYellow',&lt;br /&gt;                          pv_align_i      =&gt; 'center'&lt;br /&gt;                         );&lt;br /&gt;   excel_lib.create_cell (pv_value_i      =&gt; 'Sal',&lt;br /&gt;                          pv_class_i      =&gt; 'xlYellow',&lt;br /&gt;                          pv_align_i      =&gt; 'center'&lt;br /&gt;                         );&lt;br /&gt;   excel_lib.create_cell (pv_value_i      =&gt; 'Commission',&lt;br /&gt;                          pv_class_i      =&gt; 'xlYellow',&lt;br /&gt;                          pv_align_i      =&gt; 'center'&lt;br /&gt;                         );&lt;br /&gt;   excel_lib.create_cell (pv_value_i      =&gt; 'Dept No',&lt;br /&gt;                          pv_class_i      =&gt; 'xlYellow',&lt;br /&gt;                          pv_align_i      =&gt; 'center'&lt;br /&gt;                         );&lt;br /&gt;   excel_lib.end_row;&lt;br /&gt;&lt;br /&gt;   FOR x IN c_emp&lt;br /&gt;   LOOP&lt;br /&gt;      excel_lib.new_row ();&lt;br /&gt;      excel_lib.create_cell (pv_value_i      =&gt; TO_NUMBER (x.empno),&lt;br /&gt;                             pv_align_i      =&gt; 'right'&lt;br /&gt;                            );&lt;br /&gt;      excel_lib.create_cell (pv_value_i =&gt; x.ename);&lt;br /&gt;      excel_lib.create_cell (pv_value_i =&gt; x.job);&lt;br /&gt;      excel_lib.create_cell (pv_value_i =&gt; x.mgr);&lt;br /&gt;      excel_lib.create_cell (pv_value_i      =&gt; TO_CHAR (x.hiredate,&lt;br /&gt;                                                         'DD-Mon-RRRR'&lt;br /&gt;                                                        ),&lt;br /&gt;                             pv_align_i      =&gt; 'right'&lt;br /&gt;                            );&lt;br /&gt;&lt;br /&gt;      IF x.sal &gt;= 3000&lt;br /&gt;      THEN&lt;br /&gt;         excel_lib.create_cell (pv_value_i      =&gt; TO_NUMBER (x.sal),&lt;br /&gt;                                pv_align_i      =&gt; 'right',&lt;br /&gt;                                pv_class_i      =&gt; 'xlBlue'&lt;br /&gt;                               );&lt;br /&gt;      ELSE&lt;br /&gt;         excel_lib.create_cell (pv_value_i      =&gt; TO_NUMBER (x.sal),&lt;br /&gt;                                pv_align_i      =&gt; 'right'&lt;br /&gt;                               );&lt;br /&gt;      END IF;&lt;br /&gt;&lt;br /&gt;      IF x.comm IS NULL&lt;br /&gt;      THEN&lt;br /&gt;         excel_lib.create_cell (pv_value_i =&gt; '&amp;nbsp;');&lt;br /&gt;      ELSE&lt;br /&gt;         excel_lib.create_cell (pv_value_i      =&gt; TO_NUMBER (x.comm),&lt;br /&gt;                                pv_align_i      =&gt; 'right',&lt;br /&gt;                                pv_class_i      =&gt; 'xlHighlightedNumber'&lt;br /&gt;                               );&lt;br /&gt;      END IF;&lt;br /&gt;&lt;br /&gt;      excel_lib.create_cell (pv_value_i      =&gt; TO_NUMBER (x.deptno),&lt;br /&gt;                             pv_align_i      =&gt; 'right'&lt;br /&gt;                            );&lt;br /&gt;      excel_lib.end_row;&lt;br /&gt;   END LOOP;&lt;br /&gt;END report1;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;4. Run your Report&lt;br /&gt;&lt;br /&gt;Issue a public grant on the report package and then enter the following url (note that any requests going via mod_plsql will be fine here. I used the apex dad as it is the simplest way to demo)&lt;br /&gt;&lt;br /&gt;http://localhost:7777/pls/apex/duncan.report1&lt;br /&gt;&lt;br /&gt;Hope that all makes sense&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-115588798899545167?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/115588798899545167/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=115588798899545167' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/115588798899545167'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/115588798899545167'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/05/render-excel-spreadsheet-in-ie.html' title='Render Excel Spreadsheet in IE'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-3175057674882600974</id><published>2007-04-23T01:46:00.000-07:00</published><updated>2007-04-23T01:54:19.596-07:00</updated><title type='text'>Stop Page Submission When Enter is Pressed</title><content type='html'>If you have an HTML page containing only 1 Textbox, standard HTML / browser behavior is to allow the page to be submitted when entered is pressed.&lt;br /&gt;&lt;br /&gt;Normally I don't have an issue with this but in APEX, no request value is picked up when the page is submitted this way. Consequently, you usually get an error implying there is no page to branch to as the page was submitted without a request value.&lt;br /&gt;&lt;br /&gt;The solution to this problem is very simple.&lt;br /&gt;&lt;br /&gt;Create a dummy textbox on the page and in the: HTML Form Element Attributes attribute, enter the following: style="display:none;"&lt;br /&gt;&lt;br /&gt;Now because the browser thinks 2 textboxes exist, page submission by pressing enter will not occur.&lt;br /&gt;&lt;br /&gt;Simple solution to a small but annoying problem&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-3175057674882600974?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/3175057674882600974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=3175057674882600974' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3175057674882600974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3175057674882600974'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/04/stop-page-submission-when-enter-is.html' title='Stop Page Submission When Enter is Pressed'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-5963139408022625161</id><published>2007-04-17T05:21:00.000-07:00</published><updated>2007-04-19T04:03:27.382-07:00</updated><title type='text'>Custom Error Handling in APEX</title><content type='html'>&lt;span style="font-family:arial;"&gt;If like me you have written several PL/SQL procedures within your APEX app, you will have had to deal with the issue of error handling. As a general rule, I try to hide all Oracle Error Messages from the user and replace these with something more meaningful for example:&lt;br /&gt;&lt;br /&gt;If a user breaks a unique key by trying to insert the same value twice, the following error message is not uncommon:&lt;br /&gt;&lt;br /&gt;&lt;span style="color:red;"&gt;ORA-00001 Unique constraint violated&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There is little or no merit rendering this message to a user as those outside the Oracle community, may not be able to deduce what went wrong. We can capture this error and translate it to a user friendly message explaining what went wrong for example:&lt;br /&gt;&lt;br /&gt;&lt;span style="color:red;"&gt;The record you tried to create already exists. Please try again&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In order to achieve this in APEX, the process is relativly simple.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Set up your Custom Error Page&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;1. Create a new page with the following attributes:&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Page type: &lt;strong&gt;Blank Page&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Page Number: &lt;strong&gt;your choice (for this example I used page 500) &lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Page Alias: &lt;strong&gt;ERRPAGE&lt;/strong&gt; &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Name: &lt;strong&gt;Custom Error Page&lt;/strong&gt; &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Title: &lt;strong&gt;Custom Error Page&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;2. Create a new HTML region on the page with the following attributes:&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Title: &lt;strong&gt;Custom Error Page&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Region Template: &lt;strong&gt;Wizard Region&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Region Source: &lt;strong&gt;There was an error processing your&lt;/strong&gt; &lt;strong&gt;request.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;P500_ERR_MSG.&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;3. Create the following hidden items (replace 500 with the page number of your error page):&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;P500_ERR_MSG&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;P500_GOTO_PAGE&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;P500_GOTO_PAGE_CC&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;P500_GOTO_PAGE_REQ&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;P500_GOTO_PAGE_ITEMS&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;P500_GOTO_PAGE_VALUES&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;P500_GOTO_PAGE_RP&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;P500_GOTO_PAGE_LINK_DIS&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;4. Create a button in the Custom Error Page HTML Region with the following attributes:&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Button Name: &lt;strong&gt;OK&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Label: &lt;strong&gt;&amp;amp;P500_GOTO_PAGE_LINK_DIS.&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Button Type: &lt;strong&gt;HTML&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Action: &lt;strong&gt;Redirect to URL without submitting page&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Target is a: &lt;strong&gt;Page in this Application&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Page: &lt;strong&gt;&amp;P500_GOTO_PAGE.&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Request:&lt;strong&gt; &amp;amp;P500_GOTO_PAGE_REQ.&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Clear Cache: &lt;strong&gt;&amp;P500_GOTO_PAGE_CC.&amp;amp;P500_GOTO_PAGE_RP.&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Set these items: &lt;strong&gt;&amp;P500_GOTO_PAGE_ITEMS.&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;With these values: &lt;strong&gt;&amp;amp;P500_GOTO_PAGE_VALUES.&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;Create the Error Handling Package&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE error_pkg&lt;br /&gt;AS&lt;br /&gt;   PROCEDURE error_redirect (&lt;br /&gt;      pv_custom_msg         IN   VARCHAR2,&lt;br /&gt;      pv_goto_page          IN   NUMBER DEFAULT v ('APP_PAGE_ID'),&lt;br /&gt;      pv_goto_page_cc       IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;      pv_goto_page_req      IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;      pv_goto_page_items    IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;      pv_goto_page_values   IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;      pv_goto_page_rp       IN   BOOLEAN DEFAULT FALSE,&lt;br /&gt;      pv_goto_page_link_dis IN   VARCHAR2 DEFAULT 'Ok'&lt;br /&gt;&lt;br /&gt;   );&lt;br /&gt;END error_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:courier new;"&gt;&lt;/p&gt;&lt;/span&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE BODY error_pkg&lt;br /&gt;AS&lt;br /&gt; PROCEDURE error_redirect (&lt;br /&gt;      pv_custom_msg         IN   VARCHAR2,&lt;br /&gt;      pv_goto_page          IN   NUMBER DEFAULT v ('APP_PAGE_ID'),&lt;br /&gt;      pv_goto_page_cc       IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;      pv_goto_page_req      IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;      pv_goto_page_items    IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;      pv_goto_page_values   IN   VARCHAR2 DEFAULT NULL,&lt;br /&gt;      pv_goto_page_rp       IN   BOOLEAN DEFAULT FALSE,&lt;br /&gt;      pv_goto_page_link_dis IN   VARCHAR2 DEFAULT 'Ok'&lt;br /&gt;&lt;br /&gt;   )&lt;br /&gt; IS&lt;br /&gt; BEGIN&lt;br /&gt;  wwv_flow.g_unrecoverable_error := TRUE; -- Quit the APEX Engine&lt;br /&gt;      &lt;br /&gt;  HTP.init;-- Initiate the HTML Header incase it was closed&lt;br /&gt;      &lt;br /&gt;  -- Set session of items on the error page&lt;br /&gt;  apex_util.set_session_state ('P500_ERR_MSG', pv_custom_msg);&lt;br /&gt;  apex_util.set_session_state ('P500_GOTO_PAGE', pv_goto_page);&lt;br /&gt;  apex_util.set_session_state ('P500_GOTO_PAGE_CC', pv_goto_page_cc);&lt;br /&gt;  apex_util.set_session_state ('P500_GOTO_PAGE_REQ', pv_goto_page_req);&lt;br /&gt;  apex_util.set_session_state ('P500_GOTO_PAGE_ITEMS', pv_goto_page_items);&lt;br /&gt;  apex_util.set_session_state ('P500_GOTO_PAGE_VALUES', pv_goto_page_values);&lt;br /&gt;  apex_util.set_session_state&lt;br /&gt;        ('P500_GOTO_PAGE_LINK_DIS', pv_goto_page_link_dis);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;      -- Resets the pagination scheme for the page redirected to&lt;br /&gt;      IF pv_goto_page_rp&lt;br /&gt;      THEN&lt;br /&gt;         apex_util.set_session_state ('P500_GOTO_PAGE_RP', ',RP');&lt;br /&gt;      END IF;&lt;br /&gt;&lt;br /&gt;  -- Redirect the URL to the custom error page&lt;br /&gt;      OWA_UTIL.redirect_url (   'f?p='&lt;br /&gt;                             &amp;#124;&amp;#124; v ('APP_ID')&lt;br /&gt;                             &amp;#124;&amp;#124; ':ERRPAGE:'&lt;br /&gt;                             &amp;#124;&amp;#124; v ('APP_SESSION')&lt;br /&gt;                            );&lt;br /&gt;&lt;br /&gt;   END error_redirect;&lt;br /&gt;END error_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You will need to change the page ID of the items that are being set in session in the error_redirect procedure, to the ID of your error page.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="font-family:arial;"&gt;Re-Write your Exception Handlers&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;EXCEPTION&lt;br /&gt;   WHEN CUSTOM_EXCEPTION1&lt;br /&gt;   THEN&lt;br /&gt;      error_pkg.error_redirect&lt;br /&gt;         (pv_custom_msg =&gt; 'Your Custom Error Message Goes Here',&lt;br /&gt;          pv_goto_page  =&gt; v ('APP_PAGE_ID'));&lt;br /&gt;   WHEN OTHERS&lt;br /&gt;   THEN&lt;br /&gt;      IF SQLCODE = -6502&lt;br /&gt;      THEN&lt;br /&gt;         error_pkg.error_redirect&lt;br /&gt;           (pv_custom_msg   =&gt; 'A Non Numeric Character was Encountered',&lt;br /&gt;            pv_goto_page    =&gt; v ('APP_PAGE_ID'),&lt;br /&gt;            pv_goto_page_cc =&gt; '1,500');&lt;br /&gt;      ELSE&lt;br /&gt;      error_pkg.error_redirect&lt;br /&gt;           (pv_custom_msg =&gt; 'Please contact Support for further assistance',&lt;br /&gt;            pv_goto_page  =&gt; v ('APP_PAGE_ID'),&lt;br /&gt;            pv_goto_page_cc  =&gt; '1,500',&lt;br /&gt;             pv_goto_page_rp  =&gt; TRUE);&lt;br /&gt;      END IF;&lt;br /&gt;END;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;The only parameter that is required when calling the &lt;strong&gt;error_pkg.error_redirect&lt;/strong&gt; procedure is &lt;strong&gt;pv_custom_msg&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;All the other parameters let you build a new URL that will be redirected to once the user presses the [&lt;strong&gt;OK&lt;/strong&gt;] button on the Custom Error Page. These parameters are bound by the same rules as constructing a URL anywhere else in the APEX application. Please see: &lt;/span&gt;&lt;a href="http://download-uk.oracle.com/docs/cd/B32472_01/doc/appdev.300/b32471/concept.htm#BEIFCDGF"&gt;&lt;span style="font-family:arial;"&gt;Understanding URL Syntax&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;The only parameter that behaves slightly differently to the documentation is &lt;strong&gt;pv_goto_page_rp&lt;/strong&gt;&lt;br /&gt;This is a Boolean switch where TRUE will reset the pagination on the page redirected to after the Custom Error Page.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;The &lt;strong&gt;pv_goto_page_link_dis&lt;/strong&gt; parameter allows you to set the text of the button on the Custom Error Page.&lt;br /&gt;&lt;br /&gt;You now should have a generic approach to providing the user with a friendly error message on your own Custom Error Page in you APEX applications.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;There is an example of this in operation here&lt;/span&gt;: &lt;a href="http://apex.oracle.com/pls/otn/f?p=40923:1"&gt;http://apex.oracle.com/pls/otn/f?p=40923:1&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-5963139408022625161?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/5963139408022625161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=5963139408022625161' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5963139408022625161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/5963139408022625161'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/04/custom-error-handling-in-apex.html' title='Custom Error Handling in APEX'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-480535403356986729</id><published>2007-03-26T12:02:00.001-07:00</published><updated>2007-04-19T01:05:43.260-07:00</updated><title type='text'>Flatten Out a Heirarchy using SYS_CONNECT_BY_PATH</title><content type='html'>In a recent application, we needed to model the Organisational Hierarchy which at its most complex ran to 7 levels deep. This was achieved using a self referencing foreign key (Pigs Ear) similar to that of the EMP table in the Scott schema. In essence, it simply stores the parent / child relationship for each entry in the Hierarchy.&lt;br /&gt;&lt;br /&gt;This approach serviced the application very well in that a simply tree walk (CONNECT BY PRIOR) allowed us to construct the Hierarchical tree and bounce our requests off that.&lt;br /&gt;&lt;br /&gt;During the production of the Discoverer End User Layer, it became evident that this Hierarchy needed to be flattened out (un-normalized) in order for it to reported on. This is because Discoverer (or any other BI product) does not support the CONNECT BY and START WITH clause. Discoverer needs to know how many levels exist within a Hierarchy and that every thread in the Hierarchy has the same number of levels in order to build a folder structure that can be reported on.&lt;br /&gt;&lt;br /&gt;After a bit of investigation, I happened upon the: SYS_CONNECT_BY_PATH new in 9i&lt;br /&gt;&lt;br /&gt;This can be used to construct the "Directory Path" for all values in the EMP table i.e. we can construct the reporting path for all employees in the EMP table&lt;br /&gt;&lt;br /&gt;For Example:&lt;br /&gt;&lt;br /&gt;SELECT empno, ename, job, SYS_CONNECT_BY_PATH (ename, '/') &amp;#124;&amp;#124; '/' chain&lt;br /&gt;FROM emp&lt;br /&gt;START WITH mgr IS NULL&lt;br /&gt;CONNECT BY PRIOR empno = mgr;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;EMPNO ENAME   JOB       CHAIN&lt;br /&gt;----- ------  -----     ---------------------&lt;br /&gt; 7839 KING    PRESIDENT /KING/&lt;br /&gt; 7566 JONES   MANAGER   /KING/JONES/&lt;br /&gt; 7788 SCOTT   ANALYST   /KING/JONES/SCOTT/&lt;br /&gt; 7876 ADAMS   CLERK     /KING/JONES/SCOTT/ADAMS/&lt;br /&gt; 7902 FORD    ANALYST   /KING/JONES/FORD/&lt;br /&gt; 7369 SMITH   CLERK     /KING/JONES/FORD/SMITH/&lt;br /&gt; 7698 BLAKE   MANAGER   /KING/BLAKE/&lt;br /&gt; 7499 ALLEN   SALESMAN  /KING/BLAKE/ALLEN/&lt;br /&gt; 7521 WARD    SALESMAN  /KING/BLAKE/WARD/&lt;br /&gt; 7654 MARTIN  SALESMAN  /KING/BLAKE/MARTIN/&lt;br /&gt; 7844 TURNER  SALESMAN  /KING/BLAKE/TURNER/&lt;br /&gt; 7900 JAMES   CLERK     /KING/BLAKE/JAMES/&lt;br /&gt; 7782 CLARK   MANAGER   /KING/CLARK/&lt;br /&gt; 7934 MILLER  CLERK     /KING/CLARK/MILLER/&lt;br /&gt;&lt;br /&gt;14 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;From here, we can simply parse the CHAIN string to work out what our Hierarchy would look like to any number of levels. The following function can be used to construct out flat Hierarchy&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION parse_string (&lt;br /&gt;pv_str_i IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;pv_first_occ IN NUMBER DEFAULT NULL,&lt;br /&gt;pv_last_occ IN NUMBER DEFAULT NULL&lt;br /&gt;)&lt;br /&gt;RETURN VARCHAR2&lt;br /&gt;IS&lt;br /&gt;lv_retval VARCHAR2 (4000);&lt;br /&gt;lv_str VARCHAR2 (4000);&lt;br /&gt;lv_tester VARCHAR2 (4000);&lt;br /&gt;lv_substr_pos1 NUMBER;&lt;br /&gt;lv_substr_pos2 NUMBER;&lt;br /&gt;BEGIN&lt;br /&gt;SELECT INSTR (pv_str_i, '/', 1, pv_first_occ)&lt;br /&gt;INTO lv_substr_pos1&lt;br /&gt;FROM DUAL;&lt;br /&gt;&lt;br /&gt;SELECT INSTR (pv_str_i, '/', 1, pv_last_occ)&lt;br /&gt;INTO lv_substr_pos2&lt;br /&gt;FROM DUAL;&lt;br /&gt;&lt;br /&gt;lv_tester :=&lt;br /&gt;SUBSTR (pv_str_i, lv_substr_pos1 + 1,&lt;br /&gt;(lv_substr_pos2 - lv_substr_pos1));&lt;br /&gt;&lt;br /&gt;lv_str := REPLACE (lv_tester, '/', NULL);&lt;br /&gt;&lt;br /&gt;RETURN lv_str;&lt;br /&gt;END parse_string;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;If we wanted to then create a flat hierarchy to 3 levels deep, we can simply then write a query like follows:&lt;br /&gt;&lt;br /&gt;SELECT chain, &lt;br /&gt;NVL (parse_string (chain, 1, 2), '&lt;- ' &amp;#124;&amp;#124; ename) level1, &lt;br /&gt;NVL (parse_string (chain, 2, 3), '&lt;- ' &amp;#124;&amp;#124; ename) level2, &lt;br /&gt;NVL (parse_string (chain, 3, 4), '&lt;- ' &amp;#124;&amp;#124; ename) level3 &lt;br /&gt;FROM (SELECT empno, ename, job, &lt;br /&gt;SYS_CONNECT_BY_PATH (ename, '/') &amp;#124;&amp;#124; '/' chain &lt;br /&gt;FROM emp &lt;br /&gt;START WITH mgr IS NULL &lt;br /&gt;CONNECT BY PRIOR empno = mgr); &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CHAIN                    LEV1  LEV2    LEV3&lt;br /&gt;----------------------   ----  ----    ----&lt;br /&gt;/KING/                   KING  &lt;- KING &lt;- KING &lt;br /&gt;/KING/JONES/             KING  JONES   &lt;- JONES &lt;br /&gt;/KING/JONES/SCOTT/       KING  JONES   SCOTT &lt;br /&gt;/KING/JONES/SCOTT/ADAMS/ KING  JONES   SCOTT &lt;br /&gt;/KING/JONES/FORD/        KING  JONES   FORD &lt;br /&gt;/KING/JONES/FORD/SMITH/  KING  JONES   FORD &lt;br /&gt;/KING/BLAKE/             KING  BLAKE   &lt;- BLAKE &lt;br /&gt;/KING/BLAKE/ALLEN/       KING  BLAKE   ALLEN &lt;br /&gt;/KING/BLAKE/WARD/        KING  BLAKE   WARD &lt;br /&gt;/KING/BLAKE/MARTIN/      KING  BLAKE   MARTIN &lt;br /&gt;/KING/BLAKE/TURNER/      KING  BLAKE   TURNER &lt;br /&gt;/KING/BLAKE/JAMES/       KING  BLAKE   JAMES &lt;br /&gt;/KING/CLARK/             KING  CLARK   &lt;- CLARK &lt;br /&gt;/KING/CLARK/MILLER/      KING  CLARK   MILLER &lt;br /&gt;&lt;br /&gt;14 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now we have a table that can be used to report on with BI products such as Discoverer&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-480535403356986729?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/480535403356986729/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=480535403356986729' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/480535403356986729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/480535403356986729'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/03/flatten-out-heirarchy.html' title='Flatten Out a Heirarchy using SYS_CONNECT_BY_PATH'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-2760693991648751285</id><published>2007-03-20T01:20:00.000-07:00</published><updated>2007-03-20T02:13:16.755-07:00</updated><title type='text'>Translate Columns into Rows (Subquery Factoring)</title><content type='html'>It has always been a fairly rudimentary task pivoting a result set so that rows are displayed as columns. More recently I had the requirement to translate a result set the other way so that the columns would be displayed as rows.&lt;br /&gt;&lt;br /&gt;For Example&lt;br /&gt;&lt;br /&gt;Your result set starts out like this:&lt;br /&gt;&lt;br /&gt;SITE                          COST1             COST2              COST3              COST4&lt;br /&gt;------------------------------------------------------------------&lt;br /&gt;SITE_ONE            2000                                                                                 255&lt;br /&gt;SITE_TWO                                           100&lt;br /&gt;SITE_THREE                              145                                            5000&lt;br /&gt;&lt;br /&gt;The desired output should look like this:&lt;br /&gt;&lt;br /&gt;SITE                             VALUE&lt;br /&gt;------------------------------&lt;br /&gt;SITE_ONE              2000&lt;br /&gt;SITE_ONE              255&lt;br /&gt;SITE_TWO             100&lt;br /&gt;SITE_THREE       145&lt;br /&gt;SITE_THREE       5000&lt;br /&gt;&lt;br /&gt;Thanks to the help from Mr. Tom Kyte at http://asktom.oracle.com I was able to produce the desired results using the following query:&lt;br /&gt;&lt;br /&gt;WITH data AS&lt;br /&gt;(SELECT LEVEL l&lt;br /&gt; FROM dual CONNECT BY LEVEL &lt;= 4) SELECT site,   decode(l,   1,   cost1,   2,   cost2,   3,   cost3,   4,   cost4) cost FROM data,   costs WHERE decode(l,   1,   cost1,   2,   cost2,   3,   cost3,   4,   cost4) IS NOT NULL ORDER BY 1 &lt;br /&gt;&lt;br /&gt;It uses something called Subquery Factoring which allows you to create a pseudo table with N rows (CONNECT BY LEVEL &lt;= N) in your query. You then cartisian join to your other tables(s) and use a simple DECODE to extract the column you are interested in. &lt;br /&gt;&lt;br /&gt;Note that this technique will only work in 9i and above but I have had great success in the past with it  The thread on asktom can be found &lt;a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:124812348063"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-2760693991648751285?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/2760693991648751285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=2760693991648751285' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/2760693991648751285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/2760693991648751285'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/03/translate-columns-into-rows-subquery.html' title='Translate Columns into Rows (Subquery Factoring)'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-1568910965146806151</id><published>2007-02-13T04:56:00.000-08:00</published><updated>2007-02-14T06:08:21.169-08:00</updated><title type='text'>Where Did My PL/SQL Error?</title><content type='html'>When your PL/SQL block errors, it has always been a rudimentary task in identifying the error generated using a combination of SQLCODE and SQLERRM.&lt;br /&gt;&lt;br /&gt;These 2 functions however do not tell us the exact line of code that propagated the error. This feature would be very handy when debugging code.&lt;br /&gt;&lt;br /&gt;In Oracle 10g, there is a function called: &lt;font color="blue"&gt;DBMS_UTILITY.format_error_backtrace&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;This will return the line number that generated the error. It does not tell us what the SQL Error message is so I use it in conjunction with SQLERRM to quickly debug problematic code.&lt;br /&gt;&lt;br /&gt;In a recent application, I used it as part of my error logging and reporting function that allows the support team to quickly troubleshoot errors. The more information about the error we can give them, the quicker they should be able to response. At least that’s the theory.&lt;br /&gt;&lt;br /&gt;For a more detailed explanation of how to best utilise this function, have a look at this &lt;a href="http://www.quest-pipelines.com/newsletter-v5/0904_A.htm"&gt;atricle&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-1568910965146806151?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/1568910965146806151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=1568910965146806151' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/1568910965146806151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/1568910965146806151'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/02/what-code-line-number-errored.html' title='Where Did My PL/SQL Error?'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-3137628654789541270</id><published>2007-01-28T14:43:00.000-08:00</published><updated>2007-01-28T14:54:22.302-08:00</updated><title type='text'>Back to work!!!!!!!!!</title><content type='html'>Well, our week skiing in Meribel came and went too quickly. We all had am awesome time in the "Best British Ski Resort" France has to offer.&lt;br /&gt;&lt;br /&gt;The Skiing was good (not excellent) but enough to thoroughly exhaust every one of us and our Chalet was very comfortable. &lt;br /&gt;&lt;br /&gt;Living costs in the Trois Valleys was not cheap however. Beer ranged between 8 and 10 Euros a pint which made for some very expensive nights out. Fortunately, food was included in our Chalet costs. I was devastated parting with 40 Euros for a lasagne, chips and a coke in Courcheval but hey.&lt;br /&gt;&lt;br /&gt;For anyone interested, some pictures can be found &lt;a href="http://www.flickr.com/photos/mikecostello/sets/72157594491590079"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I shall be putting them all on my website in the next few weeks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-3137628654789541270?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/3137628654789541270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=3137628654789541270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3137628654789541270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/3137628654789541270'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/01/back-to-work.html' title='Back to work!!!!!!!!!'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-7528694990722655758</id><published>2007-01-10T00:41:00.000-08:00</published><updated>2007-01-10T00:52:05.990-08:00</updated><title type='text'>Optimistic Locking Using ORA_ROWSCN</title><content type='html'>For anyone who has manually written an Update Statement in a Web Application, the issue of Optimistic Locking always took a while to address.&lt;br /&gt;&lt;br /&gt;The normal convention is to use a series of hidden elements for every form element that is submitted and doing an "old vs new" comparison in the Update statement to see if any valus in the database have changed.&lt;br /&gt;&lt;br /&gt;I found the main drawback was the need to create large amounts of hidden elements that are then passed into the DML procedure for evaluation.&lt;br /&gt;&lt;br /&gt;I came across ORA_ROWSCN in 10GR2 that reduces the amount of hidden form elements you need to submit.&lt;br /&gt;Its basically a new pseudo column in that database that provides the SCN (System Clock Number for the last time the row was updated) associated with individual rows when they were read.&lt;br /&gt;&lt;br /&gt;The basic steps to utilise this approach are as follows:&lt;br /&gt;&lt;br /&gt;1) create table users (id number,&lt;br /&gt;name VARCHAR2 (100),&lt;br /&gt;address VARCHAR2 (100),&lt;br /&gt;tel NUMBER)&lt;br /&gt;ROWDEPENDENCIES&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;ROWDEPENDENCIES need to set as SCN by default is implemented at block level. By setting ROWDEPENDENCIES, SCN should now track at row level.&lt;br /&gt;&lt;br /&gt;2) query that a form on page 100 is based on would look like this (note that a hidden element P100_USER_RCN should be created at page level):&lt;br /&gt;&lt;br /&gt;SELECT name, address, tel, ORA_ROWSCN&lt;br /&gt;INTO :P100_USER_NAME, :P100_ADDRESS, :P100_TEL, :P100_USER_RCN&lt;br /&gt;FROM users&lt;br /&gt;WHERE id = 1;&lt;br /&gt;&lt;br /&gt;3) Your update statement would then look like this:&lt;br /&gt;&lt;br /&gt;UPDATE users&lt;br /&gt;SET id = :P100_USER_ID,&lt;br /&gt;name = :P100_USER_NAME,&lt;br /&gt;WHERE id = 1&lt;br /&gt;AND ORA_ROWSCN = :P100_USER_RCN;&lt;br /&gt;&lt;br /&gt;-- Check to see if update took place&lt;br /&gt;IF SQL%ROWCOUNT &lt;= 0 &lt;br /&gt;THEN &lt;br /&gt;  RAISE_APPLICATION_ERROR (-20002, 'Optimistic Locking Violation'); &lt;br /&gt;END IF;  &lt;br /&gt;&lt;br /&gt;Of course my first advice would be let Application Express handle your DML updates if you can, but for the times when you have to manually create your own update, this may be of some help to you.&lt;br /&gt; &lt;br /&gt;&lt;a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:22948373947565"&gt;Here&lt;/a&gt; are some benchmarked examples of this in operation on AskTom&lt;br /&gt;&lt;br /&gt;I also posted this in the Application Express Forum a while back. There were some good comments / additions made by others so &lt;a href="http://forums.oracle.com/forums/thread.jspa?messageID=1575856&amp;#1575856"&gt;here&lt;/a&gt; is the link to the post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-7528694990722655758?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/7528694990722655758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=7528694990722655758' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7528694990722655758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7528694990722655758'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/01/optimistic-locking-using-orarowscn.html' title='Optimistic Locking Using ORA_ROWSCN'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-6767118522606067169</id><published>2007-01-09T03:55:00.000-08:00</published><updated>2007-01-10T00:38:15.541-08:00</updated><title type='text'>New Line Character Using UTL_FILE</title><content type='html'>A while ago I came across an issue exporting the contents of a BLOB to a flat file on my file system. My processing was to upload a .csv file into the wwv_flows_files table, and write a file back to my Oracle directory using the UTL_FILE api. Upon inspecting the file produced, a newline character was inserted after every line.&lt;br /&gt;&lt;br /&gt;To cut a long story short, a colleague of mine came up with a workaround that involved parsing the newley produced file, removing any spurious lines and creating a new file minus the blank lines. As you can imagine, for large files, this added a fair amount of time to the Upload process.&lt;br /&gt;&lt;br /&gt;There is light at the end of the tunnel however.&lt;br /&gt;&lt;br /&gt;This &lt;a href="http://www.oracle-base.com/articles/9i/ExportBlob9i.php"&gt;article&lt;/a&gt; was sent to me by another colleague. At the end there is a solution to the problem. Cheers &lt;a href="http://www.kristianjones.co.uk/"&gt;Kris&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;"&lt;span style="font-style: italic;"&gt;Oracle 10g includes extra open modes (rb, wr, ab) to signify byte mode  operation. The "wb" open mode can be used along with the &lt;/span&gt;&lt;code style="font-style: italic;"&gt;PUT_RAW&lt;/code&gt;&lt;span style="font-style: italic;"&gt;  procedure to prevent extra newline characters being added on a Windows platform&lt;/span&gt;"&lt;br /&gt;&lt;br /&gt;I shall refrain from any Windows bashing as it would be too easy as the problem does not occur on Linux. If anyone else wants to jump in and have a dig at Microsoft, feel free.&lt;br /&gt;&lt;br /&gt;Here is a &lt;a href="http://forums.oracle.com/forums/thread.jspa?threadID=461187&amp;tstart=0"&gt;link&lt;/a&gt; to the a thread on the Application Express Discussion Forum as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-6767118522606067169?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/6767118522606067169/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=6767118522606067169' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6767118522606067169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6767118522606067169'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/01/new-line-character-using-utlfile.html' title='New Line Character Using UTL_FILE'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-7672591735850388664</id><published>2007-01-05T06:07:00.000-08:00</published><updated>2007-01-05T06:25:22.796-08:00</updated><title type='text'>Reference JavaScript from the File System in Application Express</title><content type='html'>The strangest thing &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_0"&gt;happened&lt;/span&gt; the other day whilst performing an &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_1"&gt;application&lt;/span&gt; import within Oracle XE. The import was &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_2"&gt;successful&lt;/span&gt; but running the application however was less so.&lt;br /&gt;&lt;br /&gt;Some of my pages contained a lot of JavaScript in the HTML Header section of the Page Attributes. On some of these pages, the import appeared to add a carriage return to one of the lines and thus breaking my script. The knock on effect was that my &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;application&lt;/span&gt; ground to a rather unimpressive halt.&lt;br /&gt;&lt;br /&gt;Without having the time to investigate why, I decided that it would be better to store all my JavaScript in a .&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4" onclick="BLOG_clickHandler(this)"&gt;js&lt;/span&gt; file on the server and write a reference to it on the pages where the scripts were needed.&lt;br /&gt;&lt;br /&gt;Once you have created your .&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5" onclick="BLOG_clickHandler(this)"&gt;js&lt;/span&gt; file (I store in a sub directory if the /i/ path) on the server, you can &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_6"&gt;reference&lt;/span&gt; these files by adding the following to the HTML Header of your page:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bp0.blogger.com/_iJIfjmflR6Y/RZ5faScTa-I/AAAAAAAAAAU/iIXtD1OH3UQ/s1600-h/test.bmp"&gt;&lt;img id="BLOGGER_PHOTO_ID_5016551940169100258" style="CURSOR: hand" alt="" src="http://bp0.blogger.com/_iJIfjmflR6Y/RZ5faScTa-I/AAAAAAAAAAU/iIXtD1OH3UQ/s400/test.bmp" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Storing your long &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_7"&gt;JavaScript&lt;/span&gt; externally to your application also makes debugging a bit easier as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-7672591735850388664?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/7672591735850388664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=7672591735850388664' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7672591735850388664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/7672591735850388664'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/01/reference-javascript-from-file-system.html' title='Reference JavaScript from the File System in Application Express'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_iJIfjmflR6Y/RZ5faScTa-I/AAAAAAAAAAU/iIXtD1OH3UQ/s72-c/test.bmp' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-4984428480196926621</id><published>2007-01-05T05:30:00.000-08:00</published><updated>2007-01-05T05:48:30.814-08:00</updated><title type='text'>Goodbye Notepad, Hello Firebug</title><content type='html'>For those web developers out there using &lt;a href="http://www.mozilla.com/en-US/firefox/"&gt;FireFox&lt;/a&gt;,you have to install the &lt;a href="https://addons.mozilla.org/firefox/1843/"&gt;Firebug&lt;/a&gt; extension. It lets you explore the DoM in far more detail than ever before.&lt;br /&gt;&lt;br /&gt;Simply download and install the extension, restart FireFox and hit F12. You can then see all your HTML source, JavaScript, CSS etc. It also has a cool debugging tool for JavaScript that lets you step through line at a time.&lt;br /&gt;&lt;br /&gt;I can't tell you how much time this extension has saved me this year. Get it, install it and give it a whirl. You wont be disappointed I promise.&lt;br /&gt;&lt;br /&gt;Microsoft have released their &lt;a href="http://www.microsoft.com/downloads/details.aspx?familyid=e59c3964-672d-4511-bb3e-2d5e1db91038&amp;displaylang=en"&gt;Developer Toolbar&lt;/a&gt; for Internet Explorer which goes some way to providing the same functionality for the Microsoft die hards out there but its a million miles away from our friends over at Mozilla.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-4984428480196926621?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/4984428480196926621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=4984428480196926621' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/4984428480196926621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/4984428480196926621'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/01/goodbye-notepad-hello-firebug.html' title='Goodbye Notepad, Hello Firebug'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7925752970520996999.post-6595389980658725969</id><published>2007-01-04T01:19:00.000-08:00</published><updated>2007-01-04T01:29:24.089-08:00</updated><title type='text'>The New Year Blues</title><content type='html'>Well, finally made it into work on the 3rd January and it's always tough after the holidays. However its not long to wait until the 13th January when I will be spending a week in Meribel. Hopefully we will all return in one piece this year as well. My girlfriend had quite a nasty accident last year in Austria which resulted in a large laceration to her leg and required several rather large stitches.&lt;br /&gt;&lt;br /&gt;I digress however and just wanted to offer some advice for those who are struggling with the "back to work" bug doing the rounds.  A colleague of mine told me yesterday that his girlfriend always has to have a holiday to look forward to. I thought this was a brilliant way to beat off those New Year Blues and so why not start planning your Easter getaway. Surly the prospect of another holiday is the best way to cure the disappointment of one just ending.........&lt;br /&gt;&lt;br /&gt;Just a thought.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7925752970520996999-6595389980658725969?l=djmein.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://djmein.blogspot.com/feeds/6595389980658725969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7925752970520996999&amp;postID=6595389980658725969' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6595389980658725969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7925752970520996999/posts/default/6595389980658725969'/><link rel='alternate' type='text/html' href='http://djmein.blogspot.com/2007/01/new-year-blues.html' title='The New Year Blues'/><author><name>Duncan Mein</name><uri>http://www.blogger.com/profile/07134700830240982976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>
