Debugging Procedures and Unit Testing Procedures are made up of the functions, transactions, and cursors we have been discussing in this chapter. They are also part of the packages. Understanding how to write stored procedures is important for a DBA. Understanding how to review procedures and find the good and the bad in them may be even more important. When you’re experiencing problems with procedures, check their permissions on objects. Also check for the dreaded missing semicolon somewhere. These are quick areas to check, and in a small stored procedure, they might be easy to spot. Privileges needed for running in debug mode for PL/SQL are “Debug any procedure” and “Debug connect session.” Figure 9-2 shows an example of compiling a procedure in debug mode in SQL Developer. Clicking an error message shown here will take you to the line in the code that is causing the issue. 262 Oracle Database Administration for Microsoft SQL Server DBAs FIGURE 9-2. Debugging procedures in SQL Developer Breakpoints can also be set to walk through the procedures to validate code and variables. Another way to get output throughout the procedure to see what is happening is to use DBMS_OUTPUT.PUT_LINE to output a statement, value, or step in the procedure. SQL Developer also has unit testing functionality. Test plans can be set up as an unit test repository that is created in the database. You can seed some data and pass in parameters from tables to do the testing. To set up a repository for unit testing or to connect to an existing repository, select the Unit Test option from the Tools menu, as shown in Figure 9-3. Create a new repository if one is not yet available. SYSDBA permissions are required to create a new repository, but users can be added with lesser permissions to run unit tests and create the test plans. Figure 9-4 shows the first step in creating a unit test using the wizard. All of the packages, procedures, and functions are listed and available for testing. Other objects can be pulled in for using data or as part of the test in later steps. Chapter 9: PL/SQL 263 FIGURE 9-3. Setting up a unit testing repository in SQL Developer Error Handling With error handling, if something in a procedure fails, it goes to the routine for handling that exception. In SQL Server, the TRY CATCH block can be used for error handling. This is almost like wrapping the code in a transaction with a BEGIN TRY and then executing some code. If errors come up, it goes to the BEGIN CATCH. BEGIN TRY (T-SQL Code END TRY BEGIN CATCH ……. (Error handling code) END CATCH 264 Oracle Database Administration for Microsoft SQL Server DBAs FIGURE 9-4. Creating a unit test in SQL Developer With PL/SQL’s exception handling, the errors that are raised can be user- or system-defined. DECLARE (variables defined) BEGIN (Blocks of code) EXCEPTION WHEN exception_name THEN (Exception handling code) END; The error functions in SQL Server and Oracle provide information about the error or failure, as shown in Table 9-5. Oracle’s SQLCODE function returns an error number. SQLERRM returns a message. Exceptions do not cause DML statements to roll back unless this happens by default without an exception handler routine. The exception handler routine would need to handle commits and rollbacks depending on the failure. Within the exception handler, there might be a separate commit for inserting into an error log. Using savepoints is a good way to handle the rollbacks, so this logging of errors does not become part of the transaction. Autonomous transactions are another way to handle this. Autonomous transactions are changes made with a block of code that can be saved or reversed without affecting the outer or main transaction. Chapter 9: PL/SQL 265 SQL Server Error Functions Oracle Error Functions ERROR_NUMBER() ERROR_SEVERITY() ERROR_STATE() ERROR_PROCEDURE() ERROR_LINE() ERROR_MESSAGE() SQLCODE SQLERRM TABLE 9-5. Error Functions in SQL Server and Oracle Let’s take a look at a couple of examples of exception handling. EXCEPTION WHEN NO_DATA_FOUND THEN v_msg := 'Record not found' || TO_CHAR(v_id); v_err := SQLCODE; v_prog := 'get product'; insert into errlog values(v_err,v_msg, v_prog, sysdate); Error Handling Packages You can create a package to call your error procedures. Using a standard package makes it easier to have error handling at the end of each procedure and provides a centralized place to gather the failure information. ## Create a table to hold the error information SQLPLUS> create table errlog ( errcode integer, errmsg varchar2(4000), prog_action varchar2(300), created_on date, created_by varchar2(30)); Table created. ## Create package with procedures for handling errors SQLPLUS> create or replace package errlogs IS c_table constant NUMBER :=1; PROCEDURE handle ( errcode IN NUMBER := NULL, errmsg IN VARCHAR2 := NULL, logerr IN BOOLEAN := TRUE, reraise IN BOOLEAN := FALSE); PROCEDURE raise (errcode IN NUMBER := NULL, errmsg IN VARCHAR2 := NULL, prog_action IN VARCHAR2); PROCEDURE log (errcode IN NUMBER := NULL, errmsg IN VARCHAR2 := NULL, prog_action IN VARCHAR2); END; / CREATE OR REPLACE PACKAGE BODY errlogs IS g_target NUMBER := c_table; PROCEDURE handle ( errcode IN NUMBER := NULL, errmsg IN VARCHAR2 := NULL, prog_action IN VARCHAR2 := NULL, logerr IN BOOLEAN := TRUE, reraise IN BOOLEAN := FALSE ) 266 Oracle Database Administration for Microsoft SQL Server DBAs IS BEGIN IF logerr THEN log (errcode, errmsg, prog_action); END IF; IF reraise THEN errlogs.raise (errcode, errmsg, prog_action); END IF; END; PROCEDURE raise ( errcode IN PLS_INTEGER := NULL, errmsg IN VARCHAR2 := NULL, prog_action IN VARCHAR2 := NULL ) IS l_errcode PLS_INTEGER := NVL (errcode, SQLCODE); l_errmsg VARCHAR2(1000) := NVL (errmsg, SQLERRM); l_progact VARCHAR2(300) := NVL(prog_action,'Default Action'); BEGIN IF l_errcode BETWEEN -20999 AND -20000 THEN raise_application_error (l_errcode, l_errmsg); ELSIF l_errcode != 0 THEN EXECUTE IMMEDIATE 'DECLARE myexc EXCEPTION; ' || ' PRAGMA EXCEPTION_INIT (myexc, ' || TO_CHAR (err_in) || ');' || 'BEGIN RAISE myexc; END;'; END IF; END; PROCEDURE log ( errcode IN PLS_INTEGER := NULL, errmsg IN VARCHAR2 := NULL ) IS PRAGMA AUTONOMOUS_TRANSACTION; l_sqlcode pls_integer := NVL (errcode, SQLCODE); l_sqlerrm VARCHAR2(1000) := NVL (errmsg, SQLERRM); BEGIN INSERT INTO errlog (errcode, errmsg, prog_action, created_on, created_by) VALUES (l_sqlcode,l_sqlerrm,l_progact,SYSDATE,USER); COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; END; / Chapter 9: PL/SQL 267 This package can be used in the exception handling of any procedure. The call to the package passes in the needed parameters, including information about what procedure was running, to put details in the error log. SQLPLUS> create or replace procedure testing_errors as procedure_name varchar2(30) := 'testing_errors'; BEGIN EXCEPTION WHEN OTHERS errlogs.handle(SQLCODE,SQLERRM,procedure_name); END; / Expanding on the error handling could then allow for different logs to be captured in a table or even a file. Rollback and commit information can be handled in the executing procedure, and then the error capture in the same error package for all procedures, to maintain consistency. Standard Error Messages PL/SQL can raise user error messages that can be passed along to the application for handling on the application side as well. Also, application errors can be raised to pass the information to the application. The standard Oracle exceptions can be associated with a user-defined application error. You can also have other data or changes raise user- defined application errors. Raised errors can be used in a trigger to disallow updates to a table: raise_application_error(-20002,'Updates not allowed on this table'); If there is a check on a value, the procedure could raise an error stating that the value is not allowed or needs to be in a different range: raise_application_error(-20001,'Salary not in correct range for department'); You can pass through additional information about the values of the columns or any of the variables in the procedure. When standard Oracle messages come through, different information can be passed through to the application: raise_application_error(-20004,'No Data Found, values not in table'); 268 Oracle Database Administration for Microsoft SQL Server DBAs Here is a partial list of standard exceptions: ■ NO_DATA_FOUND ■ VALUE_ERROR ■ OTHERS ■ INVALID_CURSOR ■ INVALID_NUMBER ■ CASE_NOT_FOUND ■ TOO_MANY_ROWS ■ ROWTYPE_MISMATCH Instead of having the exception handler looking at WHEN OTHERS, a different set of steps can be coded for each of these exceptions. EXCEPTION WHEN exception1 THEN handler for exception1 sequence_of_statements1 WHEN exception2 THEN another handler for exception2 sequence_of_statements2 WHEN OTHERS THEN optional handler for all other errors sequence_of_statements3 END; ## Another example with the raise application EXCEPTION WHEN TOO_MANY_ROWS THEN rollback to savepoint sales1; errlogs.handle(SQLCODE,SQLERRM,'Sales_records'); raise_application_error(-20001,'Query return more rows than expected.'); WHEN NO_DATA_FOUND THEN errlogs.handle(SQLCODE,SQLERRM,'customer_info'); raise_application_error(-20002, 'Data not available for this customer'); WHEN OTHERS THEN errlogs.handle(SQLCODE,SQLERRM,'Oh No!'); raise_application_error(-20003,'Unknow error details in log'); END; Chapter 9: PL/SQL 269 Using DBMS Packages System packages can be used in user packages as long as the user has permissions. SQL Server has several system procedures and extended procedures that are used in the same way as the Oracle packages. We have already looked at DBMS_SCHEDULER and DBMS_STATS in previous chapters. The following are a few other packages you might consider using: ■ DBMS_OUTPUT is useful for seeing what is running in a stored procedure. It sends output to the screen. ■ DBMS_METADATA is useful for getting the definitions of the objects. ■ DBMS_REDEFINITION offers a way to rebuild a table online. ■ DBMS_SQL is used to create dynamic SQL in PL/SQL. Here’s an example of using the DBMS_METADATA package: SQLPLUS> set long 200000 pages 0 lines 131 SQLPLUS> select dbms_metadata.get_ddl('TABLE','SALES') from dual; CREATE TABLE "MMDEV"."SALES" ( "SALES_ID" NUMBER NOT NULL ENABLE, "PROD_ID" NUMBER, "STATE_ID" NUMBER, "SALE_DATE" DATE, "CUSTOMER_ID" NUMBER, "REGION_ID" NUMBER, "AMOUNT" NUMBER, CONSTRAINT "SALES_PK" PRIMARY KEY ("SALES_ID") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "USERS" ENABLE, CONSTRAINT "SALES_PRODUCT_FK1" FOREIGN KEY ("STATE_ID") REFERENCES "MMDEV"."STATES" ("STATE_ID") ENABLE ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "USERS" 270 Oracle Database Administration for Microsoft SQL Server DBAs There are many more useful packages, including several to help monitor databases and get details. Because of this access, permissions need to be granted carefully. Summary PL/SQL is an extremely useful database programming language, which you can use to develop robust applications as well as run maintenance tasks and monitor databases. This chapter presented examples of how to use PL/SQL to build packages, procedures, functions, and triggers. We looked at some of the ways to process data through cursors, the syntax for updates, and other differences between the database programming languages. As you can see, there is plenty of fun to have with PL/SQL! The processing of statements is similar in both platforms, requiring transaction and points to commit or rollback. Oracle packages allow procedures and functions to be grouped together. Packages can be used for several of the transactions and processes that are written in PL/SQL. Error packages to be used with exception handling are useful to ensure consistent ways to log errors and raise application errors. Chapter 9: PL/SQL 271 . (T -SQL Code END TRY BEGIN CATCH ……. (Error handling code) END CATCH 264 Oracle Database Administration for Microsoft SQL Server DBAs FIGURE 9-4. Creating a unit test in SQL Developer With PL /SQL s. mode in SQL Developer. Clicking an error message shown here will take you to the line in the code that is causing the issue. 262 Oracle Database Administration for Microsoft SQL Server DBAs FIGURE. DEFAULT) TABLESPACE "USERS" 270 Oracle Database Administration for Microsoft SQL Server DBAs There are many more useful packages, including several to help monitor databases and get details. Because