Following is a sample program demonstrating the use of an output SQLDA structure. The client application invokes a stored procedure that determines the median salary for employees in the SAMPLE database. (The definition of the median is that half the values lie above it, and half below it.) The median salary is then passed back to the client application using an output SQLDA structure.
This sample program calculates the median salary of all employees in the SAMPLE database. Since there is no existing SQL column function to calculate medians, the median salary can be found iteratively by the following algorithm:
An application that uses neither the stored procedures technique, nor blocking cursors must FETCH each salary across the network as shown in Figure 20.
Figure 20. Median Sample Without a Stored Procedure
![]() |
Since only the salary at row n/2+1 is needed, the application discards all the additional data, but only after it is transmitted across the network.
An application using the stored procedures technique can be designed to allow the stored procedure to process and discard the unnecessary data, returning only the median salary to the client application. Figure 21 shows this feature.
Figure 21. Median Sample Using a Stored Procedure
![]() |
"How the Example Output-SQLDA Client Application Works" shows a sample output-SQLDA client application and sample output-SQLDA stored procedure in some of the supported languages.
The CHECKERR macro/function is an error checking utility which is external to the program. The location of this error checking utility depends upon the programming language used:
See "Using GET ERROR MESSAGE in Example Programs" for the source code for this error checking utility.
#include <stdio.h> #include <stdlib.h> #include <sql.h> (1) #include <sqlda.h> #include <sqlca.h> #include <string.h> #include "util.h" #define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1; int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; char database[9]; char userid[9]; char passwd[19]; /* Declare a Local Variable for Holding the Procedure's Name */ char procname[255] = "outsrv"; /* Declare Local Variables for Holding Returned Data */ double sal = 0.0; (3) short salind = 0; EXEC SQL END DECLARE SECTION; /* Declare the output SQLDA */ struct sqlda *inout_sqlda = (struct sqlda *) malloc(SQLDASIZE(1)); (2) /* Declare the SQLCA */ struct sqlca sqlca; if (argc != 4) { printf ("\nUSAGE: outcli remote_database userid passwd\n\n"); return 1; } strcpy (database, argv[1]); strcpy (userid, argv[2]); strcpy (passwd, argv[3]); /* Connect to Remote Database */ printf("CONNECT TO Remote Database.\n"); EXEC SQL CONNECT TO :database USER :userid USING :passwd; (5) CHECKERR ("CONNECT TO RSAMPLE"); /********************************************************\ * Call the Remote Procedure via CALL with Host Variables * \********************************************************/ printf("Use CALL with Host Variable to invoke the Server Procedure " "named outsrv\n"); salind = -1; /* Sal has no input, so set to null */ EXEC SQL CALL :procname (:sal :salind); (6a) CHECKERR ("CALL WITH HOST VARIABLES"); printf("Server Procedure Complete.\n"); /* Print Salary Returned in The Host Variables */ printf("Median Salary = %.2f\n\n", sal ); /***********************************************\ * Call the Remote Procedure via CALL with SQLDA * \***********************************************/ /* Initialize the output SQLDA */ (4) inout_sqlda->sqln = 1; inout_sqlda->sqld = 1; inout_sqlda->sqlvar[0].sqltype = SQL_TYP_NFLOAT; inout_sqlda->sqlvar[0].sqllen = sizeof( double ); inout_sqlda->sqlvar[0].sqldata = (char *)&sal; inout_sqlda->sqlvar[0].sqlind = (short *)&salind; printf("Use CALL with SQLDA to invoke the Server Procedure " "named outsrv\n"); salind = -1; /* Sal has no input, so set to null */ EXEC SQL CALL :procname USING DESCRIPTOR :*inout_sqlda; (6b) CHECKERR ("CALL WITH SQLDA"); printf("Server Procedure Complete.\n"); /* Print Salary Returned in The Host Variables */ printf("Median Salary = %.2f\n\n", sal ); /* Free allocated memory */ free( inout_sqlda ); /* Disconnect from Remote Database */ EXEC SQL CONNECT RESET; (7) CHECKERR ("CONNECT RESET"); return 0; } /* end of program : outcli.sqc */
Identification Division. Program-ID. "outcli". Data Division. Working-Storage Section. * Copy Files for Constants and Structures. copy "sql.cbl". (1) copy "sqlenv.cbl". copy "sqlca.cbl". 01 decimal-sqllen pic s9(4) comp-5. 01 decimal-parts redefines decimal-sqllen. 05 precision pic x. 05 scale pic x. * Declare an Output SQLDA Structure. 01 io-sqlda sync. (2) 05 io-sqldaid pic x(8) value "O-DA ". 05 io-sqldabc pic s9(9) comp-5. 05 io-sqln pic s9(4) comp-5. 05 io-sqld pic s9(4) comp-5. 05 io-sqlvar occurs 1 to 99 times depending on io-sqld. 10 io-sqltype pic s9(4) comp-5. 10 io-sqllen pic s9(4) comp-5. 10 io-sqldata usage is pointer. 10 io-sqlind usage is pointer. 10 io-sqlname. 15 io-sqlnamel pic s9(4) comp-5. 15 io-sqlnamec pic x(30). EXEC SQL BEGIN DECLARE SECTION END-EXEC. 01 dbname pic x(8). (3) 01 userid pic x(8). 01 passwd. 49 passwd-length pic s9(4) comp-5 value 0. 49 passwd-name pic x(18). * Declare Variables for the SQL CALL. 77 prog-name pic x(19) value "outsrv". * Declare Local Variables for Holding Actual Data. 77 salary pic s9(5)v99 comp-3. 77 sal-ind pic s9(4) comp-5. EXEC SQL END DECLARE SECTION END-EXEC. * Declare Output Mask for Salary 77 sal-out pic z9(5).99-. * Declare a Null Pointer Variable. 77 null-ptr-int pic s9(9) comp-5. 77 null-ptr redefines null-ptr-int pointer. 77 errloc pic x(80). Procedure Division. Main Section. * Initialize the Input/Output SQLDA Structure move 1 to io-sqln. (4) move 1 to io-sqld. move sql-typ-ndecimal to io-sqltype(1). * Length = 7 digits precision and 2 digits scale move x"07" to precision. move x"02" to scale. move decimal-sqllen to io-sqllen(1). set io-sqldata(1) to address of salary. set io-sqlind(1) to address of sal-ind. * CONNECT TO DATABASE display "Enter in the database name : " with no advancing. accept dbname. display "Enter your user id (default none): " with no advancing. accept userid. if userid = spaces EXEC SQL CONNECT TO sample END-EXEC else display "Enter your password : " with no advancing accept passwd-name. * Passwords in a CONNECT statement must be entered in a VARCHAR format * with the length of the input string. inspect passwd-name tallying passwd-length for characters before initial " ". EXEC SQL CONNECT TO :dbname USER :userid USING :pa(5) END-EXEC. move "CONNECT TO" to errloc. call "checkerr" using SQLCA errloc. * Call the Remote Procedure. display "Use CALL with Host Var. to invoke Server Procedure." EXEC SQL CALL :prog-name (:SALARY:SAL-IND) END-EXE(6a) move "CALL HV" to errloc. call "checkerr" using SQLCA errloc. display "Server Procedure Complete.". * Print Salary Returned in the host variable. move salary to sal-out. display "Median Salary = " sal-out. display " ". * Call the Remote Procedure. display "Use CALL with SQLDA to invoke Server Procedure." EXEC SQL CALL :prog-name USING DESCRIPTOR (6b) :IO-SQLDA END-EXEC. move "CALL DA" to errloc. call "checkerr" using SQLCA errloc. DISPLAY "Server Procedure Complete.". * Print Salary Returned in IO-SQLDA. move salary to sal-out. display "Median Salary = " sal-out. * Disconnect from Remote Database. EXEC SQL CONNECT RESET END-EXEC. (7) stop run. exit.
program outcli implicit none * Copy Files for Constants and Structures include 'sql.f' include 'sqlenv.f' (1) include 'sqlutil.f' include 'sqldact.f' EXEC SQL INCLUDE SQLCA EXEC SQL BEGIN DECLARE SECTION (3) character*8 dbname character*8 userid character*18 passwd real*8 salary integer*2 sal_ind character*19 prog_name EXEC SQL END DECLARE SECTION integer*4 rc character*80 errloc integer*2 sqlvar1 parameter (sqlvar1 = sqlda_header_sz + 0*sqlvar_struct_sz) character io_sqlda(sqlda_header_sz + 1*sqlvar_struct_sz) (2) character*8 io_sqldaid integer*4 io_sqldabc integer*2 io_sqln integer*2 io_sqld integer*2 io_sqltype1 ! First Variable integer*2 io_sqllen1 integer*4 io_sqldata1 integer*4 io_sqlind1 integer*2 io_sqlnamelen1 character*30 io_sqlnamedata1 equivalence (io_sqlda(sqlda_sqldaid_ofs), io_sqldaid) equivalence (io_sqlda(sqlda_sqldabc_ofs), io_sqldabc) equivalence (io_sqlda(sqlda_sqln_ofs), io_sqln) equivalence (io_sqlda(sqlda_sqld_ofs), io_sqld) equivalence (io_sqlda(sqlvar1+sqlvar_type_ofs), io_sqltype1) equivalence (io_sqlda(sqlvar1+sqlvar_len_ofs), io_sqllen1) equivalence (io_sqlda(sqlvar1+sqlvar_data_ofs), io_sqldata1) equivalence (io_sqlda(sqlvar1+sqlvar_ind_ofs), io_sqlind1) equivalence (io_sqlda(sqlvar1+sqlvar_name_length_ofs), + io_sqlnamelen1) equivalence (io_sqlda(sqlvar1+sqlvar_name_data_ofs), + io_sqlnamedata1) io_sqldaid = 'O_SQLDA' (4) io_sqldabc = sqlda_header_sz + 1*sqlvar_struct_sz io_sqln = 1 io_sqld = 1 io_sqltype1 = sql_typ_nfloat io_sqllen1 = 8 rc = sqlgaddr (%ref(salary), %ref(io_sqldata1)) rc = sqlgaddr (%ref(sal_ind), %ref(io_sqlind1)) * Program Logic print *, 'Enter in the remote database to attach to:' read 100, dbname 100 format (a8) print *, 'Enter your user id (default none):' read 100, userid if( userid(1:1) .eq. ' ' ) then EXEC SQL CONNECT TO :dbname else print *, 'Enter your password :' read 100, passwd print *, 'CONNECT TO Remote Database.' EXEC SQL CONNECT TO :dbname USER :userid USING :passwd end if (5) errloc = 'CONNECT' call checkerr (sqlca, errloc, *999) prog_name = 'outsrv' print *, ' ' print *, 'Use CALL with Host Variable to invoke the Server Procedu cre named outsrv.' EXEC SQL CALL :prog_name (:salary:sal_ind) (6) errloc = 'CALL HV' call checkerr (sqlca, errloc, *999) print *, 'Server Procedure Complete.' * Print Salary Returned in OUTPUT SQLDA print *, 'Median Salary = ', salary print *, ' ' print *, 'Use CALL with SQLDA to invoke the Server Procedure named c outsrv.' EXEC SQL CALL :prog_name USING DESCRIPTOR :io_sqlda (6) errloc = 'CALL DA' call checkerr (sqlca, errloc, *999) print *, 'Server Procedure Complete.' * Print Salary Returned in OUTPUT SQLDA print *, 'Median Salary = ', salary * Disconnect from Remote Database. EXEC SQL CONNECT RESET (7) errloc = 'CONNECT RESET' call checkerr (sqlca, errloc, *999) 999 stop end
/* REXX OUTput CLIent */ /* this variable (SYSTEM) must be user defined */ SYSTEM = OS2 if SYSTEM = OS2 then do if rxfuncquery('SQLDBS') <> 0 then rcy = rxfuncadd( 'SQLDBS', 'DB2AR', 'SQLDBS' ) if rxfuncquery('SQLEXEC') <> 0 then rcy = rxfuncadd( 'SQLEXEC', 'DB2AR', 'SQLEXEC' ) end if SYSTEM = AIX then rcy = SysAddFuncPkg("/usr/lpp/db2_05_00/lib/db2rexx") /* pull in command line arguments */ parse arg dbname userid passwd . /* check to see if the proper number of arguments have been passed in */ if passwd = '' then do say 'USAGE : outcli dbname userid passwd' exit end procname = 'outsrv.cmd' /* INITIALIZE ONE ELEMENT OF OUTPUT SQLDA */ (4) io_sqlda.sqld = 1 io_sqlda.1.sqltype = 485 /* DECIMAL DATA TYPE */ io_sqlda.1.sqllen.scale = 2 /* DIGITS RIGHT OF DECIMAL POINT */ io_sqlda.1.sqllen.precision = 7 /* WIDTH OF DECIMAL */ io_sqlda.1.sqldata = 00000.00 /* HELPS DEFINE DATA FORMAT */ io_sqlda.1.sqlind = -1 /* NO INPUT DATA */ /* CONNECT TO REMOTE DATABASE */ say 'Connect to Remote Database.' call SQLEXEC 'CONNECT TO' dbname 'USER' userid 'USING' passwd (5) call CHECKERR 'CONNECT' /* CALL THE REMOTE PROCEDURE USING CALL AND HOST VARIABLES */ say "Use CALL with Host Variables to invoke the Stored Procedure outsrv" sal=00000.00 /* To force precision 7, scale 2 */ salind=0 call SQLEXEC 'CALL :procname (:sal :salind)' (6a) call CHECKERR 'CALL USING HV' say 'Median Salary = ' sal /* CALL THE REMOTE PROCEDURE USING CALL AND SQLDA */ say "Use the CALL with SQLDA to invoke the Stored Procedure outsrv" call SQLEXEC 'CALL :procname USING DESCRIPTOR :io_sqlda' (6b) call CHECKERR 'CALL USING SQLDA' say 'Median Salary = ' io_sqlda.1.sqldata /* DISCONNECT FROM REMOTE DATABASE */ call SQLEXEC 'CONNECT RESET' (7) call CHECKERR 'CONNECT RESET' exit 0 CHECKERR: arg errloc if ( SQLCA.SQLCODE = 0 ) then return 0 else say '--- error report ---' say 'ERROR occured :' errloc say 'SQLCODE :' SQLCA.SQLCODE /******************************\ * GET ERROR MESSAGE API called * \******************************/ call SQLDBS 'GET MESSAGE INTO :errmsg LINEWIDTH 80' say errmsg say '--- end error report ---' if (SQLCA.SQLCODE < 0 ) then exit else say 'WARNING - CONTINUING PROGRAM WITH ERRORS' return 0 end end return 0
The procedure accepts pointers to SQLDA and SQLCA structures.
If an error occurs, the SQLCA containing the error information is copied to the SQLCA of the client application and a ROLLBACK statement is issued.
Note: | Server procedures cannot be written in REXX on AIX systems. |
#include <memory.h> #include <string.h> #include <sqlenv.h> (1) #include <sql.h> #include <sqlda.h> SQL_API_RC SQL_API_FN outsrv ( (2) void *reserved1, void *reserved2, struct sqlda *inout_sqlda, struct sqlca *ca) { /* Declare a local SQLCA */ EXEC SQL INCLUDE SQLCA; /* Declare Host Variables */ EXEC SQL BEGIN DECLARE SECTION; short num_records; char stmt[512]; EXEC SQL END DECLARE SECTION; /* Declare Miscellaneous Variables */ int counter = 0; EXEC SQL WHENEVER SQLERROR GOTO error_exit; EXEC SQL WHENEVER SQLWARNING CONTINUE; EXEC SQL DECLARE c1 CURSOR FOR s1; /* Prepare a Statement to Obtain and Order all Salaries */ strcpy( stmt, "SELECT salary FROM STAFF ORDER BY salary" ); EXEC SQL PREPARE s1 FROM :stmt; /*Determine the Total Number of Records */ EXEC SQL SELECT COUNT(*) INTO :num_records FROM STAFF; (3) /* Fetch Salaries until the Median Salary is Obtained */ EXEC SQL OPEN c1; while ( counter++ < num_records/2 + 1 ) (4) EXEC SQL FETCH c1 USING DESCRIPTOR :*inout_sqlda; EXEC SQL CLOSE c1; EXEC SQL COMMIT; /* Return the SQLCA to the Calling Program */ (5) memcpy( ca, &sqlca, sizeof( struct sqlca ) ); return(SQLZ_DISCONNECT_PROC); error_exit: /* An Error has occurred -- ROLLBACK and return to Calling Program */ EXEC SQL WHENEVER SQLERROR CONTINUE; memcpy( ca, &sqlca, sizeof( struct sqlca ) ); EXEC SQL ROLLBACK; return(SQLZ_DISCONNECT_PROC); }
Identification Division. Program-ID. "outsrv". Data Division. Working-Storage Section. * Copy Files for Constants and Structures copy "sql.cbl". copy "sqlenv.cbl". (1) copy "sqlca.cbl". * Declare Host Variables EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 num-records pic s9(9) comp-5 value 0. 01 stmt. 49 stmt-len pic s9(4) comp-5. 49 stmt-str pic x(50). EXEC SQL END DECLARE SECTION END-EXEC. * Declare Miscellaneous Variables 77 cntr1 pic s9(9) comp-5 value 0. 77 cntr2 pic s9(9) comp-5 value 0. Linkage Section. * Declare Parameters 77 reserved1 pointer. 77 reserved2 pointer. 77 inout-sqlda pointer. 01 O-SQLCA SYNC. 05 SQLCAID PIC X(8). 05 SQLCABC PIC S9(9) COMP-5. 05 SQLCODE PIC S9(9) COMP-5. 05 SQLERRM. 49 SQLERRML PIC S9(4) COMP-5. 49 SQLERRMC PIC X(70). 05 SQLERRP PIC X(8). 05 SQLERRD OCCURS 6 TIMES PIC S9(9) COMP-5. 05 SQLWARN. 10 SQLWARN0 PIC X. 10 SQLWARN1 PIC X. 10 SQLWARN2 PIC X. 10 SQLWARN3 PIC X. 10 SQLWARN4 PIC X. 10 SQLWARN5 PIC X. 10 SQLWARN6 PIC X. 10 SQLWARN7 PIC X. 10 SQLWARN8 PIC X. 10 SQLWARN9 PIC X. 10 SQLWARNA PIC X. 05 SQLSTATE PIC X(5). Procedure Division using reserved1 reserved2 inout-sqlda O-SQLCA.(2) Main Section. EXEC SQL WHENEVER SQLERROR GOTO ERROR-EXIT END-EXEC. EXEC SQL WHENEVER SQLWARNING CONTINUE END-EXEC. EXEC SQL DECLARE C1 CURSOR FOR S1 END-EXEC. * Prepare a Statement to Obtain and Order all Salaries. move 50 to stmt-len. move "SELECT salary FROM staff ORDER BY salary" to stmt-str. EXEC SQL PREPARE S1 FROM :stmt END-EXEC. * Determine Total Number of Records. EXEC SQL SELECT COUNT(*) INTO :num-records (3) FROM staff END-EXEC. EXEC SQL OPEN C1 END-EXEC. * Fetch Salaries until the Median Salary is Obtained. compute cntr2 = 1 + num-records / 2. perform Fetch-Row until cntr1 = cntr2.(4) EXEC SQL CLOSE C1 END-EXEC. EXEC SQL COMMIT END-EXEC. * Return the SQLCA Information to the Calling Program. move SQLCA to O-SQLCA. EXEC SQL COMMIT END-EXEC. move SQLZ-DISCONNECT-PROC to return-code goback. (5) Fetch-Row. add 1 to cntr1. EXEC SQL FETCH C1 USING DESCRIPTOR :inout-sqlda END-EXEC. ERROR-EXIT. * An Error has Occurred -- ROLLBACK and Return to Calling Program. EXEC SQL WHENEVER SQLERROR CONTINUE END-EXEC. move SQLCA to O-SQLCA. EXEC SQL ROLLBACK END-EXEC. move SQLZ-DISCONNECT-PROC to return-code goback.
integer*4 function outsrv( reserved1, (2) + reserved2, + io_sqlda, + ca ) implicit none * Include Files for Constants and Structures include 'sql.f' (1) include 'sqlenv.f' include 'sqlutil.f' include 'sqldact.f' * Declare Input Parameters integer*4 reserved1 integer*4 reserved2 character io_sqlda(sqlda_header_sz + 1*sqlvar_struct_sz) character ca(sqlca_size) * Declare a Local SQLCA EXEC SQL INCLUDE SQLCA * Declare Host Variables EXEC SQL BEGIN DECLARE SECTION integer*4 num_records character*50 stmt EXEC SQL END DECLARE SECTION * Declare Miscellaneous Variables integer*2 cntr * Program Logic EXEC SQL WHENEVER SQLERROR GOTO 100 EXEC SQL DECLARE c1 CURSOR FOR s1 * Prepare a Statement to Obtain and Order all Salaries stmt = 'SELECT SALARY FROM STAFF ORDER BY SALARY' EXEC SQL PREPARE s1 FROM :stmt * Determine the Total Number of Records EXEC SQL SELECT COUNT(*) INTO :num_records FROM STAFF (3) * Fetch Salaries until the Median Salary is Obtained EXEC SQL OPEN c1 cntr = 0 do 10 while ( cntr .lt. (num_records/2 + 1) ) cntr = cntr + 1 EXEC SQL FETCH c1 USING DESCRIPTOR :io_sqlda (4) 10 end do EXEC SQL CLOSE c1 EXEC SQL COMMIT * Copy Local Variables into Dummy Output Variables * Copy the sqlca to the ca cntr = 0 do 20 while ( cntr .lt. sqlca_size ) cntr = cntr + 1 ca(cntr) = sqlca(cntr) 20 end do goto 200 100 continue ! An Error has Occurred * Copy Local Variables into Dummy Output Variables * Copy the sqlca to the ca EXEC SQL WHENEVER SQLERROR CONTINUE cntr = 0 do 30 while ( cntr .lt. sqlca_size ) cntr = cntr + 1 ca(cntr) = sqlca(cntr) 30 end do EXEC SQL ROLLBACK 200 continue ! Exit Program outsrv = sqlz_disconnect_proc return (5) end
/* REXX OUTput SeRVer */ /* this variable (SYSTEM) must be user defined */ SYSTEM = OS2 if SYSTEM = OS2 then do if rxfuncquery('SQLDBS') <> 0 then rcy = rxfuncadd( 'SQLDBS', 'DB2AR', 'SQLDBS' ) if rxfuncquery('SQLEXEC') <> 0 then rcy = rxfuncadd( 'SQLEXEC', 'DB2AR', 'SQLEXEC' ) end if SYSTEM = AIX then rcy = SysAddFuncPkg("/usr/lpp/db2_05_00/lib/db2rexx") call SQLEXEC 'DECLARE c1 CURSOR FOR s1' call CHECKERR 'DECLARE' /* DETERMINE TOTAL NUMBER OF RECORDS */ stmt = 'SELECT COUNT(*) FROM STAFF' (3) call SQLEXEC 'PREPARE s1 FROM :stmt' call CHECKERR 'PREPARE count statement' call SQLEXEC 'OPEN c1' call CHECKERR 'OPEN count statement' call SQLEXEC 'FETCH c1 INTO :num_records' call CHECKERR 'FETCH' call SQLEXEC 'CLOSE c1' call CHECKERR 'CLOSE c1 after FETCH of num_records' /* PREPARE A STATEMENT TO OBTAIN AND ORDER ALL SALARIES */ stmt = 'SELECT salary FROM STAFF ORDER BY salary' call SQLEXEC 'PREPARE s1 FROM :stmt' call CHECKERR 'PREPARE order statement' call SQLEXEC 'OPEN c1' call CHECKERR 'OPEN order statement' /* FETCH SALARIES UNTIL THE MEDIAN SALARY IS OBTAINED */ i = 0 do while ( i < (num_records/2) ) (4) call SQLEXEC 'FETCH c1 USING DESCRIPTOR :SQLRODA' call CHECKERR 'FETCH' i = i + 1 end call SQLEXEC 'CLOSE c1' call CHECKERR 'CLOSE c1 after FETCH of median' call SQLEXEC 'COMMIT' call CHECKERR 'COMMIT' exit 0 /* Normal exit */ CHECKERR: arg errloc if ( SQLCA.SQLCODE = 0 ) then return 0 else say '--- error report ---' say 'ERROR occured :' errloc say 'SQLCODE :' SQLCA.SQLCODE /******************************\ * GET ERROR MESSAGE API called * \******************************/ call SQLDBS 'GET MESSAGE INTO :errmsg LINEWIDTH 80' say errmsg say '--- end error report ---' if (SQLCA.SQLCODE < 0 ) then exit else say 'WARNING - CONTINUING PROGRAM WITH ERRORS' return 0 end end return 0