Wednesday, March 5, 2008

Java/Oracle : Should I Encrypt / Hash the passwords ? What is the difference ?

The standard question asked by a Java/Oracle developer.

Should I Encrypt OR Hash the passwords ? What is the difference ?

The BIG difference between Encryption and Hashing is that, the data that is encrypted, should be able to be decrypted. Whereas, the data that is hashed, CANNOT be reversed.

Let's take an example.

Usually we authenticate users to log on to our systems or web sites. So, following are the steps :

1. User logs onto a web site and provides user name/password.
2. We need to authenticate the user and if the credentials are valid, log him in or deny access.

So, in step 2, we just need to make sure that he entered a correct password. For that, we DON'T need to store the user's password either in text form or in an encrypted form.
In either of these cases, I mean either you store the password in text form or in encrypted form, there is a possibility that the password can be stolen and reversed.
If we store the password in the HASH form, since it cannot be reversed, we are SAFE.
Unix/Oracle ...etc., follow the same methodology for authentication.

So, how do we apply hashing on plain text passwords.

Let us suppose that the password is : password123

Then the query would look like :

select dbms_crypto.hash(utl_raw.cast_to_raw('password123'),dbms_crypto.HASH_MD5) hashed_password from dual;

If you notice, the hash() takes a second parameter where you can specify, which hashing algorithm you would like to use.

-- Hash Functions
HASH_MD4 CONSTANT PLS_INTEGER := 1;
HASH_MD5 CONSTANT PLS_INTEGER := 2;
HASH_SH1 CONSTANT PLS_INTEGER := 3;

If you are using Oracle 10g, then you can use dbms_crypto.hash().
Otherwise, if you are using Oracle 8i/9i, you have to use - DBMS_OBFUSCATION_TOOLKIT.MD5.

Take a stab at the Oracle Guru's web site : http://asktom.oracle.com/tkyte/Misc/Passwords.html


Long Live "Tom Kyte".


Good Luck !!

r-a-v-i

Tuesday, March 4, 2008

Oracle 10g : PL/SQL : Conditional Compilation

If you are a pl/sql developer and if you looking for best practices on unit testing, this is for you.

Usually we run into situations where we need to debug some code, but we don't want to be running the debug code in production. Do we ???

