MacLochlainns Weblog

Michael McLaughlin's Technical Blog

Site Admin

Hobbyist at Heart

without comments

Fifteen years ago when I dove into JavaScript friends called me a hobbyist and said I was wasting my time. They had some basis in what they said because most of my work is server side with the database or operating system. However, they haven’t said that I wasted my time with the advent of NoSQL solutions, like Cassandra and MongoDB; or frameworks like node.js. Now writing JavaScript is mainstream and an invaluable skill set, and they’re trying to catch up. While David Flanagan’s JavaScript: The Definitive Guide is comprehensive, I recommend my students start with David McFarland’s JavaScript & jQuery: The Missing Manual. I also strongly suggest they use jQuery first and write JavaScript when jQuery isn’t suitable.

CSS3: The Missing ManualI really liked David McFarland’s CSS: The Missing Manual when it came out in 2009, and I’ve recommended it for several years. However, he’s got a new version – CSS3: The Missing Manual that came out in late January 2014. It’s an improvement over his first volume and I’d recommend you upgrade if you’re writing, modifying, or maintaining Cascading Style Sheet or if you just want to learn more about CSS.

Fortunately for me, CSS3: The Missing Manual is available through iTunes for Apple users, Naturally, it’s also available on Safari and Kindle formats. As an Apple user, I opted for the iBook format for my iPad Air. Unfortunately, it’s $27.99 as an iBook compared to $15.49 on Kindle, and that almost makes me opt to use the Kindle App. ;-)

Written by maclochlainn

March 16th, 2014 at 4:20 pm

Posted in CSS,CSS3,JavaScript,jQuery,JSON

Tagged with , , , ,

Speaking at UTOUG 2014

with one comment

Oracle Database 12c PL./SQL ProgrammingI’m off shortly to attend and speak at the Utah Oracle User Group’s Spring Training Days 2014, which is held at Salt Lake Community College – Larry H. Miller Campus. My presentation is on writing PL/SQL for Oracle Database 12c. We’ll be covering how you can best anchor PL/SQL cursors to include or exclude hidden columns, and how to work with identifying columns in the scope of multiple table inserts.

I have copy of Oracle Database 12c PL/SQL Programming to give away in my session and John Harper’s session. While the book is also available on Kindle, Mac OS X users can get it as a natively as an iBook on the Apple Store. I’ve got a copy on my iPad and it looks great. What I especially like is the search feature.

Written by maclochlainn

March 11th, 2014 at 1:03 pm

Posted in Oracle,Oracle 12c,UTOUG

Tagged with ,

Word 2010 – Add Template

without comments

You can add a Word Template without a problem until you get to Word 2010 because they’re hidden under the Developer ribbon, which is disabled by default. It’s funny something so widely used would be placed on a ribbon that is disabled by default.

It didn’t come up in the first few Google search pages. I figured that I’d blog it because it took me more than a couple minutes.

You can enable the ribbon with the following steps:

  1. Click the File menu choice as shown in the image.

WordTemplate01

  1. Click the Options menu item as shown in the image.

WordTemplate02

  1. Click the Customize Ribbon menu item from the left column.

WordTemplate03

  1. After clicking the Customize Ribbon menu item, the dialog on the right changes as shown below.

WordTemplate04

  1. Click the Developer checkbox to enable the Developer Ribbon that lets you assign a macro template.

WordTemplate05

  1. AFter enabling the Developer Ribbon, it now appears whenever you open or launch Word 2010.

WordTemplate06

Written by maclochlainn

March 9th, 2014 at 3:35 pm

Find Type Dependents

without comments

Somebody thought it was nice to show how to drop object type dependents in Oracle Database 11g, but they thought I should show how you discover dependent object types first. More or less, they were concerned how they discover type dependents when they raise the following error:

DROP TYPE item_object
*
ERROR at line 1:
ORA-02303: cannot DROP OR REPLACE a TYPE WITH TYPE OR TABLE dependents

They had a great point, so here I’m providing and object table function (object table functions are covered in Chapter 8 of Oracle Database 12c PL/SQL Programming book) that displays an object dependency tree.

Unlike table functions in Microsoft SQL Server or PostgreSQL, Oracle requires that you create the object type before you can return a result set table from a parameter-driven function. Here’s the object type for this object table function:

