For a C++ connector, the GenGlobals class defines the pollForEvents() method. You must provide an implementation of this virtual method as part of your connector class.
The C++-based pseudo-code in Figure 71 shows the basic logic flow for a pollForEvents() method. This method first retrieves a pointer to the subscription manager. The subscription manager manages subscriptions to business objects supported by the connector.
The pollForEvents() method then retrieves a set of events from the event store and, for each event, the method calls the subscription manager class method isSubscribed() to determine whether any subscriptions exist for the corresponding business object. If there are subscriptions, the method retrieves the data from the application, creates a new business object, and calls the subscription manager method gotApplEvent() to send the business object to InterChange Server. If there are no subscriptions, the method archives the event record with a status value of unprocessed.
Figure 71. C++ pollForEvents() example
int ExampleGenGlob::pollForEvents() { SubscriptionHandlerCPP *mySubHandler = GenGlobals::getTheSubHandler(); int status = 0; get the events from the event list for events 0 to PollQuantity in eventlist { extract BOName, verb, and key from the event record if(mySubHandler->isSubscribed(BOName,BOverb) { BO = new BusinessObject(BOName) BO.setAttrValue(key) retrieve application data using doVerbFor() BO.setVerb(Retrieve) BO.doVerbFor() BO.setVerb(BOverb) status = mySubHandler->gotApplEvent(BusinessObject); archive event record with success or failure status } else { archive event record with unprocessed status } return status; }
This section provides more detailed information on each of the steps in the basic logic for the event processing that the pollForEvents() method typically performs. Table 78 summarizes these basic steps.
Table 78. Basic logic of the pollForEvents() method
Step | For more information | |
---|---|---|
1. | Set up a subscription manager for the connector. | "Accessing the subscription manager" |
2. | Verify that the connector still has a valid connection to the event store. | Verifying the connection before accessing the event store |
3. | Retrieve specified number of event records from the event store and store them in an events array. Cycle through the events array. For each event, mark the event in the event store as In-Progress and begin processing. | "Retrieving event records" |
4. | Get the business object name, verb, and key data from the event record. | "Getting the business object name, verb, and key" |
5. | Check for subscriptions to the event. | "Checking for subscriptions to the event" |
If the event has subscribers: | ||
|
"Retrieving application data" | |
|
"Sending the business object to the connector framework" | |
|
"Completing the processing of an event" | |
If the event does not have subscribers, update the event status to Unsubscribed. | "Checking for subscriptions to the event" | |
6. | Archive the event. | "Archiving the event" |
As part of connector initialization, the connector framework instantiates a subscription manager. This subscription manager keeps the subscription list current. (For more information, see "Business object subscription and publishing".) A C++ connector has access to the subscription manager and the connector subscription list through a subscription handler, which is encapsulated by the SubscriptionHandlerCPP class. It can use methods of this class to determine whether business objects have subscribers and to send business objects to the connector controller.
Table 79 lists the method that the C++ connector library provides to obtain a reference to a subscription handler.
Table 79. Method for obtaining a subscription handler
C++ connector library class | Method |
---|---|
GenGlobals | getTheSubHandler() |
For a C++ connector, the pollForEvents() method first sets up a subscription manager for the connector by calling the GenGlobals::getTheSubHandler(). For example:
SubscriptionHandlerCPP *mySub = GenGlobals::getTheSubHandler();
The getTheSubHandler() method returns a pointer to the subscription manager, which is an instance of the SubscriptionHandlerCPP class. Through this subscription handler, the connector can query its subscription manager to find out whether the integration broker is interested in a particular type of business object.
When the init() method in the connector class initializes the application-specific component, one of its most common tasks is to establish a connection to the application. The poll method requires access to the event store. Therefore, before the pollForEvents() method begins processing events, it should verify that the connector is still connected to the application. The way to perform this verification is application-specific. Consult your application documentation for more information.
A good design practice is to code the connector application-specific component so that it shuts down whenever the connection to the application is lost. If the connection has been lost, the connector should not continue with event polling. Instead, it should take the steps in Table 56 to notify the connector framework of the lost connection. The pollForEvents() method should return BON_APPRESPONSETIMEOUT to notify the connector framework of the loss of connection to the application.
Figure 72 contains code to handle the loss of connection to the application. In this example, error message 20018 is issued to inform an administrator that the connection from the connector to the application has been lost and that action needs to be taken.
Figure 72. Loss of connection in pollForEvents()
int ExampleGlobals::pollForEvents() { ... if (//application is not responding ) { // Lost connection to the application // Log an error message logMsg(generateMsg(20018, CxMsgFormat::XRD_FATAL, NULL, 0, "MyConnector")); // Populate a ReturnStatusDescriptor object char errorMsg[512]; sprintf(errorMsg, "Lost connection to application"); rtnObj->seterrMsg(errorMsg); return BON_APPRESPONSETIMEOUT; } .... // if connection is open, continue processing ... }
To send event notifications to the connector framework, the poll method must first retrieve event records from the event store. For a C++ connector, you must use an application-specific interface to retrieve event records from the event store.
The poll method can retrieve one event record at a time and process it or it can retrieve a specified number of event records per poll and cache them to an events array. Processing multiple events per poll can improve performance when the application generates large numbers of events.
The number of events picked up in any polling cycle should be configurable using the connector configuration property PollQuantity. At install time, a system administrator sets the value of PollQuantity to an appropriate number, such as 50. The poll method can use the getConfigProp() to retrieve the value of the PollQuantity property, and then retrieve the specified number of event records and process them in a single poll.
The connector should assign the In-Progress status to any event that it has read out of the event store and has started to process. If the connector terminates while processing an event and before updating the event status to indicate that the event was either sent or failed, it will leave an In-Progress event in the event store. For more information on how recover these In-Progress events, see "Recovering In-Progress events"..
To retrieve event records from the event store, a C++ connector must use whatever technique the application provides. As an example, an implementation for a table-based application might create an event table from which the connector application-specific code retrieves event data. The code fragment in Figure 73 shows how a connector for this kind of implementation can retrieve events from an event table using the ODBC API and ODBC SQL commands. The program initially defines the macro for a SQL SELECT statement that retrieves event records from the event table.
#define GET_EVENT_QUERY \ "SELECT event_id, object_name, object_verb, object_key \ FROM cw_events WHERE event_status = 0 \ ORDER BY event_time"
This SELECT statement retrieves the event identifier, business object name, verb, and key data from those event records whose status is 0 ( Ready-for-Poll). These event records are ordered by their timestamp. For information on retrieving event records by event priority, see "Processing events by event priority"..
The poll method allocates memory for the data, binds the memory to specific columns of data, and retrieves the data one event record at a time. For the complete example program, see Example of a basic pollForEvents() method..
Figure 73. Retrieving an event
// Allocate a statement handle for the SQL statement rc = SQLAllocStmt(gHdbc1, &hstmt1); // Execute the SELECT query. Use the macro GET_EVENT_QUERY rc = SQLExecDirect(hstmt1, GET_EVENT_QUERY, SQL_NTS); // Allocate memory for event data ev_id = new char[80]; obj_name = new char[80]; obj_verb = new char[80]; obj_key = new char[80]; query = new char[255]; key_value = new char[80]; // Bind all results set columns to event variables rc = SQLBindCol(hstmt1, 1, SQL_C_CHAR, ev_id, 80, &cbValue); rc = SQLBindCol(hstmt1, 2, SQL_C_CHAR, obj_name, 80, &cbValue); rc = SQLBindCol(hstmt1, 3, SQL_C_CHAR, obj_verb, 80, &cbValue); rc = SQLBindCol(hstmt1, 4, SQL_C_CHAR, obj_key, 80, &cbValue); // Fetch the event data while (rc == SQL_SUCCESS || rc != SQL_SUCCESS_WITH_INFO) { ev_id = '\0'; obj_name = '\0'; obj_verb = '\0'; obj_key = '\0'; rc = SQLFetch(hstmt1); if (rc == SQL_SUCCESS) { // Process the record if ((cp = strchr(ev_id, ' ')) != NULL) *cp = NULL; if ((cp = strchr(obj_name, ' ')) != NULL) *cp = NULL; if ((cp = strchr(obj_verb, ' ')) != NULL) *cp = NULL; if ((cp = strchr(obj_key, ' ')) != NULL) *cp = NULL; }
Once the connector has retrieved an event, it extracts the event ID, the object key, and the name and verb of the business object from the event record. The connector uses the business object name and verb to determine whether the integration broker is interested in this type of business object. If the business object and its active verb have subscribers, the connector uses the entity key to retrieve the complete set of data.
For a C++ connector, you must use an application-specific interface to obtain this information from the event records in the event store. Figure 73 showed one way that a C++ connector can process event data. In the example, the appropriate event data is retrieved into the connector variables:
Event ID | ev_id |
Business object name | obj_name |
Verb | obj_verb |
Object key | obj_key |
The connector should send the business object with the same verb that was in the event record.
To determine whether the integration broker is interested in receiving a particular business object and verb, the poll method calls the isSubscribed() method. The isSubscribed() method takes the name of the current business object and a verb as arguments. The name of the business object and verb must match the name of the business object and verb in the repository.
Other integration brokers |
---|
If your business integration system uses a WebSphere message broker (WebSphere MQ Integrator, WebSphere MQ Integrator Broker, or WebSphere Business Integration Message Broker) or WebSphere Application Server, the connector framework assumes that the integration broker is interested in all the connector's supported business objects. If the poll method uses the isSubscribed() method to query the connector framework about subscriptions for a particular business object, the method returns true for every business object that the connector supports. |
Table 80 lists the method that the C++ connector library provides to check for subscriptions to the event.
Table 80. Method for checking subscriptions
C++ connector library class | Method |
---|---|
SubscriptionHandlerCPP | isSubscribed() |
Based on the value that isSubscribed() returns, the poll method should take one of the following actions:
Connector action taken | For more information |
---|---|
Retrieve the complete set of business object data from the entity in the application database. | "Retrieving application data" |
Send the business object to the connector framework, which routes it to the integration broker. | "Sending the business object to the connector framework" |
Archive the event (if archiving is implemented) in case an integration broker subscribes at a later time. | "Archiving the event" |
IBM suggests that the connector return "fail" if no subscriptions exist for the event. However, you can return the outcome status that your design dictates.
No other processing should be done with unsubscribed events. If at a later date, the integration broker subscribes to these events, a system administrator can move the unsubscribed event records from the archive store back to the event store.
As Table 80 shows, the isSubscribed() method is provided in the subscription manager, the SubscriptionHandlerCPP object. The method returns 1 if there are subscribers, and 0 if there are no subscribers. An example of a C++ connector checking for subscriptions follows:
if (mySubHndlr->isSubscribed(obj_name, obj_verb) = TRUE) { // handle event } else { // archive the event (if archiving is supported) }
If there are subscribers for an event, the poll method must take the following steps:
To retrieve the complete set of entity data, the poll method must use name of the entity's key information (which is stored in the event) to locate the entity in the application database. The poll method must retrieve the complete set of application data when the event has the following verbs:
For a Delete event from an application that supports physical deletes, the application may have already deleted the entity from the database, and the connector may not be able to retrieve the entity data. For information on delete processing, see "Processing Delete events"..
Once the populated business object exists, the poll method can publish the business object to subscribers.
Table 80 lists the methods that the C++ connector library provides to retrieve entity data from the application database and populate a business object.
Table 81. Methods for retrieving business object data
C++ Connector Library Class | Method |
---|---|
BusinessObject | doVerbFor(),, getAttrCount(),, getAttrType(),, getAttrValue(),, setAttrValue(),, setVerb() |
For a C++ connector, the standard way of retrieving application data from within pollForEvents() is to use the Retrieve method in the connector's business object handler. With this approach, you do not have to recode data retrieval in the pollForEvents() method.
To retrieve application data using the doVerbFor() method of the BusinessObject class, follow these general steps:
The setAttrValue() method takes as arguments the name of the attribute, a string representation of the value or pointer to the value of the attribute, and the attribute type.
The BusinessObject class implementation of the doVerbFor() method calls the business object handler specified in the business object definition to perform the action of the verb. The doVerbFor() method operates on the current business object, filling the business object with the current values of the application entity.
The C++ code fragment in Figure 74 illustrates this approach.
Figure 74. Retrieving application data
// Create a new business object pBusObj = new BusinessObject(obj_name); // Set verb to Retrieve pBusObj->setVerb("Retrieve"); // Extract value of key from key:value pair if ((cp = strchr(obj_key, ':')) != NULL) { cp++; strcpy(key_value, cp); cp--; *cp = NULL; } // Find the key attribute in the business object and // set it to the key value for (i = 0; i < pObj->getAttrCount()-1; i++) { if (pBusObj->getSpecFor()->getAttribute(i)->isKey()) { pBusObj->setAttrValue(pBusObj->getAttrName(i),key_value, pBusObj->getAttrType(pBusObj->getAttrName(i))); } } // Call the business object handler doVerbFor() if (pBusObj->doVerbFor() == BON_FAIL) { // Log error message if retrieve fails retcode = BON_FAIL; }
The code fragment in Figure 74 uses getAttrName() to get the name of each attribute that is a key. It then calls getAttrType() to get the type of the key attribute. The setAttrValue() method verifies that the new value has the correct data type before changing the attribute value.
The ObjectEventId attribute is used in the IBM WebSphere business integration system to track the flow of business objects through the system. In addition, it is used to keep track of child business objects across requests and responses, as child business objects in a hierarchical business object request might be reordered in a response business object.
Connectors are not required to populate ObjectEventId attributes for either a parent business object or its children. If business objects do not have values for ObjectEventId attributes, the IBM WebSphere business integration system generates values for them. However, if a connector populates child ObjectEventIds, the values must be unique across all other ObjectEventId values for that particular business object regardless of level of hierarchy. ObjectEventId values can be generated as part of the event notification mechanism. For suggestions on how to generate ObjectEventId values, see "Event identifier"..
Once the data for the business object has been retrieved, the poll method performs the following tasks:
Table 80 lists the methods that the C++ connector library provides to set the business object verb and send the business object.
Table 82. Classes and methods for setting verb and sending business object
C++ connector library class | Method |
---|---|
BusinessObject | setVerb() |
SubscriptionHandlerCPP | gotApplEvent() |
To set the verb in a business object to the verb specified in the event record, the poll method calls the business object method setVerb(). The poll method should set the verb to the same verb that was in the event record in the event store.
For a C++ connector , the populated BusinessObject object that the doVerbFor() method returns still has a verb of Retrieve. The poll method must set the business object's verb to its original value with the setVerb() method, as the following code fragment shows:
// Set verb to action as indicated in the event record pBusObj->setVerb(obj_verb);
In this code fragment, obj_verb is the verb value that has previously been obtained from the event record, as shown in Figure 73..
The poll method uses the method gotApplEvent() to send the business object to the connector framework. This method takes the following steps:
The connector framework does some processing on the event object to serialize the data and ensure that it is persisted properly. It then makes sure the event is sent.
Other integration brokers |
---|
If your business integration system uses a WebSphere message broker (WebSphere MQ Integrator, WebSphere MQ Integrator Broker, or WebSphere Business Integration Message Broker) or WebSphere Application Server, the connector framework makes sure the event is converted to an WebSphere MQ message and written to the appropriate MQ queue. |
The poll method should check the return code from gotApplEvent() to ensure that returned errors are handled appropriately. For example, until the event delivery is successful, the poll method should not remove the event from the event table. Table 83 shows the possible event-status values, based on whether event delivery is successful.
Table 83. Possible event status after event delivery
State of Event Delivery | Event Status |
---|---|
If the event delivery is successful | 1 (see Table 40) |
If the event delivery fails | -2 (see Table 40) |
The following code fragment shows the call to gotApplEvent() for a C++ connector:
// Send business object to connector framework if (( retcode = mySub->gotApplEvent(*pBusObj)) == BON_FAIL) { // Log an error message // Update event status to "error posting event" // Event remains in event table and is not archived } // Update event status to "event successfully posted"
The gotApplEvent() method returns BON_SUCCESS if the connector framework successfully delivers the business object, and returns a nonzero value (such as BON_FAIL) if the delivery fails.
The processing of an event is complete when the tasks in Table 84 complete.
Table 84. Steps in processing an event
Processing task | For more information |
---|---|
The poll method has retrieved the application data for the event and created a business object that represents the event. | "Retrieving application data" |
The poll method has sent the business object to the connector framework. | "Sending the business object to the connector framework" |
The poll method must ensure that the event status correctly reflects the completion of the event processing. Therefore, it must handle both of the following conditions:
The processing of an event is successful when the tasks in Table 84 successfully complete. The following steps show how the poll method should finish processing a successful event:
Until the event delivery is successful, the poll method should not remove the event from the event table.
If an error occurs in processing an event, the connector should update the event status to indicate that an error has occurred. Table 85 shows the possible event-status values, based on errors that can occur during event processing.
Table 85. Possible event status after unsuccessful event processing
State of Event Delivery | Event Status |
---|---|
If an error occurs in processing an event | -1 (see Table 40) |
If the event delivery fails | -2 (see Table 40) |
For example, if there are no application entities matching the entity key, the event status should be updated to "error processing event". As discussed in "Sending the business object", the poll method should check the return code from gotApplEvent() to ensure that any errors that are returned are handled appropriately. If the event cannot be successfully delivered, its event status should be updated to "error posting event".
In either case, the event should be left in the event store to be analyzed by a system administrator. When the poll method queries for events, it should exclude events with the error status so that these events are not picked up. Once an event's error condition has been resolved, the system administrator can manually reset the event status so that the event is picked up by the connector on the next poll.
Archiving an event consists of creating an archive record by moving the event record from the event store to an archive store. To archive event records to the archive store, a C++ connector must use whatever technique the application provides. Usually, the connector uses whatever method it used to access event records in the event store, such as the ODBC API and ODBC SQL commands. Consult your application documentation for more information.
To archive event records from this event store, the poll method takes the following actions:
Table 86 shows the event-status values that the archive record will usually have.
Table 86. Event-status values in an archive record
Event-status value | Description |
---|---|
1 | The event was detected, and the connector created a business object for the event and sent the business object to the connector framework. For more information, see "Handling successful event processing". |
2 | The event was detected, but there were no subscriptions for the event, so the event was not sent to the connector framework and on to the integration broker. For more information, see "Checking for subscriptions to the event". |
-1 | The event was detected, but the connector encountered an error when trying to process the event. The error occurred either in the process of building a business object for the event or in sending the business object to connector framework. For more information, see "Handling unsuccessful event processing". |
After archiving is complete, your poll method should set the appropriate return code:
The section provides implementations of the basic logic for the pollForEvents() method in a C++ connector. The code sample in Figure 75 demonstrates event handling in a C++ connector that uses the ODBC API to communicate with an application database. In this example, the connector retrieves one event at a time and processes it.
As a first step in the pollForEvents() method, a utility function allocates handles for two ODBC connections to the application database. The connector uses these connection handles to interact with the database.
The pollForEvents() method gets a pointer to the subscription manager using GenGlobals::getTheSubHandler(). It then executes a SQL SELECT statement using the ODBC SQLExecDirect() interface to determine what data is returned. When the SQL statement returns, the method allocates memory for the data, assigns the memory to specific columns of data, and retrieves the data one event record at a time into allocated memory.
For each event, the connector calls the subscription manager method isSubscribed() to determine whether there are subscribers to this particular business object and verb. If there are subscribers, the connector builds a new business object, sets the key, sets the business object's verb to Retrieve, and calls the business object's doVerbFor() method to retrieve the complete set of data for the application entity. If doVerbFor() returns success, the poll method sets the appropriate verb in the business object and sends the business object to InterChange Server using the method gotApplEvent().
Once the business object is sent, the poll method executes a SQL statement to remove the event from the event table. This action causes a delete trigger to store the event in the archive table.
Figure 75. Implementation of basic logic for pollForEvents()
// The event table is named cw_events. #define GET_EVENT_QUERY \ "SELECT event_id, object_name, object_verb, object_key \ FROM cw_events WHERE status = 0 AND priority = 1 \ ORDER BY event_time" #define REMOVE_EVENT_QUERY \ "DELETE FROM cw_events WHERE event_id = '%s'"; int ExampleGlobals::pollForEvents() { SubscriptionHandlerCPP *mySub; BusinessObject *pObj = NULL; char *ev_id = 0; char *obj_name = 0; char *obj_verb = 0; char *obj_key = 0; char *query = 0; char *key_value = 0; char *cp; int i; RETCODE rc; HSTMT hstmt1 = 0; HSTMT hstmt2 = 0; SDWORD cbValue = SQL_NO_TOTAL; int retcode = BON_SUCCESS; // Private function for establishing and checking the two // database connections that the poll function needs checkOdbcConnections(); // Set up the subscription manager mySub = GenGlobals::getTheSubHandler(); // Allocate a statement handle for the SQL statement rc = SQLAllocStmt(gHdbc1, &hstmt1); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { // handle odbc errors return (BON_APPRESPONSETIMEOUT); } // Execute the event SQL SELECT query to determine // what data will be returned. // Use the macro GET_EVENT_QUERY rc = SQLExecDirect(hstmt1, GET_EVENT_QUERY, SQL_NTS); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { // handle odbc errors rc = SQLFreeStmt(hstmt1, SQL_CLOSE); return (BON_APPRESPONSETIMEOUT); } // Allocate memory for event table columns ev_id = new char[80]; obj_name = new char[80]; obj_verb = new char[80]; obj_key = new char[80]; query = new char[255]; key_value = new char[80];
// Bind all results set columns to variables rc = SQLBindCol(hstmt1, 1, SQL_C_CHAR, ev_id, 80, &cbValue); rc = SQLBindCol(hstmt1, 2, SQL_C_CHAR, obj_name, 80, &cbValue); rc = SQLBindCol(hstmt1, 3, SQL_C_CHAR, obj_verb, 80, &cbValue); rc = SQLBindCol(hstmt1, 4, SQL_C_CHAR, obj_key, 80, &cbValue); // Fetch the results while (rc == SQL_SUCCESS || rc != SQL_SUCCESS_WITH_INFO) { ev_id = '\0'; obj_name = '\0'; obj_verb = '\0'; obj_key = '\0'; rc = SQLFetch(hstmt1); if (rc == SQL_SUCCESS || rc != SQL_SUCCESS_WITH_INFO) { // Process the record // Trim off rest of string at the first // space character found if ((cp = strchr(ev_id, ' ')) != NULL) *cp = NULL; if ((cp = strchr(obj_name, ' ')) != NULL) *cp = NULL; if ((cp = strchr(obj_verb, ' ')) != NULL) *cp = NULL; if ((cp = strchr(obj_key, ' ')) != NULL) *cp = NULL; // Determine whether there are subscribers to the event if (mySub->isSubscribed(obj_name, obj_verb) != TRUE) { // log message // delete event from event table // add event to archive table continue; } // Prepare to retrieve data into the business object pObj = new BusinessObject(obj_name); pObj->setVerb("Retrieve"); // Get key:value pair if ((cp = strchr(obj_key, ':')) != NULL) { cp++; strcpy(key_value, cp); cp--; *cp = NULL; } // Find the first key in the object and set it for (i = 0; i < pObj->getAttrCount()-1; i++) { if (pObj->getSpecFor()->getAttribute(i)->isKey()) { pObj->setAttrValue(pObj->getAttrName(i), key_value, pObj->getAttrType(pObj->getAttrName(i))); break; } }
// Call the business object handler doVerbFor() // to retrieve application data if (pObj->doVerbFor() == BON_FAIL) { // Log error message if retrieve fails // Handle retrieve errors retcode = BON_FAIL; break; } // Call gotApplEvent() to send the business object // with the info pObj->setVerb(obj_verb); if ((mySub->gotApplEvent(*pBusObj)) == BON_FAIL) { // Log error message retcode = BON_FAIL; break; } // Allocate statement handle for the SQL // statement on the second connection rc = SQLAllocStmt(gHdbc2, &hstmt2); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { // Handle ODBC errors hstmt2 = (HSTMT)0; retcode = BON_APPRESPONSETIMEOUT; break; } // Remove the event from the event table // This will execute a trigger that will archive // the event in the archive table // Use the REMOVE_EVENT_QUERY macro. sprintf(query, REMOVE_EVENT_QUERY, ev_id); rc = SQLExecDirect(hstmt2, query, SQL_NTS); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { // Handle odbc errors retcode = BON_APPRESPONSETIMEOUT; break; } else { // Handle odbc errors retcode = BON_APPRESPONSETIMEOUT; break; } } // Clean up and free resources associated with // the statement handles if (hstmt1) { rc = SQLFreeStmt(hstmt1, SQL_DROP); } if (hstmt2) { rc = SQLFreeStmt(hstmt2, SQL_DROP); }
if (ev_id) { delete ev_id; } if (obj_name) { delete obj_name; } if (obj_verb) { delete obj_verb; } if (query) { delete query; } if (key_value) { delete key_value; } return (retcode); }