Product Documentation

c-treeACE V11.0 Update Guide

Previous Topic

Next Topic

ctRecordUpdateCallbackControl

Declaration

ctRecordUpdateCallbackControl() is used to add and delete callback function definitions. Its prototype is as follows:

NINT ctDECL ctRecordUpdateCallbackControl(pRUCBCTL prucbctl);

Description

A file can have one or more callback function definitions. Each callback function definition is identified by its name, which is a case-sensitive ASCII string. The callback functions are called in the order in which they were added.

These callbacks execute within the server process space. Programming errors will affect the entire database server process.

The synchronous callback types ( RUCBonrecupd, RUCBontrancmt ) occur in the context of the calling user. Most c-tree APIs cannot be safely called from these callbacks, as they may alter existing internal user state information. For advanced usage, information could be sent to another thread to make c-tree file calls if desired. ctThrd* APIs are useful in this regard to pass information to another thread.

The asynchronous callback types ( RUCBqueuethrd, RUCBqueueapp ) are more flexible, since they don't share the same state as the original operation. It should be safe to directly call c-tree file APIs, however, ensure you don't operate on the same file that is generating the callback.

More generally, within the server each thread has an implicit top level "connection" context that is single threaded, which we call OWNER. If you want to do something like have a pool of worker threads, you'll need to manage this using ctSetOWNER (faircom.com) or ctdbGetCtreeOWNER()/ctdbSetCtreeOWNER(). Only a single thread should be within a c-tree API call with a particular OWNER at the same time.

The RUCBCTL structure has the following definition:

typedef struct rucbctl {

COUNT version; /* version of this structure */

COUNT opcode; /* operation code */

LONG bufsiz; /* buffer size */

pVOID bufptr; /* input/output buffer */

#ifndef ct8P

LONG pad;

#endif

} RUCBCTL, *pRUCBCTL;

The RUCBACB structure, used when adding a callback definition, has the following definition:

/* Input buffer format for RUCBCTLaddcallback opcode. */

typedef struct rucbacb {

COUNT version; /* Version of this structure */

FILNO datno; /* Data file number */

LONG calltm; /* Time of function call */

LONG nfncnames; /* Number of function names */

LONG pad; /* Padding to ensure 8-byte alignment of next field */

pTEXT datnam; /* Data file name */

pTEXT cbname; /* Unique name to identify this callback */

pTEXT dllname; /* Name of callback DLL */

pTEXT params; /* Optional parameters */

ppTEXT fncnames; /* Callback function names */

#ifndef ct8P

LONG pad2[5];

#endif

} RUCBACB, *pRUCBACB;

The calltm member defines "when" a callback is executed. There are currently four possible values:

  • RUCBonrecupd - Synchronous call during record update.
  • RUCBontrancmt - Synchronous call during transaction commit.
  • RUCBqueuethrd - Asynchronous queue update to a background thread that will call the callback after the transaction commits.
  • RUCBqueueapp - Asynchronous queue update to the transaction log or memory queue. The application is responsible for reading the log/queue.

The RUCBDCB structure, used when deleting a callback definition, has the following definition:

/* Input buffer format for RUCBCTLdelcallback opcode. */

typedef struct rucbdcb {

COUNT version; /* Version of this structure */

FILNO datno; /* Data file number */

LONG pad; /* Padding for 8-byte pointer alignment */

pTEXT datnam; /* Data file name */

pTEXT cbname; /* Unique name to identify this callback */

#ifndef ct8P

LONG pad2[2];

#endif

} RUCBDCB, *pRUCBDCB;

Option to Update All Existing Records

The default when adding a callback is to not call it for existing records. An option can be specified to choose a new behavior of calling the callback for all existing records. This option, RUCBcallexist, is specified by OR'ing it into the calltm field of the RUCBACB structure when calling ctRecordUpdateCallbackControl().

Additional Callback for pre-RUC Updates

V13 and later supports an additional callback that is called at the start of record add and update operations and allows the callback to update the record image. By comparison, the record update callbacks that existed prior to this revision are called later in the add and update routines and do not support the callback changing the record image.

To use this feature, when calling ctRecordUpdateCallbackControl() to add a record update callback function, set the calltm field of the RUCBACB structure to RUCBprerecupd.

Recall that the prototype for the user-defined record update callback function is as follows:

NINT rucbRecordUpdateCallbackFunction(pRUCBF prucbf, pRUCBO prucbo, pRUCBSTT prucbstt);

Additional fields were added to the RUCBSTT structure. When the structure version (indicated by the verson field value) is RUCBSTT_VERSION_V02, the RUCBSTT structure includes fields that hold the record length, record image pointer, and function pointers to the server's memory allocation and free functions:

/* State information to pass to record update callback function: */