1
2
3
4
5
6
7
8
9
10
11
-- Create an object type that mimics a record structure.  
CREATE OR REPLACE TYPE type_tree IS object
( level_id     NUMBER
, type_name    VARCHAR2(30)
, object       VARCHAR2(10)
, parent_type  VARCHAR2(30)); 
/
 
-- Create a UDT Collection.
CREATE OR REPLACE TYPE type_tree_table IS TABLE OF type_tree;
/

Now, you can create the object table function that returns an object type dependent tree. This function uses branching recursion because you need to walk the dependency tree. The cursor against the three types of metadata in the database catalog lets us walk the tree. Each object type, and their dependents may have:

  • A dependent object type
  • A dependent collection, which uses the object type as a base object type.
  • A dependent schema function, which uses the object type as a parameter data type or return data type.
  • A dependent schema procedure, which uses the object type as a parameter data type.
  • A dependent package, which contains a function or procedure using the object type as a parameter data type, or a function using the object type as a return data type.

Here’s the type_dependent function that walks the tree and displays the hierarchical results:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
CREATE OR REPLACE
  FUNCTION type_dependents
  ( pv_base_object_type  VARCHAR2
  , pv_level_id          NUMBER DEFAULT 1
  , pv_collection        TYPE_TREE_TABLE DEFAULT NULL ) RETURN TYPE_TREE_TABLE IS
 
  /* Declare a return data type. */
  lv_level_id     NUMBER;
 
  /* Declare item type. */
  lv_type_name    VARCHAR2(30);
  lv_object_type  VARCHAR2(30);
  lv_parent_type  VARCHAR2(30);
 
  /* Declare a collection variable. */
  lv_collection   TYPE_TREE_TABLE := type_tree_table();
  lv_incoming     TYPE_TREE_TABLE := type_tree_table();
 
  /* The first part of the cursor finds the dependent type names of complex object 
     types, and the second part of the cursor finds the dependent collection types.
     Effectively the set operator finds two distinct branches because you may use
     any base type as an element of a complex object or of a collection. */
  CURSOR base_type 
  ( cv_level_id   NUMBER
  , cv_base_type  VARCHAR2 ) IS
    SELECT  (cv_level_id) AS level_id
    ,        LPAD(' ', 2*(cv_level_id - 1)) || ut.type_name AS type_name
    ,        ut.typecode AS object_type
    ,        NULL AS parent_type
    FROM     user_types ut
    WHERE    ut.type_name = cv_base_type;
 
  CURSOR dependent_type 
  ( cv_level_id   NUMBER
  , cv_base_type  VARCHAR2 ) IS
    SELECT   cv_level_id AS level_id
    ,        LPAD(' ', 2*(cv_level_id - 1)) || uta.type_name AS type_name
    ,        ut.typecode AS object_type
    ,        ut.type_name AS parent_type
    FROM     user_type_attrs uta INNER JOIN user_types ut
    ON       uta.attr_type_name = ut.type_name
    WHERE    ut.type_name = cv_base_type
    UNION ALL
    SELECT   cv_level_id AS level_id
    ,        LPAD(' ', 2*(cv_level_id - 1)) || uct.type_name AS type_name
    ,        CASE
               WHEN uct.coll_type = 'TABLE' THEN
                 uct.coll_type
               ELSE
                'VARRAY'
             END AS object_type
    ,        ut.type_name AS parent_type
    FROM     user_types ut INNER JOIN user_coll_types uct
    ON       ut.type_name = uct.elem_type_name
    WHERE    uct.elem_type_name = cv_base_type
    UNION ALL
    SELECT   cv_level_id AS level_id
    ,        CASE
               WHEN package_name IS NULL THEN
                 LPAD(' ', 2*(cv_level_id - 1)) || ua.object_name
               ELSE
                 LPAD(' ', 2*(cv_level_id - 1)) || ua.package_name
             END AS type_name
    ,        CASE
               WHEN package_name IS NULL THEN
                 uo.object_type
               ELSE
                'PACKAGE'
             END AS object_type
    ,        ua.type_name AS parent_type
    FROM     user_arguments ua LEFT JOIN user_objects uo
    ON       ua.package_name = uo.object_name
    OR       ua.object_name = uo.object_name
    WHERE    type_name = cv_base_type
    ORDER BY object_type;
 
