You are on page 1of 34

Say Goodbye to Hard-Coding in Your PL/SQL Programs

Steven Feuerstein
PL/SQL Evangelist, Quest Software steven.feuerstein@quest.com
Copyright 2000-2009 Steven Feuerstein - Page 1

How to benefit most from this session


Watch, listen, focus on concepts and principles. Download and use any of my the training materials:

PL/SQL Obsession

http://www.ToadWorld.com/SF

Download and use any of my scripts (examples, performance scripts, reusable code) from the same location: the demo.zip file. filename_from_demo_zip.sql You have my permission to use all these materials to do internal trainings and build your own applications.
But remember: they are not production ready. Modify them to fit your needs and then test them!
Copyright 2000-2006 Steven Feuerstein - Page 2

Say Goodbye to Hard-Coding


What is hard-coding? Why is it a problem? Soft Coding and Easy Coding Identify all sorts of hard-coding in PL/SQL Specific techniques for getting rid of those hard-codings

Copyright 2000-2008 Steven Feuerstein - Page 3

What is Hard Coding? - 1


Traditionally, has referred to practice of placing literal values in the main body of your code.
From Wikipedia: The term "hard-coded" was coined in 1990 by R. Galichon (then a Programmer/Analyst at Mobil). The term was used as an analogy to hardwiring circuits - and was meant to convey the inflexibility which results from its usage within software design and implementation.

More generally, hard-coding is closely tied to the problem of repetition in our code.
Copyright 2000-2008 Steven Feuerstein - Page 4

What is Hard Coding? - 2


You "hard code" every time you write a piece of code that assumes an aspect of your application will not change and therefore can be explicitly referenced throughout the code base. Then when the change takes place, you have to locate all those repetitions and fix them.
Sometimes that's easy, sometimes it is very difficult to do, but in all cases, it causes problems.
Copyright 2000-2008 Steven Feuerstein - Page 5

Why is Hard Coding Bad?


Hard-coding would be fine if nothing ever changed in our applications.
If requirements stayed the same... If the definitions of our tables stayed the same.... If rules and formulas stayed the same.... If configuration constants stayed the same....

Too bad!
Whenever anything changes, you have to find all the places you explicitly coded it, and fix them.
Copyright 2000-2008 Steven Feuerstein - Page 6

Soft Coding and Easy Coding


If hard-coding is bad, then maybe we should do the opposite of hard-coding. Soft Coding
Rather than explicitly code values, rules and algorithms, make them "soft" or dynamic changeable and settable at runtime.

Easy Coding
It's hard fixing hard-codings in multiple places. It'd be easier to fix things in one place. It really does make things easier.
Copyright 2000-2008 Steven Feuerstein - Page 7

Hard-Coding Avoidance: Principles and Concepts


Single point of definition (no repetition)
You should always aim for a single point of definition or SPOD for everything in your application.

Information hiding the name is the thing


Avoid exposing the implementation details of formulas, rules, algorithms, data access. The more you hide, the more flexibility you have.

"Never" and "Always" in software


It's never going to stay the same. It's always going to change.

Copyright 2000-2008 Steven Feuerstein - Page 8

Where's the hard-coding?


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* PROCEDURE process_employee (department_id_in IN NUMBER) IS l_id NUMBER (9, 2); l_salary NUMBER; l_name VARCHAR2 (100); /* Full name: LAST COMMA FIRST (ReqDoc 123.A.47) */ CURSOR emps_in_dept_cur IS SELECT employee_id, salary, last_name || ',' || first_name lname FROM employees WHERE department_id = department_id_in; BEGIN OPEN emps_in_dept_cur; LOOP FETCH emps_in_dept_cur INTO l_id, l_salary, l_name; IF l_salary > 10000000 THEN must_be_ceo; END IF; analyze_compensation (l_id, l_salary); EXIT WHEN emps_in_dept_cur%NOTFOUND; END LOOP; END;

Copyright 2000-2008 Steven Feuerstein - Page 9

Potential Hard-Codings in PL/SQL Code


Literal values
Especially language-specific literals

Constrained declarations
Especially VARCHAR2(n)

Fetch into a list of variables Rules and formulas


Especially the "trivial" ones

SQL statements
??? Very scary to contemplate to be explained later

Algorithmic details
Example: error logging mechanisms
Copyright 2000-2008 Steven Feuerstein - Page 10

Literal Values
The most commonly recognized form of hard-coding. The only place a literal should appear in your code is in its SPOD. Hide literals behind:
constants functions

Or soft code in tables.


