Quantcast
Channel: SCN : All Content - SAP HANA Developer Center
Viewing all articles
Browse latest Browse all 6745

Porting Oracle procedures to Hana

$
0
0

While working on porting SAP Net Weaver Identity Center to Hana I had great use of blogs and related material for finding my way around the roadblocks.

Hence decided to share back some of the insight and hopefully it can help others in the same way it helped me.

 

Most of the migration was done from Oracle to Hana, as the SQL languages seems more similar to each other.

 

Lot of time was saved by creating an small utility that crude ports oracle procedure to Hana.

Perhaps it does 70-80% of the work and definitely most tedious part can be automated quickly.

The utility is basically based on string replacement and regular expression and some additional logic for handling input and local variables etc.

Of course much improvement could be done using a proper language parser, but considered it to ambitious and costly for our needs.

In a way I also like it simple and elegant and the regular expression are easily deployed in a text editor/sed or similar and works well for finding recurrent issues.

 

Anyway if you are interested in the full fetched version of the Ora2Hana conversion utility, drop me a mail.

 

Note about syntax, I use = for separation between source and target and !! when = cannot be used.

So for most editors you use first part in the 'find what' and the second in the 'replace with' regular expression checked.

 

 

Replacing keywords:      

Often you want to replace one word for another using:

 

\b<keyword>\b=<replacement>

 

This makes it lot safer than normal replacement as it avoiding mixed substring replacement.

Also works fine for those cases you also just want to uppercase words.

The \b basically means break so it matches breaking characters (space, brackets,tabs etc..)

 

Also attached is a basic keyword list that serve as a template for this kind of replacement.

In the utility use an extended one for certain attributes and names spesific for IdM.

 

 

Remove () from number()

IntConversion.png

There is usually lots of variable delcaration and these two regexp might save you a bit of work.

(?i)(.*?)number\(20\);=$1BIGINT;

(?i)(.*?)number\((.*?)\);=$1INTEGER;

 

Exception handling:

Converting Oracle exception can saves you much effort.

It is basically just restructing of the elements by using group matcing.

 

Exception2.png


(?s)(?i)(.*)BEGIN(.*?)EXCEPTION(.*?)THEN(.*?)END;(.*)=$1\nBEGIN\n\tDECLARE EXIT HANDLER FOR SQL_ERROR_CODE 1299\n\tBEGIN\n\t\t$4\n\tEND; --TBD review error handling\n\t$2\nEND;$5

 

The (?s) causes this to affect multiple lines. If you use the replaceAll functionality in Java as we do, you might have iterate over this until there are no further changes or max iteration threshold is reached.

 

The (?i) tells interpreter to ignore casing.

 

The (.*) means match all and (.*?) means match all up to first occurrence of…

The brackets in this case also refer to grouping and the $1…$5 refer to the content of the matched groups.

 

I know there are more elegants way of writting this, but this was the first that came to mind.

 

Note that you still however will have to look out for return inside exception and apply proper handling to that.

 

 

Removing comments:

Sometimes comments In the code mess things up as they contain false keyword then this quick replacement might be handy.

(.*)--(.*)=$1

Cursors

Changes CURSOR IS to FOR:

 

Declare_Cursor.png

(?i)CURSOR (.*?) IS=DECLARE CURSOR $1 FOR --TBD:add : in front of params

 

If you want to deal with explicit cursor this one is help full.

(?i)(.*?)EXIT(\s+?)WHEN(\s+?)(.*?)%NOTFOUND;(.*?)=$1IF $4::NOTFOUND THEN BREAK;END IF;

 

Converting Loops into while/for

loop_to_while.png

                (?i)(?s)open(.*?);(\s+?)loop(\s+?)=OPEN $1;$2WHILE <condition> DO --TBD review conversion. alternative  FOR rec AS $1(..,..) DO$3

(?i)(?s)while(.*?)loop(\s+?)=WHILE $1 DO --TBD review $2

(?i)(.*)end loop;=$1END WHILE; --TBD review

Temporary tables

We used specific naming syntax for our temporary tables on Oracle so this regexp prepends the # in front of them.

mx_tmp.png

(?i)(.*?)mx_tmp(.*?)=$1#mx_tmp$2

Prefixing and name spaces:

 

Since this is quite brute but effective it does of not consider differences between tables and view so you will have to correct those namespaces.

 

We use MXMC_DB by default and specific names spaces, this would have to be changed for you but these examples could serve as nice templates.

 

 

Append call MXMC_DB in front of procedures

(?i);([^ ]+?)mx(\S+?)_(.*?)\((.*?)\);=;$1CALL MXMC_DB."sapidm.hana.db.hdbprocedures::mx$2_$3"($4);

Append call MXMC_DB in front of procedures mc_

(?i);([^ ]+?)mc_(.*?)\((.*?)\);=;$1CALL MXMC_DB.""sapidm.hana.db.hdbprocedures::mc_$2"($3);

Adds MXMC_DB in SELECT with one table

(?i)SELECT(.*?)FROM(\s+?)(\w+?)(\s+?)WHERE(.*?);=SELECT$1FROM$2MXMC_DB."sapidm.hana.db.hdbtables::$3"$4WHERE$5;

Adds MXMC_DB in UPDATE with one Table

(?i)UPDATE(\s+?)(\w+)(\s+?)SET(.*);=UPDATE$1MXMC_DB."sapidm.hana.db.hdbtables::$2"$3SET$4;

Adds MXMC_DB in INSERT INTO one Table

(?i)INSERT INTO(\s+?)(\w+)(\s+?)\((.*?)\)(\s+?)VALUES=INSERT INTO$1MXMC_DB."sapidm.hana.db.hdbtables::$2"$3($4)$5VALUES