typedef struct rucbstt_t {

LONG verson; /* structure version */

VRLEN reclen; /* record length in bytes */

LONG8 tranno; /* transaction number for the operation */

pTEXT recbuf; /* buffer containing record image */

rucbAllocFunction_t allocfn; /* memory allocation function */

rucbFreeFunction_t freefn; /* memory free function */

} RUCBSTT, *pRUCBSTT;

To update the record image in your user-defined record update callback function, if you are increasing the record length, use the memory allocation function to reallocate the record buffer and set prucbstt->recbuf to the new buffer and prucbstt->reclen to the new record length. If you are not increasing the record length, you can modify the record image in the existing record buffer.

Note that for a fixed length data file or a ctAugmentedFxd variable length data file, the record update callback function is not allowed to change the record length. Attempting to do so causes the add or update operation to fail with error DSIZ_ERR (443).

Also, record update callbacks now receive the partition host file name and file number. The record update callback functions were receiving the file name and file number of the partition member. We have changed the behavior so that the host name and file number are passed if available. In our testing, we found that the host info might not be available when closing a partition member because the host might already be closed.

Return Values

Value

Symbolic Constant

Explanation

0

NO_ERROR

Successful operation.

See c-tree Plus Error Codes for a complete listing of valid c-tree Plus error values.

Example

Follow these steps to use ctRecordUpdateCallbackControl() to add a callback function:

  1. Declare RUCBCTL and RUCBACB structures and a list of callback names:

    RUCBCTL rucbctl;

    RUCBACB rucbacb;

    pTEXT myCallbackFunctionNames[NbrRUCBFnc] = {

    "rucbOpenFileCallback",

    "rucbCloseFileCallback",

    "rucbRecordUpdateCallback"

    };

    NINT rc;

  2. Initialize the RUCBCTL structure to indicate that this is an add callback operation:

    memset(&rucbctl,0,sizeof(rucbctl));

    rucbctl.verson = RUCBCTL_VERS_V01;

    rucbctl.opcode = RUCBCTLaddcallback;

    rucbctl.bufsiz = sizeof(rucbacb);

    rucbctl.bufptr = &rucbacb;

  3. Initialize the RUCBACB structure with your callback settings:

    memset(&rucbacb,0,sizeof(rucbacb));

    rucbacb.verson = RUCBACB_VERS_V01;

    rucbacb.opcode = RUCBCTLaddcallback;

    /* datno of -1 instructs ctRecordUpdateCallbackControl() to open the file;

    ** otherwise it is the file number of an already-open data file. */

    rucbacb.datno = -1;

    rucbacb.datnam = "mydatafile.dat";

    rucbacb.dllname = "ctuser.dll";

    rucbacb.params = "my parameters";

    rucbacb.fncnames = myCallbackFunctionNames;

    rucbacb.nfncnames = NbrRUCBFnc;

    rucbacb.cbname = "My First Callback";

    /* This record update callback will be called on record update. */

    rucbacb.calltm = RUCBonrecupd;

  4. Call ctRecordUpdateCallbackControl() and check the result:

    if ((rc = ctRecordUpdateCallbackControl(&rucbctl)) != NO_ERROR)

    printf("Error: Failed to add record update callback function '%s': %d (%d)\n",

    rucbacb.cbname,rc,sysiocod);

    else

    printf("Successfully added record update callback function '%s'.\n",

    rucbacb.cbname);

To use ctRecordUpdateCallbackControl() to delete a callback function:

  1. Declare RUCBCTL and istructures:

    RUCBCTL rucbctl;

    RUCBDCB rucbdcb;

    NINT rc;

  2. Initialize the RUCBCTL structure to indicate that this is a delete callback operation:

    memset(&rucbctl,0,sizeof(rucbctl));

    rucbctl.verson = RUCBCTL_VERS_V01;

    rucbctl.opcode = RUCBCTLdelcallback;

    rucbctl.bufsiz = sizeof(rucbdcb);

    rucbctl.bufptr = &rucbdcb;

  3. Initialize the RUCBDCB structure with your callback settings:

    memset(&rucbdcb,0,sizeof(rucbdcb));

    rucbdcb.verson = RUCBDCB_VERS_V01;

    /* datno of -1 instructs ctRecordUpdateCallbackControl() to open the file;

    ** otherwise it is the file number of an already-open data file. */

    rucbdcb.datno = -1;

    rucbdcb.datnam = "mydatafile.dat";

    rucbdcb.cbname = "My First Callback";

  4. Call ctRecordUpdateCallbackControl() and check the result:

    if ((rc = ctRecordUpdateCallbackControl(&rucbctl)) != NO_ERROR)

    printf("Error: Failed to delete record update callback function '%s': %d (%d)\n",

    rucbdcb.cbname,rc,sysiocod);

    else

    printf("Successfully deleted record update callback function '%s'.\n",

    rucbdcb.cbname);

TOCIndex