Oracle 10g provides a beautiful feature - conditional compilation. (Like #ifdef in C).

Let's see an example.

Create a test procedure ...

create or replace procedure test_proc
as
begin
dbms_output.put_line( 'Debug 1' );
dbms_output.put_line( 'Debug 2' );
$IF $$debug_code $THEN
dbms_output.put_line( 'Debug 3' );
dbms_output.put_line( 'Debug 4' );
$END
dbms_output.put_line( 'Debug 5' );
end;

set echo on;
set serveroutput on;

Let us suppose that we are running this procedure on our dev/qa environment.

Then, you have to run the following command :
------------------------------------------------------------------------------------
SQL> alter procedure test_proc compile plsql_ccflags = 'debug_code:true' reuse settings;

Procedure altered

SQL>
------------------------------------------------------------------------------------

Now let's run the procedure and see what the output is ....
------------------------------------------------------------------------------------
SQL> exec test_proc;

Debug 1
Debug 2
Debug 3
Debug 4
Debug 5

PL/SQL procedure successfully completed

SQL>
------------------------------------------------------------------------------------

Let's suppose that we are all set to go to production and we need to deploy this procedure on production and we don't want all the debugging.

Then, execute the command :

------------------------------------------------------------------------------------
SQL> alter procedure test_proc compile plsql_ccflags = 'debug_code:false' reuse settings;

Procedure altered

SQL>
------------------------------------------------------------------------------------
That's it. Let's see the output of the procedure, if we run it on production.

------------------------------------------------------------------------------------
SQL> exec test_proc;

Debug 1
Debug 2
Debug 5

PL/SQL procedure successfully completed

SQL>
------------------------------------------------------------------------------------
So, on production when the debug is disabled, you might have noticed that the statements "Debug 3" and "Debug 4" did not print.

Plan B : If you don't want to pollute your code, you can you Log4PLSQL.

Long Live "Tom Kyte".

Good Luck !!

r-a-v-i

Oracle 10g : Case-Insensitive Searching

Say in the famous Scott's "emp" table, I have data like : "Ravi", "rAvi", "RAvi" ...etc.,. So, if I need to fire a query for "ravi", the standard practice is to create a Upper(emp_name) index and fire the following query :

select * from emp where upper(emp_name) like upper('%raVI%');

Prior to Oracle 10g, you had to adopt one of the following strategies:

* Use a function-based index on UPPER (column_name) and modify the queries to use WHERE UPPER (column_name) = value.
* Use a trigger to roll the column value to upper- or lowercase upon modification.
* Use Oracle Text to create a TEXT index on the column; text indexes (which would then mandate the use of the CONTAINS operator in the predicate) can be case-sensitive or -insensitive.

In each of these cases, one of your conditions would have been violated. You would have needed to use triggers, or UPPER() or CONTAINS in the WHERE clause.

In Oracle 10g, you can do this transparently, without effecting your query. Which means that, you don't have to use UPPER() or CONTAINS in your where clause.

Wait ...wait ...I know ... you will believe only if I show you an example. right ?

Here you go ...

drop table cit;

create table cit ( data varchar2(20) );

insert into cit values ( 'Ravi' );
insert into cit values ( 'rAVi' );
insert into cit values ( 'rAvI' );

commit;

/*Create a function-based index on the DATA column and use the binary case insensitive sort*/

create index cit_idx on cit( nlssort( data, 'NLS_SORT=BINARY_CI' ) );

select * from cit where data = 'ravi';
--You will get zero records.

alter session set nls_comp=ansi;
alter session set nls_sort=binary_ci;


select * from cit where data = 'ravi';
--You will get 3 records.

--Now, let's take a look at the explain plan. CBO is using FTS.
--Let's fake CBO that there are 10000000 in the table.
exec dbms_stats.set_table_stats (ownname=>user,tabname=>'CIT',numrows=> 10000000);

--Now run the select again and take a look at the explain plan.
select * from cit where data = 'ravi';

Execution Plan
------------------------------
SELECT STATEMENT (Cost=2)
TABLE ACCESS (BY INDEX ROWID)
INDEX (RANGE SCAN) OF 'CIT_IDX'

Isn't it just awsome !!


Long live "Tom Kyte".


Source : http://www.oracle.com/technology/oramag/oracle/04-jul/o44asktom.html

Good Luck !!

r-a-v-i

Oracle 10g : How to pass ARRAYS of records from Java/Tomcat to Oracle

Environment : JDK 1.5, Tomcat 5.5, Oracle 10gR2.

Let's suppose that you have a web app where you get some records from the user interface and from your DAO, you are trying to pass the records as Oracle ARRAYS to database. Here is a step by step example.

Yes, it's a nightmare as you have to take care of some steps. But, once you understand what to do and what are the issues, it's pretty easy.

Use Case : Let us suppose that we have a java bean Employee and we are trying to send an array of employee records at a time to database.

/*Step 1 : Create a object type in the database*/
/*
CREATE OR REPLACE TYPE "EMP_TYPE" is object(
emp_id Varchar2(500),
emp_name varchar2(500));

*/
/*
Step 2 : Create a type EMP_TYPE_TABLE

CREATE OR REPLACE TYPE "EMP_TYPE_TAB";
*/

/*Step 3 : Create a Java bean which maps the attributes of the above object type in Step 2.*/

import java.io.Serializable;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;

public class Employee implements SQLData, Serializable{
static final long serialVersionUID = 4070409649129120458L;
public Employee(){}

// constructor that takes parameters
// getters and setters for emp_id, emp_name
// You have to implement readSQL() and writeSQL() methods, as shown below.
// This is where you are mapping the Employee table's columns to the Employee
//java bean.

public void readSQL(SQLInput stream, String typeName) throws SQLException {
this.emp_id = stream.readString();
this.emp_name = stream.readString();
}
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(emp_id);
stream.writeString(emp_name);
}
}

//EmpDAO class gets a connection to the database and passes the data.