Add MXMC_DB in DELETE FROM (one table)

(?i)DELETE(.*?)FROM(\s+?)(\w+?)(\s+?)WHERE(.*?);=DELETE$1FROM$2MXMC_DB."sapidm.hana.db.hdbtables::$3"$4WHERE$5;

Adds MXMC_DB in front of Sequences

(?i)(.*)MXPSEQ(.*?)nextval=$1 MXMC_DB."sapidm.hana.db.hdbsequence::MXPSEQ$2"NEXTVAL

Code splitting

Sometimes you have known SQL patterns you want to substitute for procedure calls.

Splitting up the code makes it more readable and maintainable, but caution should also be taken as this could have performance impact.

Rhe examples are adapted to our environment, but gives you an idea of how to tweaking it to your own.

 

Code_splitting.png

 

Example 1:

(?i)(.*?)select(.*?)attr_id(.*?)INTO(.*)FROM(.*?)mxi_attributes(.*?)attrname(.*?)=CALL MXMC_DB.MCF_GETATTRID(IN_IDSTORE,'MX_CTX_CONDITIONAL',L_AIDCTXCOND);

 

Example 2

(?i)(.*?)select(.*?)MCIDSTORE(.*?)INTO(.*)FROM(.*?)MXI_ENTRY(.*?)MCMSKEY(.*?)=CALL MXMC_DB.MCF_GETIDSTORE (IN_ENTRY_ID, 1, L_IDSTORE_ID);

 

Validation checks:

You can simply use Search / File search dialog in Hana Studio to do verification scan of procedures with the following regular expressions.

 

Local Input parameters

In Hana you will have to take in to consideration that you cannot pass NULL as parameter to proccedures,

The following Regexp search, helps you identify possible candidates.  (note: all our local parameters starts with L by default)

This of course gives you bunch of false candidates so you have to rule them out manually by code analysis.

(?i)(.*?)CALL(.*),L(.*?);

 

Attempt to close Cursor inside FORs

Using FOR loops is recommended over explicit open of cursors.

In cases of error handling you might have attempted to CLOSE cursor and break or return the loop, this is wrong and can easily go undetected.

he next regular expressions find the potential candidates for you.

(?i)(?s)FOR (.*?) DO(.*?)CLOSE(.*?)END FOR

 

Hana-fu and other magic tricks

 

Sometimes we use select argument with top argument as a variable, this can be achieved in Hana by using a colon.

 

       DELETEFROMMXMC_DB."sapidm.hana.db.hdbtables::MC_EXECUTION_LOG"WHEREMCUNIQUEIDIN(

             SELECTTOP:LDELLIMITMCUNIQUEIDFROMMXMC_DB."sapidm.hana.db.hdbtables::MC_EXECUTION_LOG"
                WHERE (mcLogTime<= LOLDESTTIME) AND (mcLogLevel  <= LSEVERITY)

       );

 

Preserving output from dynamic SQL statement:

 

Selects:

 

createglobaltemporarytable"MXMC_DB"."tloc" (vcntinteger);

  LTMPSQL :=  'insert into "MXMC_DB"."tloc" SELECT COUNT(ValId) FROM MXMC_DB."'|| LTMPTABLE ||'" WHERE UPPER(ValId)='''|| UPPER(IN_PVALUEID) ||''' AND UPPER(ValKey)='''|| UPPER(IN_PAVALUE) ||'''';

  EXECLTMPSQL;

  SELECTTOP 1 vcntINTOv_row_countFROM"MXMC_DB"."tloc";

   DROPTABLE"MXMC_DB"."tloc";

 

Although we initially used local temporarily table here and this worked find in SQL console,  table resolution failed when running from runtime.

 

Proccedure call

 

Here we use a dynami sql to call a procedure by a given variable and recieves output values as a tabletype storing it to a local temporaily table.


CREATE
LOCALTEMPORARYTABLE"#mx_tmp_proc_resultset"LIKE"MXMC_DB"."sapidm.hana.db.hdbstructures::PROC_RESULTSET";

EXEC'CALL MXMC_DB."sapidm.hana.db.hdbprocedures::'|| LPROCNAME||'"(' || IFNULL(LPAR1,'') || ',' || IFNULL(LPAR2,'') || ','||'"#mx_tmp_proc_resultset"'||') WITH OVERVIEW';

 

       BEGIN

             DECLAREEXITHANDLERFORSQL_ERROR_CODE 1299

             BEGIN

                    OUT_STATUS := -1;

                    OUT_STATUSTEXT := 'No resultset found';

             END;  

             SELECTNUM_RECORDS,MORE,STATUS,STATUSTEXTINTOLNUMRECORDS,LMORE,LSTATUS,LSTATUSTEXTFROM  #mx_tmp_proc_resultset";

       END;

       DROPTABLE  "#mx_tmp_proc_resultset";

       IF( OUT_STATUS<>0) THEN

             RETURN;

       ENDIF;

 

Note: If you can you should make static calls, by using if-then selection for a limited set of procedure calls.

 

And in relation to the code above the here is called procedure, which returns a table type.

PROCEDURE"MXMC_DB"."sapidm.hana.db.hdbprocedures::MCPROCCHECKSTALLEDJOBS" (

       ININ_PAR1                 VARCHAR(255),             
     ININ_PAR2                 VARCHAR(255),              --Par2: Number of entries to process in one operation

       OUTOUT_RESULT_SETMXMC_DB."sapidm.hana.db.hdbstructures::PROC_RESULTSET"

)

       LANGUAGESQLSCRIPT

       SQLSECURITYDEFINER

AS

 

Now happy Hana porting to all of you, hopefully it saved you some sweat.


Viewing all articles
Browse latest Browse all 6745

Trending Articles