BEGIN
 
  /* Set the call level indicator. */
  lv_level_id := pv_level_id;
 
  /* Check if the collection holds values, which should occur in recursive calls.  */
  IF pv_collection IS NOT NULL THEN
    lv_collection := pv_collection;
  ELSE
    /* Open the base type and record level 1. */
    OPEN  base_type (lv_level_id, pv_base_object_type);
    FETCH base_type
    INTO  lv_level_id
    ,     lv_type_name
    ,     lv_object_type
    ,     lv_parent_type;
    CLOSE base_type;
 
    /* Extend the collection. */
    lv_collection.EXTEND;
    lv_collection(lv_collection.COUNT) := type_tree( lv_level_id
                                                   , lv_type_name
                                                   , lv_object_type
                                                   , lv_parent_type );
 
    /* Increment the type dependency level. */
    lv_level_id := lv_level_id + 1;
 
  END IF;
 
  /* Loop through and return records. */
  FOR i IN dependent_type(lv_level_id, pv_base_object_type) LOOP
 
    /* Extend the collection. */
    lv_collection.EXTEND;
    lv_collection(lv_collection.COUNT) := type_tree( i.level_id
                                                   , i.type_name
                                                   , i.object_type
                                                   , i.parent_type );
 
    /* Recursively call down to dependent types. */
    IF i.type_name <> i.parent_type THEN
      lv_collection := type_dependents(TRIM(i.type_name), (lv_level_id + 1), lv_collection);
    END IF;
 
  END LOOP;
 
  /* Return 0 for false. */
  RETURN lv_collection;
END;
/

You would query the function with the TABLE function, like this:

COLUMN level_id     FORMAT 99999 HEADING "Level|ID #"
COLUMN type_name    FORMAT A30   HEADING "Type Name"
COLUMN object       FORMAT A10   HEADING "Object Type"
COLUMN parent_type  FORMAT A30   HEADING "Parent Type"
 
SELECT   *
FROM     TABLE(type_dependents('ITEM_OBJECT',1));

It displays the following results based on the sample types created by the original blog post:

 Level
  ID # Type Name                      Object Typ Parent Type
------ ------------------------------ ---------- ------------------------
     1 ITEM_OBJECT                    OBJECT
     2   GET_ITEM_OBJECT              FUNCTION   ITEM_OBJECT
     2   IDENTIFIED_OBJECT            OBJECT     ITEM_OBJECT
     2   ITEM_PACKAGE                 PACKAGE    ITEM_OBJECT
     2   ITEM_TABLE                   TABLE      ITEM_OBJECT
     3     ITEM_ASYNC_TABLE           COLLECTION ITEM_TABLE
     4       ITEM_LIST                TABLE      ITEM_ASYNC_TABLE
     2   ITEM_VARRAY                  VARRAY     ITEM_OBJECT
     3     ITEM_ASYNC_VARRAY          COLLECTION ITEM_VARRAY
     4       ITEM_ARRAY               VARRAY     ITEM_ASYNC_VARRAY
 
10 rows selected.

I hope this helps those working with Oracle Database 10g (de-supported), 11g, or 12c. As a note, you don’t need to drop type dependents in Oracle 12c because it supports type evolution. I cover type evolution in Appendix B of Oracle Database 12c PL/SQL Programming. As always, I hope this helps those trying to determine type dependents in an Oracle database.

Written by maclochlainn

March 8th, 2014 at 10:33 am

PostgreSQL Install-Windows

with one comment

A number of folks have asked me to create a post of a PostgreSQL installation on Windows. So, here is my step-by-step PostgreSQL 9.3 installation.

Download PostgreSQL Database

You can start the download of PostgreSQL from this site. It leads you to the EnterpriseDB site, where you can download the prepackaged software.

Install PostgreSQL 9.3 on Windows

These steps walk you through installing PostgreSQL and the the pgJDBC v9.3, psqlODBC (32 bit), and psqlODBC (64 bit) packages.

PostgreSQL_i01

  1. The first thing you need to do is launch the PostgreSQL file from download folder.