Copyright 2000-2008 Steven Feuerstein - Page 11

Constants vs. Functions


Constants are simple and quick, but they expose the value in the package specification.
If the value needs to change, all programs that depend on that package must be recompiled.

Put the value behind a function and then it is hidden.


When the value changes, only the package body must be recompiled. But this is less efficient than a constant.
thisuser*.*
Copyright 2000-2008 Steven Feuerstein - Page 12

Soft-Code Values in Table


You can make things really flexible by putting all literals in a table, associating them with a name, and retrieving them as needed from the table. Downsides are:
More complex code More overhead, but caching can avoid this problem.
soft_code_literals.sql
Copyright 2000-2008 Steven Feuerstein - Page 13

Constrained Declarations
You should consider every declaration of the form VARCHAR2(N) to be a bug.
Unless it is the SPOD for that type.

Instead, anchor the declaration back to its "source".


%TYPE for variables based on columns %ROWTYPE for records based on table/cursor SUBTYPES for all applications-specific types

Copyright 2000-2008 Steven Feuerstein - Page 14

SUBTYPEs
Most everyone knows about %TYPE and %ROWTYPE.
But what if you can't anchor back to a DB element?

You can always use a SUBTYPE to define a "single point of definition" and use that for all subsequent declarations. SUBTYPEs allow you to give application-specific names to types.
Critical when working with complex structures like collections of records, and nested collections.
15

Applying SUBTYPEs
Instead of this:
DECLARE l_full_name VARCHAR2(100); l_big_string VARCHAR2(32767);

You would write this:


DECLARE l_full_name employees_rp.full_name_t; l_big_string plsql_limits.maxvarchar2;

Copyright 2000-2008 Steven Feuerstein - Page 16

fullname.pks plsql_limits.pks string_tracker3.*

Rules and Formulas


Much more critical than repeated literals. What we know about rules and formulas:
They will change. They will get more complex.

What to do?
Learn to recognize rules and formulas. Often they are missed, especially when simple. Hide the logic behind functions.

Copyright 2000-2008 Steven Feuerstein - Page 17

Example: Never trust a rule!


Oracle offers DBMS_UTILITY.GET_TIME and GET_CPU_TIME (10g) to compute elapsed time down to the hundredth of a second.
DECLARE l_start_time PLS_INTEGER; BEGIN l_start_time := DBMS_UTILITY.get_time; -- Do stuff here... DBMS_OUTPUT.put_line (DBMS_UTILITY.get_time l_start_time); END;

And yet so wrong...


get_time.sql sf_timer.*
Copyright 2000-2008 Steven Feuerstein - Page 18

SQL Statements
Why do I talk about SQL in a presentation on hard coding? Because I believe that every SQL statement that you write is a hard-coding!
I know, it is shocking to contemplate.

So what am I talking about?

Copyright 2000-2008 Steven Feuerstein - Page 19

How can SQL be hard coding?


I need to write a three way join to return HR information for a report.
SELECT . . . FROM employees, departments, locations WHERE . . . (a page full of complex conditions)

And Joe needs to use that same query in his business rule procedure. And so on... And then the three way join turns into a four way join and we have to find all occurrences of this query.
A very tough thing to do!
Copyright 2000-2008 Steven Feuerstein - Page 20

What to do about SQL hard coding


Of course, you have to (and should) write SQL statements in your PL/SQL code.
PL/SQL is, in fact, the best place for SQL.

But we should be very careful about where, when and how we write these statements.
Follow the principles; they are your guide. Don't repeat anything!

The best approach: hide SQL inside a data access layer.


Copyright 2000-2008 Steven Feuerstein - Page 21

SQL as a Service
Think of SQL as a service that is provided to you, not something you write.
Or if you write it, you put it somewhere so that it can be easily found, reused, and maintained.

This service consists of programs defined in the data access layer.


Known as table APIs, transaction APIS, or data encapsulation, these programs contain all the intelligence about business transactions and underlying tables.
Copyright 2000-2008 Steven Feuerstein - Page 22

Application Code

Intermediate Layer

Order Table

Item Table

With a data access layer, I can...


Change/improve my implementation with minimal impact on my application code.
The underlying data model is constantly changing. We can depend on Oracle to add new features. We learn new ways to take advantage of PL/SQL.

Vastly improve my SQL-related error handling.


Do you handle dup_val_on_index for INSERTs, too_many_rows for SELECT INTOs, etc?

Greatly increase my productivity


I want to spend as much time as possible implementing business requirements.
Copyright 2000-2008 Steven Feuerstein - Page 23

