![]()  |  Application Development |       ![]()  |  
You can write the SQL statements needed to access the database for the entire application or for a single screen in the application.
Database Development Process | ![]()  | 
For database applications where you write the SQL statements, the following steps outline a possible application development process:
Database Interface Commands | ![]()  | 
Two database interface commands can be used to construct your own SQL statements: DBMS QUERY and DBMS RUN. DBMS QUERY is used for SQL SELECT statements and for stored procedures that return result set data to the application. For more information on fetching database information, refer to Chapter 29, "Reading Information from the Database."
DBMS RUN is used for data modification statements, such as SQL UPDATE, that do not return data to the application. For more information on writing information to a database, refer to Chapter 30, "Writing Information to the Database."
Using Database Cursors | ![]()  | 
A cursor is a SQL object associated with a specific query or operation. Panther stores information on each cursor, including:
DBMS commands: ALIAS, CATQUERY, COLUMN_NAMES, FORMAT, OCCUR, START, STORE, and UNIQUE.
 Every connection has one or two default cursors which Panther automatically creates. Your application can also declare named cursors on a connection; Panther can use either or both types. 
 This section describes the use of cursors, default and named cursors, in an application. For information on how data are passed between an application and a database, refer to Chapter 29, "Reading Information from the Database," and Chapter 30, "Writing Information to the Database."
 Default cursors are convenient for SQL statements that are executed once, and for applications using only one select set at a time. Database commands executed with the JPL commands DBMS QUERY, DBMS RUN, and DBMS SQL use default cursors unless a different cursor is specified.
 For most engines, Panther automatically declares two default cursors—one for SQL  A default  The first statement sets c2 as the default connection. The second statement uses WITH CONNECTION to set c1 as the current connection for the  An application can also close the default cursor if it is not needed. For more information, refer to "Closing a Cursor."
 Named cursors are convenient for SQL statements that are executed several times. A cursor is declared for a statement; executing the cursor, executes the statement. Named cursors often improve an application's efficiency because the same statement does not need parsing each time it is executed. Named cursors are also necessary for applications using more than one select set at a time. 
 When a cursor is declared, Panther creates a structure for it and adds its name to a list of open cursors. The cursor is available throughout the application until the application closes the cursor or closes the cursor's connection. Panther frees the structure when the cursor is closed. 
 You can create one or more named cursors to access and manipulate data. The sequence is as follows:
DBMS commands are provided for changing the default behavior for a cursor associated with a SELECT statement. The commands are ALIAS, CATQUERY, COLUMN_NAMES, FORMAT, OCCUR, START, and UNIQUE. For descriptions of these commands, refer to Chapter 11, "DBMS Statements and Commands," in Programming Guide.
 Using a Default Cursor