PostgreSQL_i02

  1. After launching the downloaded file, you see an Open File – Security Warning dialog message. Click the Run button to run the PostgreSQL executable file.

PostgreSQL_i03

  1. The first dialog screen is the Setup – PostgreSQL dialog, which welcomes you and prompts you to start the installtion of PostgreSQL. Click the Next button to continue.

PostgreSQL_i04

  1. The Installation Directory dialog sets the location for the PostgreSQL server. Click the Next button to continue.

PostgreSQL_i05

  1. The Data Directory dialog sets the location for the PostgreSQL server’s data repository. Click the Next button to continue.

PostgreSQL_i06

  1. The first Setup dialog sets the database’s superuser password. Click the Next button to continue.

PostgreSQL_i07

  1. The second Setup dialog sets the database’s listener port, which is Port 5432 by default. Click the Next button to continue.

PostgreSQL_i08

  1. The third Setup dialog sets the database’s default Locale. Click the Next button to continue.

PostgreSQL_i09

  1. The Ready to Install dialog lets you start the installation. Click the Next button to continue.

PostgreSQL_i10

  1. The Installing dialog lets you montor the unpacking of programs for the installation. You need not click any buttons to continue.

PostgreSQL_i11

  1. The Completing the PostgreSQL Setup Wizard dialog advises you that you’ve completed the setup. Checking the Stack Builder check box lets you download and install additional tools, drivers, and applications. Click the Next button to continue.

PostgreSQL_i12

  1. This is Welcome dialog to the Stack Builder. Click the Next button to continue.

PostgreSQL_i13

  1. This is dialog lets you select applications that you would like to install. Click the plus symbol for the Database Drivers option to continue.

PostgreSQL_i14

  1. In the modified dialog, click the pgJDBC v9.3, psqlODBC (32 bit), and psqlODBC (64 bit) check boxes. Then, click the Next button to continue.

PostgreSQL_i15

  1. This dialog tells you the pgJDBC v9.3, psqlODBC (32 bit), and psqlODBC (64 bit) installed packages. Click the Next button to continue.

PostgreSQL_i16

  1. This dialog lays a downloading progress bar while loading the additional packages. You don’t need to do anything to continue.

PostgreSQL_i17

  1. This dialog lays a downloading progress bar while loading the additional packages. Click the Next button to continue.

PostgreSQL_i18

  1. The Setup pgJDBC dialog welcomes you to the setup wizard. Click the Next button to continue.

PostgreSQL_i19

  1. The Installation Directory dialog lets you set the pgJDBC installation directory. Click the Next button to continue.

PostgreSQL_i20

  1. The Ready to Install dialog lets you install the pgJDBC package. Click the Next button to install it.

PostgreSQL_i21

  1. The Setup dialog confirms you’ve installed the add-on packages. Click the Finish button to complete the installation.

Configure psqlODBC on Windows

These steps walk you through configuring the psqlODBC packages.

PostgreSQL_i22

  1. The Setup dialog helps you configure the psqlODBC package. Click the Next button to install it.

PostgreSQL_i23

  1. The Installation Directory dialog lets you set the psqlODBC installation directory. Click the Next button to continue.

PostgreSQL_i23

  1. The Ready to Install dialog lets you install the psqlODBC package. Click the Next button to install it.

PostgreSQL_i24

  1. The Ready to Install dialog lets you install the psqlODBC package. Click the Next button to install it.

PostgreSQL_i25

  1. The Installing psqlODBC dialog displays a dialog while installing the psqlODBC package(s). You need not click anything, the dialog dismisses itself when complete.

PostgreSQL_i26

  1. The Installing psqlODBC dialog displays a dialog while installing the psqlODBC package(s). You need not click anything, the dialog dismisses itself when complete.

PostgreSQL_i27

  1. The Stack Builder dialog displays a message that everything is installed. Click the Finish button when complete.

Connect to the Default PostgreSQL database

You have two options for connecting to the PostgreSQL database. One uses the GUI pgAdmin III console and the other uses the command line.

Connect through pgAdmin III to the PostgreSQL Database

PostgreSQL_i27

  1. In Windows, navigate to through the Start to the pgAdmin III menu option.

PostgreSQL_i28

  1. After launching pgAdmin III, you’ll see the pgAdmin II console, displayed at left.

