Copyright 2000-2009 Steven Feuerstein - Page 1
Say Goodbye to Hard-Coding
in Your PL/SQL Programs
Steven FeuersteinPL/SQL Evangelist, Quest Software
Copyright 2000-2006 Steven Feuerstein - Page 2
How to benefit most from this session
Watch, listen, focus on concepts and principles.
Download and use any of my the training materials:
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!
filename_from_demo_zip.sql
Download and use any of my scripts (examples, performance scripts, reusable code) from the same location: the demo.zip file.
http://www.ToadWorld.com/SFPL/SQL Obsession
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?
Copyright 2000-2008 Steven Feuerstein - Page 9
1 PROCEDURE process_employee (department_id_in IN NUMBER)2 IS3 l_id NUMBER (9, 2); l_salary NUMBER;4 l_name VARCHAR2 (100);56 /* Full name: LAST COMMA FIRST (ReqDoc 123.A.47) */7 CURSOR emps_in_dept_cur8 IS9 SELECT employee_id, salary, last_name || ',' || first_name lname
10 FROM employees11 WHERE department_id = department_id_in;12 BEGIN13 OPEN emps_in_dept_cur;1415 LOOP16 FETCH emps_in_dept_cur17 INTO l_id, l_salary, l_name;1819 IF l_salary > 1000000020 THEN21 must_be_ceo;22 END IF;2324 analyze_compensation (l_id, l_salary);25 EXIT WHEN emps_in_dept_cur%NOTFOUND;26 END LOOP;27* END;
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.
Copyright 2000-2008 Steven Feuerstein - Page 12thisuser*.*
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.
Copyright 2000-2008 Steven Feuerstein - Page 13
soft_code_literals.sql
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
15
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.
Applying SUBTYPEs
Instead of this:
Copyright 2000-2008 Steven Feuerstein - Page 16
DECLAREl_full_name VARCHAR2(100);l_big_string VARCHAR2(32767);
You would write this:
DECLAREl_full_name employees_rp.full_name_t;l_big_string plsql_limits.maxvarchar2;
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.
Copyright 2000-2008 Steven Feuerstein - Page 18
DECLAREl_start_time PLS_INTEGER;
BEGINl_start_time := DBMS_UTILITY.get_time;-- Do stuff here...DBMS_OUTPUT.put_line (DBMS_UTILITY.get_time – l_start_time);
END;
get_time.sql
sf_timer.*
And yet so wrong...
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.
Copyright 2000-2008 Steven Feuerstein - Page 20
SELECT . . .FROM employees, departments, locations
WHERE . . . (a page full of complex conditions)
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!
And Joe needs to use that same query in his
business rule procedure. And so on...
Copyright 2000-2008 Steven Feuerstein - Page 21
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 22
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.
Order
Table
Item
Table
Application
Code
Intermediate Layer
Copyright 2000-2008 Steven Feuerstein - Page 23
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 24
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>_XPqu_outcome_xp.qu_outcomes
qu_outcome_xp.int_create_outcomes
Copyright 2000-2008 Steven Feuerstein - Page 25
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
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?
Copyright 2000-2008 Steven Feuerstein - Page 27
WHEN NO_DATA_FOUND THENl_code := SQLCODE;INSERT INTO errlogVALUES ( l_code
, 'No company for id ' || TO_CHAR ( v_id ), 'fixdebt', SYSDATE, USER );
WHEN OTHERS THENl_code := SQLCODE; l_errm := SQLERRM;INSERT INTO errlogVALUES (l_code, l_errm, 'fixdebt', SYSDATE, USER );
RAISE;END;
It's easy to "read" but only because it exposes
the logging mechanism.
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.
Copyright 2000-2008 Steven Feuerstein - Page 28
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;
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
30
Function Result Cache Example
CREATE OR REPLACE PACKAGE emplu11gIS
FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE)RETURN employees%ROWTYPERESULT_CACHE;
END emplu11g;
CREATE OR REPLACE PACKAGE BODY emplu11gIS
FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE)RETURN employees%ROWTYPERESULT_CACHE RELIES_ON (employees)
ISonerow_rec employees%ROWTYPE;
BEGINSELECT * INTO onerow_recFROM employeesWHERE employee_id = employee_id_in;
RETURN onerow_rec;END onerow;
END emplu11g;
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.
Copyright 2000-2008 Steven Feuerstein - Page 31
string_tracker3.*
cc_smartargs.pkb
32
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.
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