SELECT statements and one for non-SELECT statements (such as UPDATE). In a few cases, where the engine's standard is a single default cursor, Panther adheres to that standard and declares one default cursor. On such engines, an additional option, CURSORS, is supported in the engine's DECLARE CONNECTION statement. It permits you to choose between one or two default cursors for the connection. For more information on how cursors are handled for each engine, refer to Database Drivers.
SELECT cursor is associated with a particular connection, namely the connection in effect when a SELECT statement is executed. For example:
DBMS CONNECTION c2
DBMS WITH CONNECTION c1 QUERY \
	SELECT title_id, name FROM titles \
	WHERE genre_code = 'ADV'DBMS RUN UPDATE titles SET pricecat = :+pricecat \
	WHERE title_id = :+title_idSELECT statement. In the UPDATE statement, no connection is specified. Therefore, Panther uses the default connection c2.
 Using a Named Cursor
 Named cursors are created with a declaration statement. The statement names the cursor and associates it with a connection and a SQL statement. If a connection is not named in the declaration, Panther uses the default connection.
 An application can declare a named cursor for any valid SQL statement. For example:
 The SQL statement is not executed until the cursor is executed:
 The cursor can be executed any number of times. The name of the cursor must be a valid Panther identifier. The cursor name is case-sensitive, so  For more information on writing data to the database and using parameters in a cursor declaration, refer to "Using Parameters in a Cursor Declaration."
 A cursor can use colon-variables in the DECLARE CURSOR statement. For example:
 The variable  To dereference variables each time the cursor is executed, use bind tags in the DECLARE CURSOR statement. For example:
 The bind tag is two colons followed by any valid identifier; the bind tag is not an actual variable. When the cursor is executed, the application must provide a literal value, a valid variable name, or a Panther expression for each bind tag. When this particular example is executed, the application fetches all rentals where  You can supply a new variable for the bind tag:
 Literals and expressions are valid values for a bind tag. For example:
 or
 The first example supplies the literal "Citizen Kane" as the value for the bind tag. The second example uses the concatenation operator ( It is not required to supply the bind tag names in the EXECUTE USING statement. If the tag names are not supplied, Panther associates the first variable with the first tag, the second variable with the second tag, etc. For example:
 Panther uses the contents of  A bind tag is valid for any column value in a DECLARE CURSOR statement. A bind tag is not permitted for SQL keywords, table names, or columns names. Therefore, bind tags are valid for column values in any of the following:
 Declaring a Cursor
DBMS [ WITH CONNECTION connectionName ] \
	DECLARE cursorName CURSOR FOR SQL_statementDBMS DECLARE c1 CURSOR FOR SELECT * FROM rentals
DBMS WITH CURSOR c1 EXECUTE
CUR1 and cur1 are two distinct names.
 Supplying Values Using Colon Expansion
DBMS DECLARE c1 CURSOR FOR \
	SELECT * FROM rentals WHERE rental_date = :+todaytoday is dereferenced when the cursor is declared. It is not dereferenced when the cursor is executed. An application can use colon variables or colon-plus variables anywhere in the statement.
 Supplying Values Using Binding
DBMS DECLARE c1 CURSOR FOR \
	SELECT * FROM rentals WHERE rental_date = ::rental_dateDBMS WITH CURSOR c1 EXECUTE USING rental_date = today
rental_date is the value of today. To execute the select again where rental_date is another value, change the contents of today and reexecute the cursor:
today = @date(today) - 1
DBMS WITH CURSOR c1 EXECUTE USING rental_date = todayDBMS WITH CURSOR c1 EXECUTE USING rental_date = yesterday
DBMS DECLARE c1 CURSOR FOR \
	SELECT * FROM titles WHERE title LIKE ::title_qbeDBMS WITH CURSOR c1 EXECUTE USING title_qbe = "Citizen Kane"
DBMS WITH CURSOR c1 EXECUTE USING \
	title_qbe = title_val ## "%"##) to append the contents of the title_val variable with the literal percent sign (%) as the value for the bind tag.
DBMS DECLARE c1 CURSOR FOR \
	SELECT * FROM customers \
	WHERE first_name LIKE ::first_qbe \
	AND last_name LIKE ::last_qbeDBMS WITH CURSOR c1 EXECUTE USING f1, f2
f1 as the value for bind tag ::first_qbe and uses the contents of f2 as the value for bind tag ::last_qbe.
WHERE clause of SELECT, UPDATE, and DELETE statements.
 For example:
 Bind tags are also valid for stored procedure parameter values.
 The command DBMS EXECUTE does not permit the WITH CONNECTION clause. The cursor remains associated with the connection specified by name or by default in the  When cursor  A cursor can be redeclared on the same connection for another SQL statement. For example:
 If the cursor is associated with a  Generally, the transaction manager declares and closes cursors as needed. Once the transaction manager creates the select cursor during a  In the following example, the  In addition, the RELEASE command gives up cursors in the transaction manager.
 Cursors are closed when the application closes the connection. However, if you want to reuse a named cursor, redeclare a cursor on a different connection or free the resources needed by the cursor, you must close the cursor.
 The default cursor remains closed unless the application executes a DBMS QUERY,  DBMS DECLARE c1 CURSOR FOR \
	UPDATE pricecats SET price = ::newprice \
	WHERE pricecat = ::pricecatDBMS WITH CURSOR c1 EXECUTE USING \
	newprice = price_fld, pricecat = pricecat_fldDBMS DECLARE c1 CURSOR FOR \
	INSERT INTO pricecats \
	(pricecat, pricecat_dscr, rental_days, price, late_fee) \
	VALUES (::p1, ::p2, ::p3, ::p4, ::p5)DBMS WITH CURSOR c1 EXECUTE USING \
	p1 = pricecat, p2 = pricecat_dscr, p3 = rental_days, \
	p4 = price, p5 = late_fee Executing a Cursor with Multiple Connections
DECLARE statement. For example:
DBMS CONNECTION sybcon
DBMS DECLARE cur1 CURSOR FOR SELECT * FROM titles
DBMS CONNECTION oracon
DBMS WITH CURSOR cur1 EXECUTE
DBMS RUN UPDATE ....
cur1 is declared, Panther associates it with the default connection sybcon. Although the default connection is changed to oracon before the cursor is executed, the connection associated with cur1 does not change. When the cursor is executed, Panther performs the SELECT on connection sybcon. The default connection oracon performs the subsequent UPDATE.
 Modifying a Cursor
DBMS DECLARE abc CURSOR FOR \
	SELECT cust_id, title_id FROM rentals \
	WHERE return_date IS NULL
DBMS WITH CURSOR abc EXECUTE
DBMS DECLARE abc CURSOR FOR \
	SELECT * FROM titles WHERE title_id = ::title_num
DBMS WITH CURSOR abc EXECUTE USING title_numSQL SELECT statement, you can modify its behavior by using additional DBMS commands These commands include ALIAS, CATQUERY which can be used with FORMAT, COLUMN_NAMES, OCCUR, START, and UNIQUE. Refer to Chapter 11, "DBMS Statements and Commands," in the Programming Guide for more information about each command. These settings are not lost when a cursor is redeclared, but only when the cursor is closed. A cursor cannot be redeclared for a different connection. 
 Using Cursors in the Transaction Manager
TM_GET_SEL_CURSOR event, the variable @tm_sel_cursor contains the name of the select cursor. Using this variable, you can write a transaction event function to declare the cursor and to execute any additional processing. Then, subsequent transaction events attach the SQL statement by redeclaring the cursor. 
make_cursor event function declares the cursor and sets a variable to hold select results with the DBMS CATQUERY command. Then, if you choose the VIEW or SELECT command in the transaction manager, this event function is called and is followed by the transaction events that redeclare and execute the cursor with the applicable SQL statement, writing the select results to the title_all variable.
proc make_cursor (event)
if event == TM_SEL_BUILD_PERFORM
{
	DBMS DECLARE :@tm_sel_cursor CURSOR
	DBMS WITH CURSOR :@tm_sel_cursor CATQUERY title_all 
	return TM_PROCEED
}
return TM_PROCEED Closing a Cursor
 To close a cursor and free its data structure, execute:
DBMS CLOSE CURSOR 
cursorNameor
DBMS WITH CURSOR 
cursorName CLOSE To close the default cursor, execute:
DBMS CLOSE CURSOR
DBMS RUN or DBMS SQL statement without specifying a cursor using the WITH CURSOR clause. Panther automatically reopens the default cursor if it is needed.
Database Transaction | ![]()  | 
A database transaction is a logical unit of work on a database. The unit of work is usually a set of statements that update a database in a consistent way. Either all of the statements in the unit must be completed or none of the statements should be completed at all.
In the VideoBiz sample application, there are a least two transactions:
 Transaction processing is engine dependent and requires an understanding of the engine's behavior. For some errors, the application must explicitly tell the engine to undo the transaction. The application must test for these errors.
 Transaction processing is not implemented consistently among SQL databases. Review the documentation on transaction processing supplied by your database vendor.
 Database transaction processing, in general, occurs when changes are being made to a database. The transaction begins (with a  On engines supporting explicit transactions, each  Engines have different ways of handling transactions that are not terminated by an explicit commit or rollback. Some automatically commit or rollback the transaction; others can leave the database in an inconsistent state. Under no circumstances should your application use the engine's default behavior to terminate a transaction.
 Use explicit rollbacks and commits to:
 Engine-Specific Behavior
DBMS BEGIN statement), and the changes are not permanently effective until the transaction is committed (with a DBMS COMMIT statement). The alternative to committing a transaction is rolling it back (with a DBMS ROLLBACK statement), which essentially throws away the changes. Usually, database engines support either explicit transactions, as described here, or auto transactions, which generally start with the first recoverable statement after a logon, COMMIT, or ROLLBACK. 
COMMIT or ROLLBACK must have a matching BEGIN. On engines supporting autocommit modes, the application can use any number of COMMIT or ROLLBACK statements; if there is no recoverable statement, the COMMIT or ROLLBACK is ignored. 
 Finally, although vendors supply commands for transaction processing in their SQL language, use  There are various kinds of errors that can occur during an application. The engine is responsible for recovery from system failures such as power loss. Also, if a single statement fails for some reason in the middle of execution, the engine is responsible for rolling back the effects of that statement. If that statement was executed in a transaction, however, the application must execute an explicit rollback to undo any work done between the start of the transaction and the failed statement.
 At the very least, a Panther application must execute a rollback when the engine returns an error to the application. An example of this would be when the engine rejects an insert because the row's primary key is not unique. If the insert were part of a transaction, the application should stop executing the transaction and execute a rollback to undo any work done by previous statements in the transaction.
 As an additional precaution, it is recommended that you execute a rollback for any error that occurs during the transaction, including an error detected by Panther before a statement is passed to the engine. An error detected by Panther rather than the engine is usually the result of a development or maintenance error rather than bad user input (for example, a statement's colon-plus or binding variable cannot be found because a widget was renamed). While these errors are rare, the application should provide handling for them.
 If the transaction processing is done with the C library functions provided by Panther's database drivers, error codes from Panther are returned to the calling function, either directly or via an installed error handler. If a transaction requires very sophisticated error handling, it might be easier to use these Panther library functions rather than JPL.
 One method for transaction processing in JPL uses a generic JPL procedure as a transaction handler. This JPL procedure could perform the following:
DBMS COMMIT, DBMS ROLLBACK, and other transaction commands provided with Panther database drivers. Using DBMS RUN to specify engine-specific commit and rollback processing is not recommended. Using the DBMS versions permits Panther to establish necessary structures and it provides better error handling if a transaction fails.
 Error Processing for a Transaction
jpl_retcode.
 A sample of such a procedure is shown in the JPL code below. The actual transaction statements are executed in the subroutine whose name is passed to this procedure. This transaction handler can be used with the default error handler or with an installed error handler that returns the abort code ( In this application, there are JPL procedures containing transactions which update the database. The  To execute this new customer transaction, the application should execute the following JPL statements:
 Once  Refer to the Database Drivers for a list and description of the supported transaction commands for each engine.
1) for all errors.
proc tran_handle (subroutine)
{
	vars jpl_retcode
# Call the subroutine.
	jpl_retcode = :subroutine
# Check the value of jpl_retcode. If it is 0, all
# statements in the subroutine executed successfully
# and the transaction was committed. If it is 1,
# the error handler aborted the subroutine. If it
# is -1, Panther aborted the subroutine. Execute a
# ROLLBACK for all non-zero return codes.
	if jpl_retcode
	{
		msg emsg "Aborting transaction."
		DBMS ROLLBACK
	}
	else
	{
		msg emsg "Transaction succeeded."
	}
	return 0
}new_cust procedure adds a new customer to the database:
proc new_cust()
{
	DBMS RUN INSERT INTO customers ....
	DBMS COMMIT
	return 0
}vars newCust = "new_cust()"
call tran_handle (newCust)tran_handle has set up the variable, it calls the procedure new_cust. Whether new_cust is successful or unsuccessful, control is always returned to tran_handle.