PostgreSQL_i29

  1. Right click on the PostgreSQL 9.3 (localhost:5432) item in the Object browser and click the Connect menu choice from the floating menu.

PostgreSQL_i30

  1. Enter the password you chose when installing the PostgreSQL Server, and click the OK button.

Connect through the Command-line to the PostgreSQL Database

These steps show you how to connect through the PostgreSQL Command Line Interface (CLI) – psql. psql is like Oracle’s sqlplus, MySQL’s mysql, and Microsoft SQL Server’s sqlcmd CLI interfaces. By default, the PostgreSQL binary directory is not in your default %PATH% environment variable, so you need to add it. I recommend an environment file because in a test situation you may not want to use it all the time.

Create the following directory for the environment file:

C:\Data\PostgreSQL\env

You can create an environment file with any text editor or do it at the command line, like this:

COPY CON postgresqlenv.bat
SET PATH=%PATH%;C:\Program Files\PostgreSQL\9.3\bin;.
^Z

Then, you can source the environment file like this:

C:\> postgresql.bat

Now, you can connect to the default database provided you understand that PostgreSQL Stack Builder set some values that differ from the defaults if you installed it manually. Specifically, they set the default value of the database to postgres and default value of the user to postgres.

That means you can connect with the following, provided you remember the password you used during installation:

C:\> psql -d postgres -U postgres

You should see the following when you connect:

C:\> psql -d postgres -U postgres
 Password for user postgres:
psql (9.3.3)
WARNING: Console code page (437) differs from Windows code page (1252)
         8-bit characters might not work correctly. See psql reference
         page "Notes for Windows users" for details.
Type "help" for help.

You can now submit interactive SQL statements or run batch files, like:

postgres=# SELECT 'Hello World!';

It displays:

   ?COLUMN?
--------------
 Hello World!
(1 ROW)

Adding a column alias to the string literal gives a more readable formatted output:

postgres=# SELECT 'Hello World!' AS "String Literal";

It displays:

 String Literal
----------------
 Hello World!
(1 ROW)

PostgreSQL’s CLI Help

You have the ability to do a lot with the PostgreSQL psql CLI. The general command to discover help is help, and it displays the following:

postgres=# help
You are USING psql, the command-line interface TO PostgreSQL.
TYPE:  \copyright FOR distribution terms
       \h FOR help WITH SQL commands
       \? FOR help WITH psql commands
       \g OR terminate WITH semicolon TO EXECUTE query
       \q TO quit

You quit PostgreSQL with a \q command:

postgres=# \q

Have fun exploring PostgreSQL. You can click here to learn how to setup your own tablespace, database, and user. As always, I hope this helps those looking to discover how to install and use PostgreSQL.

Written by maclochlainn

March 2nd, 2014 at 7:59 pm

Add User Defined Types

with one comment

Somebody asked me if there was a cheaper alternative to using the Embarcadero Data Architect (a data modeling tool). I said sure, you can use the MySQL Workbench. My friend laughed and said, it’s to model Oracle databases and they use different data types. I broke the news to him that he can create his own user defined types and use MySQL Workbench to model problems for the Oracle Database 11g.

For example, you can launch the MySQL Workbench, and click on the Model menu option, and in the menu window click on the User Defined Types choice, as shown in the following:

UserDefinedType

Choosing the User Defined Type option, launches the following form. You can enter customized user defined types in the User Defined Types module:

MySQLWB_UserDefinedTypes01

You enter user defined types by entering a name value and choosing valid MySQL type value before clicking the Add button. When you’ve added your last user defined type, click the OK button instead of the Add button. The next screen shot shows how you can create Oracle Database 11g native data types, specifically the NUMBER and VARCHAR2 data types.

MySQLWB_UserDefinedTypes02

Hopefully, this has shown that you can create User Defined Types let you use MySQL Workbench to create Oracle ERD models. Here’s an example of a table with Oracle’s NUMBER and VARCHAR2 data types:

CustomGLTable

Yes, MySQL Workbench is a marvelous tool with wide potential for use to solve problems with MySQL and other databases.

Written by maclochlainn

February 28th, 2014 at 2:03 am

Relationship Notations

without comments