public class EmpDAO{
java.sql.Connection conn;
java.sql.Connection dconn;
/*
Step 1 : Get database connection
This is a very important step. To pass your records of data as Arrays, you need to get a oracle.jdbc.driver.T4CConnection and then use ArrayDescriptor's. So, how do you get a T4CConnection ?


To get T4CConnection from java.sql.Connection, you need to cast like this :
t4cConn = ((DelegatingConnection)conn).getInnermostDelegate();

If you are working on tomcat, you have two options to get a DataSource in your context.xml.
a) By using apache commons-dbcp
OR
b) by directly using javax.sql.DataSource.
Let's see how to get the T4CConnection in both these cases.
*/

public void sendRecordsToDB(){

//Use Case (a) : if you configured apache commons-dbcp
BasicDataSource ds = (BasicDataSource)ctx.lookup(jndiName);
ds.setAccessToUnderlyingConnectionAllowed(true);
conn = ds.getConnection();
dconn = ((DelegatingConnection)conn).getInnermostDelegate();

//Use Case (b) : if you are directly using javax.sql.DataSource

BasicDataSource bds = new BasicDataSource();
bds.setDriverClassName("");
bds.setUsername("");
bds.setPassword("");
bds.setUrl("jdbc:oracle:thin:@");
bds.setAccessToUnderlyingConnectionAllowed(true);
conn = bds.getConnection();
dconn = ((DelegatingConnection)conn).getInnermostDelegate();

/*So, using either of the above approaches we got dconn, which is an instance of T4CConnection.*/

/* Now let's build an array list of employees.
*/
final List listOfEmployees = new LinkedList();

Employee e1 = new Employee();
e.setEmpId(1);
e.setEmpName("Ravi");

listOfEmployees.add(e1);

Employee e2 = new Employee();
e.setEmpId(2);
e.setEmpName("Vedala");

listOfEmployees.add(e2);

// Now, create an array descriptor

ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor( "EMP_TYPE_TAB", dconn );

ARRAY array_to_pass = new ARRAY( descriptor, dconn, (Object[]) listOfEmployees.toArray());
ps = (OracleCallableStatement)dconn.prepareCall("begin insert_employees(:1); end;");
ps.setARRAY( 1, array_to_pass );
ps.execute();
conn.commit();
}

/*
- See how simple and beautiful is the procedure.
- Using the TABLE() function, you can treat the whole array as a table as EMP_TYPE_TAB is a nested table.
*/

PROCEDURE insert_employees(p_emparray in EMP_TYPE_TAB) AS
BEGIN
/* INSERT ARRAY OF RECORDS IN TO THE EMP TABLE*/
INSERT INTO scd_company_staging
(emp_id,emp_name)
SELECT * FROM TABLE(p_empparray);
END insert_employees;

The nightmare exception for Java/Oracle developers :-)

java.lang.ClassCastException: oracle.jdbc.driver.T4CConnection cannot be cast to oracle.jdbc.OracleConnection
at oracle.sql.ArrayDescriptor.createDescriptor(ArrayDescriptor.java:149)
at oracle.sql.ArrayDescriptor.createDescriptor(ArrayDescriptor.java:115)
...........

Solution :
a)You will see the above exception, if you have ojdbc14.jar in your war file. You would be having ojdbc14.jar on your classpath for compiling your java classes. Use it only for compilation. Don't include it in the build to Tomcat. ie., the war file of your web app should NOT have ojdbc14.jar in it.

b) Make sure that the Oracle thin driver (eg : ojdbc14.jar) is in tomcat's common\lib.

Long live "Tom Kyte".


Good Luck !!
r-a-v-i

Saturday, March 1, 2008

Oracle 10g : How to pass ARRAYS to Oracle using Java?

Environment : JDK 1.5 (did not test on older versions of java but it should work on them !!)

Keep the JDBC driver in the classpath : '''ojdbc14.jar'''

Note : Oracle ARRAY is supported on Oracle9i Database version 9.0.1 or later.

Take a look at the package : Tests