Example: Quest Code Tester backend


For each table, we have three generated packages:
<table>_CP for DML <table>_QP for queries <table>_TP for types

And usually an "extra stuff" package with custom SQL logic and related code:
<table>_XP
Copyright 2000-2008 Steven Feuerstein - Page 24

qu_outcome_xp.qu_outcomes qu_outcome_xp.int_create_outcomes

How to implement data encapsulation


It must be very consistent, well-designed and efficient - or it will not be used. Best solution: generate as much of the code as possible.
And any custom SQL statements should be written once and placed in a standard container (usually a package).

Powerful and free option for generating table APIs for use with PL/SQL is the freeware Quest CodeGen Utility.
Quest CodeGen Utility: www.ToadWorld.com/SF

Copyright 2000-2008 Steven Feuerstein - Page 25

Hide algorithmic details


Even if the users don't change their minds, we (developers) and Oracle technology change. So assume that whatever you are working on will change and hide it behind an API.
Logging errors Function result cache Manipulating collection contents

Copyright 2000-2008 Steven Feuerstein - Page 26

Logging Errors
We usually, but not always, want to write error information out to a log table. How's this?
WHEN NO_DATA_FOUND THEN l_code := SQLCODE; INSERT INTO errlog VALUES ( l_code , 'No company for id ' || TO_CHAR ( v_id ) , 'fixdebt', SYSDATE, USER ); WHEN OTHERS THEN l_code := SQLCODE; l_errm := SQLERRM; INSERT INTO errlog VALUES (l_code, l_errm, 'fixdebt', SYSDATE, USER ); RAISE; END;

It's easy to "read" but only because it exposes the logging mechanism.
Copyright 2000-2008 Steven Feuerstein - Page 27

Hide how and what you log: shared, generic logging utility This rewrite is based on the Quest Error Manager.
Freeware available at www.ToadWorld.com/SF. You do less work and get more information.
WHEN NO_DATA_FOUND THEN q$error_manager.register_error ( text_in => 'No company for id ' || TO_CHAR ( v_id )); WHEN OTHERS THEN q$error_manager.raise_unanticipated ( name1_in => 'COMPANY_ID', value1_in => v_id); END;

Copyright 2000-2008 Steven Feuerstein - Page 28

The Oracle11g function result cache The optimal way to query and cache data changes over time.
Explicit and implicit cursors FORALL and BULK COLLECT And now the function result cache

With the Result Cache, you specify declaratively that Oracle not run your function over and over for the same inputs.
Instead return the data previously cached with matching inputs.
Copyright 2000-2008 Steven Feuerstein - Page 29

Function Result Cache Example


CREATE OR REPLACE PACKAGE emplu11g IS FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE) RETURN employees%ROWTYPE RESULT_CACHE; END emplu11g; CREATE OR REPLACE PACKAGE BODY emplu11g IS FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE) RETURN employees%ROWTYPE RESULT_CACHE RELIES_ON (employees) IS onerow_rec employees%ROWTYPE; BEGIN SELECT * INTO onerow_rec FROM employees WHERE employee_id = employee_id_in; RETURN onerow_rec; END onerow; END emplu11g;

30

11g_emplu.pkg

Manipulating collection contents


Collections are Oracle's version of arrays in PL/SQL.
A relatively complicated but critically important datatype.

Best to hide collection references behind procedures and functions.


Especially when you work with string-indexed and multi-level collections.
string_tracker3.* cc_smartargs.pkb
Copyright 2000-2008 Steven Feuerstein - Page 31

Hiding stuff a great career move!


By hiding my SQL statements (and all other forms of hard-coding) behind subprograms, I am in a good position to.... Hide my mistakes
Does the query have a bug in it? OK, fix the one instance of the query inside my function. I don't have to tell everyone about it....

Get a promotion
I can improve my application code much more quickly than those who hard-code SQL..... The Result Cache is a great example of this.
32

Say goodbye to hard coding!


It's not all that difficult to do, once you recognize all the different ways that hard coding can manifest itself in your code. Repeat nothing: become allergic to redundant repetition. Aim for a "single point of definition" in everything you write. Hide, hide, hide: values, implementations, workarounds
Copyright 2000-2008 Steven Feuerstein - Page 33

Your Reward
Elegant, functional code that you and others can maintain easily The respect of your peers A deep sense of satisfaction with a job well done The opportunity to continue making a darned good living mostly from just thinking about stuff.
Copyright 2000-2008 Steven Feuerstein - Page 34

You might also like