One of my students asked how to convert MySQL Workbench’s default Crow’s Foot (IE) diagram to one of the other supported formats – Classic, Connect to Columns, UML, and IDEF1X. Crow’s Foot is also known as the Information Engineering Model method (covered in Chapter 3 of my MySQL Workbench: Data Modeling & Development.

It quite simple, you open the Model Overview window, click on the Model menu choice. In the dialog, click on the Relationship Notation menu option. Click on one of the choices in the nested menu, like Column to Columns.

ModifyRelationNotations

Hope this helps those working with MySQL Workbench.

Written by maclochlainn

February 27th, 2014 at 1:22 am

MySQL Image Architecture

with 14 comments

The LinkedIn MySQL DB Development group posed a questions on how to handle images. Naturally, the argument always goes: Should images be deployed in the database or the file system? I believe they should be stored in the database because the cost and time associated is too high with regard to managing files, a file naming schema, and backing up the file system discretely from the database.

Since there’s a significant difference between the backup of transactional data and image data, they should be placed in different databases. The imagedb database is where you would place the images and large text descriptions, as shown in the MySQL Workbench ERD:

imagedb ERD

The imagedb ERD splits the foreign key references back to the system_user table, which contains the individual user credentials. The system_user table serves as the Access Control List (ACL) for the application.

Until I get a chance to write the code for this model, you can refer to the generic PHP/MySQL solution from several years back (its code source was last tested with PHP 5.3). As always, I hope this helps.

Written by maclochlainn

February 23rd, 2014 at 2:02 am

Drop Types Recursively

with one comment

As covered in my new Oracle Database 12c PL/SQL Programming book (publisher’s satisfied), you can evolve object types. That means you can change a base object type and the change cascades through dependents. Somebody asked how to remove an object type chain without appending the FORCE clause.

It’s quite easy if you understand writing a recursive function in PL/SQL, as done here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
-- Create a recursive function.
CREATE OR REPLACE
  FUNCTION drop_dependents
  (pv_base_object_type  VARCHAR2) RETURN NUMBER IS
 
  /* Declare a return data type. */
  lv_retval  NUMBER := 0;
 
  /* Declare item type. */
  lv_type_name    VARCHAR2(30);
  lv_object_name  VARCHAR2(30);
 
  /* The first part of the cursor finds the dependent type names of complex object 
     types, and the second part of the cursor finds the dependent collection types.
     Effectively the set operator finds two distinct branches because you may use
     any base type as an element of a complex object or of a collection. */
  CURSOR base_type 
  (cv_base_type  VARCHAR2) IS
    SELECT   uta.type_name
    ,        NULL AS object_name
    FROM     user_type_attrs uta INNER JOIN user_types ut
    ON       uta.attr_type_name = ut.type_name
    WHERE    ut.type_name = cv_base_type
    UNION ALL
    SELECT   uct.type_name
    ,        NULL AS object_name
    FROM     user_types ut INNER JOIN user_coll_types uct
    ON       ut.type_name = uct.elem_type_name
    WHERE    uct.elem_type_name = cv_base_type
    UNION ALL
    SELECT   CASE
               WHEN package_name IS NULL THEN
                 uo.object_type
               ELSE
                 'PACKAGE'
             END AS type_name
    ,        CASE
               WHEN package_name IS NULL THEN
                 ua.object_name
               ELSE
                 ua.package_name
             END AS object_name
    FROM     user_arguments ua LEFT JOIN user_objects uo
    ON       ua.package_name = uo.object_name
    OR       ua.object_name = uo.object_name
    WHERE    type_name = cv_base_type;
 
BEGIN
 
  /* Open a parameterized cursor. */  
  OPEN  base_type(pv_base_object_type);
 
  /* Loop through return records. */
  LOOP
    /* Fetch records. */
    FETCH base_type
    INTO  lv_type_name
    ,     lv_object_name;
 
    /* Drop type without dependents, or drop leaf node dependent. */
    IF base_type%NOTFOUND THEN
 
      /* Drop functions when they include an object type or object type
         dependent as a formal parameter type or return type. Drop
         procedures when they include an object type or object type
         dependent. Drop procedures when any function or procedure
         uses an object type or object type dependent. */
      IF lv_type_name IN ('FUNCTION','PACKAGE','PROCEDURE') THEN
 
        /* Drop the base type when no dependents are found. */
        EXECUTE IMMEDIATE 'DROP '||lv_type_name||' '||lv_object_name;
 
      ELSE
 
        /* Drop the base type when no dependents are found. */
        EXECUTE IMMEDIATE 'DROP TYPE '||pv_base_object_type;
 
      END IF;
 
      /* Set exit state to one or true. */
      lv_retval := 1;
 
      /* Exit the loop. */
      EXIT;
 
    ELSE
 
      /* A type must exclude function, package, and procedure; and the
         object name must be null before you recurse to another level. */
      IF lv_type_name NOT IN ('FUNCTION','PACKAGE','PROCEDURE') AND
         lv_object_name IS NOT NULL THEN
 
        /* Drop base type when no dependents are found. */
        lv_retval := drop_dependents(lv_type_name);
 
      END IF;
 
    END IF;
  END LOOP;
 
  /* Close open cursor. */
  CLOSE base_type;
 
  /* Return 0 for false. */
  RETURN lv_retval;
END;
/

Somebody asked me to provide a test case of a hierarchy of object types to support the drop_dependents function. So, here’s the test case code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
-- Create object type.
CREATE OR REPLACE 
  TYPE item_object IS OBJECT
  ( item_name     VARCHAR2(30)
  , item_subname  VARCHAR2(30));
/
 
-- Create object type.
CREATE OR REPLACE 
  TYPE identified_object IS OBJECT
  ( identified_id      NUMBER
  , identified_object  item_object);
/
 
-- Create object collection.
CREATE OR REPLACE
  TYPE item_table IS TABLE OF item_object;
/
 
-- Create object collection.
CREATE OR REPLACE
  TYPE item_varray IS VARRAY(5) OF item_object;
/
 
-- Create object type.
CREATE OR REPLACE 
  TYPE item_async_table IS OBJECT
  ( item_name        VARCHAR2(30)
  , item_collection  item_table);
/
 
-- Create object type.
CREATE OR REPLACE 
  TYPE item_async_varray IS OBJECT
  ( item_name        VARCHAR2(30)
  , item_collection  item_varray);
/
 
-- Create object collection.
CREATE OR REPLACE
  TYPE item_list IS TABLE OF item_async_table;
/
 
-- Create object collection.
CREATE OR REPLACE
  TYPE item_array IS VARRAY(10) OF item_async_varray;
/
 
-- Create package specification.
CREATE OR REPLACE PACKAGE item_package AS
 
  /* A published function of the package. */
  FUNCTION initialize_object
  ( id    NUMBER
  , name  NUMBER ) RETURN ITEM_OBJECT;
 
END item_package;
/
 
-- Create a schema function.
CREATE OR REPLACE FUNCTION get_item_object
  ( pv_id    NUMBER
  , pv_name  NUMBER ) RETURN ITEM_OBJECT IS
 
  /* Declare a local variable. */
  lv_item_object   ITEM_OBJECT;
 
BEGIN
 
  /* Initialize the object type. */
  lv_item_object := item_object(pv_id, pv_name);  
 
  /* Return the dat type. */
  RETURN lv_item_object;
END;
/

If you call the function with the base type, it’ll drop the most dependent object type first, and the base object type last. The rest are dropped in their order of dependency. You can call a drop_dependents function with a base type, like ITEM_OBJECT, by using the following syntax:

1
2
3
4
5
6
7
SET SERVEROUTPUT ON SIZE UNLIMITED
BEGIN
  IF drop_dependents('ITEM_OBJECT') = 1 THEN
    dbms_output.put_line('Objects dropped.');
  END IF;
END;
/

Hope this helps those looking to drop a chain of object types in an Oracle database.

Written by maclochlainn

February 18th, 2014 at 3:39 am

Signal from a procedure

without comments

As I review with my students, a stored function works like a standalone program, while a stored procedure runs in the scope of another program unit. For example, you can compare the result of a function as an expression in an IF statement, like:

  IF add_numbers(1,3) > 3 THEN
    ...
  ELSE
    ...
  END IF;

You can’t call procedures inside an IF statement, but you can call the procedure and use a single OUT-mode (pass-by-reference) parameter from the procedure in a subsequent IF statement. You can implement a a wait procedure like that with the following example.

The example first creates two tables, the road_runner and coyote tables:

-- Drop road_runner table if exists.
DROP TABLE IF EXISTS road_runner;
 
-- Create roadrunner table.
CREATE TABLE road_runner
( road_runner_id    int unsigned  auto_increment primary key
, road_runner_text  varchar(20)
, CONSTRAINT road_runner_nk UNIQUE (road_runner_text));
 
-- Drop coyote table if exists.
DROP TABLE IF EXISTS coyote;
 
-- Create coyote table.
CREATE TABLE coyote
( coyote_id       int unsigned  auto_increment primary key
, coyote_text     varchar(20)
, road_runner_id  int unsigned
, CONSTRAINT coyote_nk UNIQUE (coyote_text));

The following creates a procedure that:

  • Writes data to two tables when the values are unique, returning a value of zero when it works.
  • Writes data to neither table when the values to either table are non-unique, returning a value of one when it fails.

The procedure uses a 0 as a false value and a 1 as a true value. The use of a 0 and 1 for truth is a consistent approach for languages where they don’t support a Boolean data type.

-- Change the delimiter to a "$$"
DELIMITER $$
 
-- Drop the paired procedure.
DROP PROCEDURE IF EXISTS two_table$$
 
-- Create the paired procedure.
CREATE PROCEDURE two_table
(IN    pv_road_runner_text  varchar(20)
,IN    pv_coyote_text       varchar(20)
,  OUT pv_confirm_it        int)
BEGIN
  /* Declare a variable to hold a sequence value for an 
     auto incrementing value. */
  DECLARE lv_road_runner_id  int unsigned;
 
  /* Declare a condition variable for attempting to write
     a non-unique record to a table. */
  DECLARE duplicate CONDITION FOR 1062;
 
  /* Declare an event handler for a duplicate condition 
     variable, rollback transaction, and set 1 as a false
     condition. */
  DECLARE EXIT HANDLER FOR duplicate
    BEGIN
      ROLLBACK to all_or_none;
      SET pv_confirm_it = 1;
    END;
 
  /* Start the transaction. */
  START TRANSACTION;
 
  /* Set the save point for a multiple table transaction. */
  SAVEPOINT all_or_none;
 
  /* Insert into road runner table. */
  INSERT INTO road_runner
  (road_runner_text)
  VALUES  
  (pv_road_runner_text);
 
  /* Capture the auto incrementing sequence value as a 
     local variable. */
  SET lv_road_runner_id := last_insert_id();
 
  /* Insert into the coyote table. */
  INSERT INTO coyote
  (coyote_text
  ,road_runner_id)
  VALUES  
  (pv_coyote_text
  ,lv_road_runner_id);
 
  /* Commit the record. */
  COMMIT;
 
  /* Set the control variable to a true value. */
  SET pv_confirm_it := 0;
 
END;
$$
 
DELIMITER ;

You can test the pass-by-reference procedure with the following code:

-- Set a control variable with a null value.
SET @sv_control := null;
 
-- Query the results from a join between the road_runner and coyote tables.
SELECT   *
FROM     road_runner r INNER JOIN coyote c
ON       r.road_runner_id = c.road_runner_id;
 
-- Call the two_table procedure with unique results.
CALL two_table('Road Runner 1','Coyote 1',@sv_control);
 
-- Query the control variable result from the second call to the
-- two_table procedure.
SELECT @sv_control AS "1st Insert";
 
-- Reset the value for a second test.
SET @sv_control := 0;
 
-- Call teh two_table procedure with unique results.  
CALL two_table('Road Runner 2','Coyote 1',@sv_control);  
 
-- Query the results from a join between the road_runner and coyote tables.  
SELECT   *
FROM     road_runner r INNER JOIN coyote c
ON       r.road_runner_id = c.road_runner_id;
 
-- Query the control variable result from the second call to the
-- two_table procedure.
SELECT   @sv_control AS "2nd Insert";

I hope this helps anybody trying to implement a pass-by-reference procedure with a control variable. You can find other examples in Chapter 14 of Oracle Database 11g and MySQL 5.6 Developer Handbook (pp. 446-449 and 450-451).

Written by maclochlainn

February 16th, 2014 at 5:22 pm