'''Java Code''' :
(Look at the technique - don't look at the java standard practices like using try-catch-finally ...etc., to keep the code readable, I have just taken them out.)
----------------------------------------------------------------------------------------------------------
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import oracle.jdbc.OracleTypes;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
public class TestArray {
private static Connection getConnection() {
Connection conn = null;
try {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
conn =
DriverManager.getConnection("",
"test", "test");
conn.setAutoCommit(false);
System.out.println("Connection :- " + conn);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return conn;
}

private static void insertEmp(Connection conn) throws Exception {
/* let us create 20 names in a string array*/
String[] names = new String[20];
for (int i = 0; i < 20; i++) {
names[i] = "Ravi" + i;
}
/*Create an array descriptor of type STRINGS */
ArrayDescriptor desc =
ArrayDescriptor.createDescriptor("STRINGS", conn);
/* Create an array object */
ARRAY nameArray = new ARRAY(desc, conn, names);
String st = "begin Tests.insert_emp(:1); end;";
CallableStatement cs = conn.prepareCall(st);
cs.setArray(1, nameArray);
cs.execute();
cs.close();
conn.commit();
System.out.println("Successfully INSERTED " + names.length +
" EMPLOYEE records :- ");
}

private static void getEmp(Connection conn) throws Exception {
String[] names = { };
/*Create an array descriptor of type STRINGS */
ArrayDescriptor desc =
ArrayDescriptor.createDescriptor("STRINGS", conn);
/* Create an array object */
ARRAY nameArray = new ARRAY(desc, conn, names);
String st = "begin Tests.get_emp(:1); end;";
CallableStatement cs = conn.prepareCall(st);
/* Register the out parameter as STRINGS */
cs.registerOutParameter(1, OracleTypes.ARRAY, "STRINGS");
cs.execute();
/* Get the array into a local ARRAY object */
nameArray = (ARRAY)cs.getArray(1);
System.out.println("Array is of type " + nameArray.getSQLTypeName());
System.out.println("Array element is of type code "+nameArray.getBaseType());
System.out.println("Array is of length " + nameArray.length());
/* Get the array of names into a string array */
names = (String[])nameArray.getArray();
System.out.println("*****************************************************************");
System.out.println("Retrieving ONLY the names of employees as an ARRAY \n");
System.out.println("*****************************************************************");
for (int i = 0; i < names.length; i++)
System.out.println(names[i]);
cs.close();
}

private static void getEmpRecs(Connection conn) throws SQLException {
/* Create an array of objects (since we are getting heterogeneous data)*/
Object[] empRecs = { };
/* Create an array descriptor for EMP_REC_ARRAY */
ArrayDescriptor desc =
ArrayDescriptor.createDescriptor("EMP_REC_ARRAY", conn);
ARRAY nameArray = new ARRAY(desc, conn, empRecs);
String st = "begin Tests.get_emp_recs(:1); end;";
CallableStatement cs = conn.prepareCall(st);
/* Register OUT param as EMP_REC_ARRAY */
cs.registerOutParameter(1, OracleTypes.ARRAY, "EMP_REC_ARRAY");
cs.execute();
/* Get the Array into a local ARRAY object */
nameArray = (ARRAY)cs.getArray(1);
System.out.println("Array is of type " + nameArray.getSQLTypeName());
System.out.println("Array element is of type code "+nameArray.getBaseType());
System.out.println("Array is of length " + nameArray.length());
/* Get the Employee Records */
empRecs = (Object[])nameArray.getArray();
System.out.println("***********************************************************");
System.out.println("Retrieving the EMPLOYEE RECORDS as an ARRAY !!\n");
System.out.println("***********************************************************");
int id = 0;
String name = "";
for (int i = 0; i < empRecs.length; i++) {
/* Since we don't know the type of the record, get it into STRUCT !! */
oracle.sql.STRUCT empRec = (oracle.sql.STRUCT)empRecs[i];
/* Get the attributes - nothing but the columns of the table */
Object[] attributes = empRec.getAttributes();
/* 0- first column, 1 - second column ...*/
id = Integer.parseInt("" + attributes[0]);
name = "" + attributes[1];
System.out.println("id = " + id + " name = " + name);
}
cs.close();
}

public static void main(String[] args) throws Exception {
Connection conn = null;
try {
conn = getConnection();
/*Insert 20 employee names*/
insertEmp(conn);
/*Get the 20 employee names*/
getEmp(conn);
/*Get the 20 employee RECORDS*/
getEmpRecs(conn);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
if (conn != null)
conn.close();
}
}
}

----------------------------------------------------------------------------------------------------------
'''SQL Scripts :'''

Create a test table and the types :

create table emp(id number , name varchar2(50))
/

create or replace type Strings IS VARRAY(20) of VARCHAR2(30)
/

create or replace type emp_rec IS OBJECT (id number, name varchar2(30))
/

create or replace type emp_rec_array AS VARRAY(100) of emp_rec
/

create or replace package Tests is

-- Author : r-a-v-i

procedure insert_emp(emp_names Strings);
procedure get_emp(emp_names out Strings);
procedure get_emp_recs(emp_recs out emp_rec_array);
end Tests;
/
create or replace package body Tests is
procedure insert_emp(emp_names Strings)
as
cnt Integer := 1;
v_id Integer := 0;
begin
dbms_output.put_line(emp_names.COUNT);
loop
if cnt > emp_names.COUNT then
exit;
else
dbms_output.put_line(cnt||' = '||emp_names(cnt));
select nvl(max(id),0) + 1 into v_id from emp;
insert into emp(id,name) values (v_id,emp_names(cnt));
end if;
cnt := cnt + 1;
end loop;
commit;
end;
procedure get_emp(emp_names out Strings)
as
v_emp_names Strings := Strings();
cursor c1 is select name from emp order by id;
v_name varchar2(30);
cnt Integer := 1;
begin
open c1;
loop
fetch c1 into v_name;
if c1%notfound then exit;
else
dbms_output.put_line(cnt||' = '||v_name);
v_emp_names.extend;
v_emp_names(cnt) := v_name;
end if;
cnt := cnt + 1;
end loop;
emp_names := v_emp_names;
end;
procedure get_emp_recs(emp_recs out emp_rec_array)
as
v_emp_recs emp_rec_array := emp_rec_array();
cursor c1 is select id,name from emp order by id;
v_id Integer;
v_name varchar2(30);
v_emp_rec emp_rec;
cnt Integer := 1;
begin
open c1;
loop
fetch c1 into v_id,v_name;
if c1%notfound then exit;
else
v_emp_rec := emp_rec(v_id,v_name);
v_emp_recs.extend;
v_emp_recs(cnt) := v_emp_rec;
end if;
cnt := cnt + 1;
end loop;
emp_recs := v_emp_recs;
end;
end Tests;
/

Long live "Tom Kyte".


----------------------------------------------------------------------------------------------------------