Working with Callbacks
FairCom DB API represents a high-level, easy to use API on top of the popular FairCom DB ISAM and FairCom Low-Level APIs. FairCom DB API is intended as the new standard for FairCom DB programming. When compared to ISAM and low-level APIs, FairCom DB API introduced a more formal definition for the structure of data and index files, new concepts such as ROWID and NULL field support, and more specifically a formal definition for each field type supported.
Existing applications’ data and index files, i.e. data created prior to the publication of the FairCom DB API API, may have implemented certain field types in ways that may be incompatible with FairCom DB API. Field types such as CT_DATE, CT_TIME and CT_TIMES are probably the most common examples of existing data that may be incompatible with FairCom DB API.
The FairCom DB API Callback feature was implemented to provide developers a means to intercept certain FairCom DB API operations and add custom code to manipulate record buffer layouts or change field data on the fly such that the record and field data are compatible with FairCom DB API.
To enable a given callback, perform the following:
- Write your callback functions. Every callback function must have the callback function type. There is no need to implement every single callback type. Implement only the callback functions that are necessary for your logic.
- Set your callbacks with the ctdbSetCallback() function. For example if you have implemented CTDB_ON_LOGON and CTDB_ON_LOGOUT callback functions, you call the ctdbSetCallback() function once for each callback to the session handle with the appropriate callback type.
You remove a callback from a FairCom DB API handle by calling the ctdbClearCallback() function. After removed, a callback for a particular handle will not be called again. You can remove all callbacks from a particular session, database, table or record handle by calling the ctdbClearAllCallback() function.
Callback Function Type
Every FairCom DB API callback function must be a function that returns CTDBRET and takes a CTHANDLE parameter. The following typedef declares the function callback type:
typedef CTDBRET (ctdbDECL* ctdbCallbackFunc)(CTHANDLE Handle);When implementing callbacks you need to create your callback functions with the same type of ctdbCallbackFunc. Below is an example of a FairCom DB API session logon callback.
FairCom DB API C API Example
CTDBRET ctdbDECL OnSessionLogon(CTHANDLE Handle)
{
pCTDBSESSION pSession = (pCTDBSESSION)Handle;
printf("Server: %s\n", pSession->server_name);
printf("User: %s\n", pSession->user_name);
printf("Password: %s\n", pSession->user_password);
}Please refer to the Callback Types section below for more information on each available callback type.
Callback Return Codes
Every callback function must return a CTDBRET value. If no errors are detected the callback function must return CTDBRET_OK. If a callback is not yet implemented it should return CTDBRET_NOTIMPLEMENTED. Please refer to the ctdbsdk.h header file for valid values for CTDBRET.
Callback Handle Parameters
The type of handle that is passed to a callback function depends of the type of the callback.
Sessions
For these types the handle parameter is a session handle. You can safely typecast the handle parameter to a pCTDBSESSION structure pointer. Please refer to the ctdbsdk.h header file for the declaration of CTDBSESSION structure.
- CTDB_ON_SESSION_LOGON
- CTDB_ON_SESSION_LOGOUT
Databases
For these types the handle parameter is a database handle. You can safely typecast the handle parameter to a pCTDBDATABASE structure pointer. Please refer to the ctdbsdk.h header file for the declaration of CTDBDATABASE structure.
- CTDB_DATABASE_CONNECT
- CTDB_DATABASE_DISCONNECT
Tables
For these types the handle parameter is a table handle. You can safely typecast the handle parameter to a pCTDBTABLE structure pointer. Please refer to the ctdbsdk.h header file for the declaration of CTDBTABLE structure.
- CTDB_ON_TABLE_OPEN
- CTDB_ON_TABLE_CLOSE
- CTDB_ON_TABLE_GET_DODA
- CTDB_ON_TABLE_GET_SCHEMA
- CTDB_ON_TABLE_GET_EXT_INFO
- CTDB_ON_TABLE_GET_RECLEN
- CTDB_ON_TABLE_GET_VTABLE_INFO
- CTDB_ON_TABLE_ALTER
- CTDB_ON_TABLE_REBUILD
Records
For these types the handle parameter is a record handle. You can safely typecast the handle parameter to a pCTDBRECORD structure pointer. Please refer to the ctdbsdk.h header file for the declaration of CTDBRECORD structure.
- CTDB_ON_RECORD_INIT
- CTDB_ON_RECORD_RESET
- CTDB_ON_RECORD_BEFORE_READ
- CTDB_ON_RECORD_AFTER_READ
- CTDB_ON_RECORD_BEFORE_BUILD_KEY
- CTDB_ON_RECORD_AFTER_BUILD_KEY
- CTDB_ON_RECORD_BEFORE_WRITE
- CTDB_ON_RECORD_AFTER_WRITE
Callback Types
When a callback is registered with a FairCom DB API handle, you need to specify which callback is being intercepted. The sections below describe each of the callback types that can be intercepted.
CTDB_ON_SESSION_LOGON
A CTDB_ON_SESSION_LOGON callback is invoked after a successful session logon but before the ctdbLogon() function returns. The handle passed as a parameter with this callback is a session handle. You can safely typecast the handle parameter to a pCTDBSESSION structure pointer.
The session logon callback can be used as an indication that the session is now active and that database and table processing may occur. This callback is a good place to set session-wide parameters that may be active until a session logout is performed.
FairCom DB API C API Example
CTDBRET ctdbDECL OnSessionLogon(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBSESSION pSession = (pCTDBSESSION)Handle;
if (!pSession)
Retval = CTDBRET_NOTSESSION;
else
{
/* allocate 100 bytes of memory and keep it in the session local tag */
pSession->localTag = pSession->OnAlloc(100);
if (!pSession->localTag)
Retval = CTDBRET_NOMEMORY;
}
return Retval;
}CTDB_ON_ SESSION_LOGOUT
A CTDB_ON_SESSION_LOGOUT callback is invoked before a session logout is performed. The handle passed as a parameter with this callback is a session handle and you can safely typecast the handle parameter as a pCTDBSESSION structure pointer.
The session logout callback can be used to undo and cleanup operations performed by a session logon callback. No other callbacks calls will be issued after a CTDB_ON_SESSION_LOGOUT callback.
FairCom DB API C API Example
CTDBRET ctdbDECL OnSessionLogout(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBSESSION pSession = (pCTDBSESSION)Handle;
if (!pSession)
Retval = CTDBRET_NOTSESSION;
else
{
/* release any memory allocated by session logon callback */
if (pSession->localTag)
{
pSession->OnFree(pSession->localTag)
pSession->localTag = NULL;
}
}
return Retval;
}CTDB_ON_DATABASE_CONNECT
A CTDB_ON_DATABASE_CONNECT callback is invoked after a successful database connect but before the ctdbConnect() function returns. The handle passed as a parameter with this callback is a database handle and you can safely typecast the handle parameter to a pCTDBDATABASE structure pointer.
The database connect callback can be used as an indication that the database connection is now active and that table processing may occur. This callback is a good place to set database-wide parameters that may be active until a database disconnect is performed.
Below is an example demonstrating the database connect callback to set table open and table close callbacks.
FairCom DB API C API Example
CTDBRET ctdbDECL OnDatabaseConnect(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBDATABASE pDatabase = (pCTDBDATABASE)Handle;
if (!pDatabase)
Retval = CTDBRET_NOTDATABASE;
else
{
Retval = ctdbSetCallback(Handle, CTDB_ON_TABLE_OPEN, onTableOpen);
if (Retval == CTDBRET_OK)
Retval = ctdbSetCallback(Handle, CTDB_ON_TABLE_CLOSE, onTableClose);
}
return Retval;
}CTDB_ON_DATABASE_DISCONNECT
A CTDB_ON_DATABASE_DISCONNECT callback is invoked just before a database disconnect is performed. The handle passed as a parameter with this callback is a database handle and you can safely typecast the handle parameter to a pCTDBDATABASE structure pointer.
The database disconnect callback may be used to undo and cleanup any operations performed by a database connect callback.
Below is an example demonstrating how to clear the callbacks established by the database connect callback.
FairCom DB API C API Example
CTDBRET ctdbDECL OnDatabaseDisconnect(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBDATABASE pDatabase = (pCTDBDATABASE)Handle;
if (!pDatabase)
Retval = CTDBRET_NOTDATABASE;
else
{
Retval = ctdbClearCallback(Handle, CTDB_ON_TABLE_OPEN);
if (Retval == CTDBRET_OK)
Retval = ctdbClearCallback(Handle, CTDB_ON_TABLE_CLOSE);
}
return Retval;
}CTDB_ON_TABLE_OPEN
A CTDB_ON_TABLE_OPEN callback is invoked after the FairCom DB data and index files are open, but before ctdbOpenTable() returns. The handle passed as a parameter with this callback is a table handle and you can safely typecast the handle parameter to a pCTDBTABLE structure pointer.
The open table callback can be used as an indication that the table is active and that table and records operations may occur.
Below is an example demonstrating how to obtain the record callbacks when the table open callback is called.
FairCom DB API C API Example
typedef struct
{
pCTDBSESSION pSession;
pCTDBTABLE pTable;
pDATOBJ dodaptr;
VRLEN doda_size;
VRLEN dodacount;
NINT fixlen;
} LOCALTABLE, ctMEM* pLOCALTABLE;
CTDBRET ctdbDECL OnTableOpen(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBTABLE pTable = (pCTDBTABLE)Handle;
pCTDBSESSION pSession;
pLOCALTABLE pLocal = NULL;
/* check the table handle */
if (!pTable)
{
Retval = CTDBRET_NOTTABLE;
goto Exit;
}
pSession = (pCTDBSESSION)pTable->pSession;
/* check the table name for our table */
if (strcmp(pTable->name, "table") != 0)
{
/* this is not the table we are looking for */
goto Exit;
}
/* allocate the local data for the table handle */
pLocal = (pLOCALTABLE)pSession->onAlloc(sizeof(LOCALTABLE));
if (!pLocal)
{
Retval = CTDBRET_NOMEMORY;
goto Exit;
}
/* initialize the local table data */
pLocal->pSession = pSession;
pLocal->pTable = pTable;
pLocal->dodaptr = NULL;
pLocal->doda_size = 0;
pLocal->dodacount = 0;
pTable->localTag = (pVOID)pLocal;
pLocal = NULL;
/* set the other callbacks for this table */
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_TABLE_OPEN, OnTableOpen)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_TABLE_GET_DODA, OnTableGetDoda)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_TABLE_GET_RECLEN, OnTableGetReclen)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_TABLE_CLOSE, OnTableClose)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_RECORD_INIT, OnRecordInit)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_RECORD_RESET, OnRecordReset)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_RECORD_BEFORE_READ, OnRecordBeforeRead)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_RECORD_AFTER_READ, OnRecordAfterRead)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_RECORD_BEFORE_BUILD_KEY, OnRecordBeforeBuildKey)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_RECORD_AFTER_BUILD_KEY, OnRecordAfterBuildKey)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_RECORD_BEFORE_WRITE, OnRecordBeforeWrite)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(Handle, CTDB_ON_RECORD_AFTER_WRITE, OnRecordAfterWrite)) != CTDBRET_OK) goto Exit;
/* set the callback for any records already allocated */
if (pTable->records)
{
NINT i;
for (i = 0; i < pTable->records->count; i++)
{
CTHANDLE recHandle = (CTHANDLE)_ctdbListItem(pTable->records, i);
if (recHandle)
{
if ((Retval = ctdbSetCallback(recHandle, CTDB_ON_RECORD_INIT, OnRecordInit)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(recHandle, CTDB_ON_RECORD_RESET, OnRecordReset)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(recHandle, CTDB_ON_RECORD_BEFORE_READ, OnRecordBeforeRead)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(recHandle, CTDB_ON_RECORD_AFTER_READ, OnRecordAfterRead)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(recHandle, CTDB_ON_RECORD_BEFORE_BUILD_KEY, OnRecordBeforeBuildKey)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(recHandle, CTDB_ON_RECORD_AFTER_BUILD_KEY, OnRecordAfterBuildKey)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(recHandle, CTDB_ON_RECORD_BEFORE_WRITE, OnRecordBeforeWrite)) != CTDBRET_OK) goto Exit;
if ((Retval = ctdbSetCallback(recHandle, CTDB_ON_RECORD_AFTER_WRITE, OnRecordAfterWrite)) != CTDBRET_OK) goto Exit;
}
}
}
Exit:
if (pLocal)
if (pSession)
pSession->onFree(pLocal);
return Retval;
}CTDB_ON_TABLE_CLOSE
A CTDB_ON_TABLE_CLOSE callback is invoked before the FairCom DB data and index files are closed, but before the ctdbCloseTable() function returns. The handle passed as a parameter with this callback is a table handle and you can safely typecast the handle parameter to a pCTDBTABLE structure pointer. The close table callback may be used to undo and cleanup operations performed by the table open callback.
FairCom DB API C API Example
CTDBRET ctdbDECL OnTableClose(CTHANDLE Handle)
{
pCTDBTABLE pTable = (pCTDBTABLE)Handle;
if (pTable && pTable->localTag)
{
pCTDBSESSION pSession = (pCTDBSESSION)pTable->pSession;
pLOCALTABLE pLocal = (pLOCALTABLE)pTable->localTag;
if (pLocal)
{
if (pLocal->dodaptr)
pSession->onFree(pLocal->dodaptr);
pSession->onFree(pTable->localTag);
}
pTable->localTag = NULL;
}
return CTDBRET_OK;
}CTDB_ON_TABLE_GET_SCHEMA
A CTDB_ON_TABLE_GET_SCHEMA callback is invoked after the FairCom DB data and index files are open and the CTDB_ON_TABLE_OPEN callback is invoked. This callback is also invoked after the FairCom DB data file record schema object is loaded into the table handle.
You may use the CTDB_ON_TABLE_GET_SCHEMA callback event to modify FairCom DB schema information stored in the table handle. You should typecast the table handle to a pCTDBTABLE structure pointer. The following CTDBTABLE structure members keep schema information:
| CTDBTABLE Structure Member | Explanation |
|---|---|
| pConvMap schemaptr | c-tree ConvMap structure pointer |
| VRLEN schema_size | size in bytes of allocated ConvMap structure |
At the point a CTDB_ON_TABLE_GET_SCHEMA callback is invoked, the table’s schema has already been loaded and stored in the schemaptr member of CTDBTABLE structure. The schema_size member contains the allocated size of schemaptr.
FairCom DB API C API Example
CTDBRET ctdbDECL OnTableGetSchema(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBTABLE pTable = (pCTDBTABLE)Handle;
if (!pTable)
Retval = CTDBRET_NOTTABLE;
else
{
/* change the field padding byte */
if (pTable->schemaptr)
pTable->schemaptr->padding = ' ';
}
return Retval;
}CTDB_ON_TABLE_GET_DODA
A CTDB_ON_TABLE_GET_DODA callback is invoked after FairCom DB data and index files are open and the CTDB_ON_TABLE_OPEN and CTDB_ON_TABLE_GET_SCHEMA callbacks have been invoked. This callback is also invoked after the FairCom DB data file record schema and DODA object are loaded into the table handle.
You can use the CTDB_ON_TABLE_GET_DODA callback event to modify the FairCom DB DODA stored in the table handle. You should typecast the table handle to a pCTDBTABLE structure pointer. The following CTDBTABLE structure members keep DODA information:
| CTDBTABLE Structure Member | Explanation |
|---|---|
| pDATOBJ dodaptr | pointer to c-tree DODA |
| VRLEN doda_size | size in bytes of allocated DODA |
| VRLEN dodacount | number of fields in DODA |
By the time a CTDB_ON_TABLE_GET_DODA callback is invoked, the table’s DODA has already been loaded and stored in the dodaptr member of CTDBTABLE structure. The doda_size member contains the allocated size of dodaptr and dodacount keep count of the number of fields described by dodaptr.
The example below shows how to use the CTDB_ON_TABLE_GET_DODA callback to modify the DODA information.
FairCom DB API C API Example
static DATOBJ newdoda[4] =
{
{"id", (pTEXT)0, CT_INT4U, 4},
{"who", (pTEXT)4, CT_INT2, 2},
{"when",(pTEXT)8, CT_TIMES, 8},
{"text",(pTEXT)16, CT_STRING, 0}
};
CTDBRET ctdbDECL OnTableGetDoda(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBTABLE pTable = (pCTDBTABLE)Handle;
NINT i;
/* check the table handle */
if (!pTable)
{
Retval = CTDBRET_NOTTABLE;
goto Exit;
}
/* fix the DODA */
for (i = 0; i < pLocal->dodacount; i++)
{
pTable->dodaptr[i].fadr = newdoda[i].fadr;
pTable->dodaptr[i].flen = newdoda[i].flen;
pTable->dodaptr[i].ftype = newdoda[i].ftype;
}
/* fix the IFIL index segment */
if (pTable->ifilptr && pTable->ifilptr->ix && pTable->ifilptr->ix[0].seg)
{
pTable->ifilptr->ix[0].seg->soffset = 0;
pTable->ifilptr->ix[0].seg->slength = 4;
pTable->ifilptr->ix[0].seg->segmode = CTSEG_SCHSEG;
}
Exit:
return Retval;
}CTDB_ON_TABLE_GET_RECLEN
A CTDB_ON_TABLE_GET_RECLEN callback is invoked after FairCom DB data and index files are open and the T and CTDB_ON_TABLE_GET_SCHEMA and CTDB_ON_TABLE_GET_DODA callbacks have been invoked. This callback is also invoked after the FairCom DB data file record schema and DODA object are loaded into the table handle.
You can use the CTDB_ON_TABLE_GET_RECLEN callback event to control the length, in bytes, of the fixed portion of the record buffer. The handle passed to this callback is always a table handle and you can safely typecast the table handle to a pCTDBTABLE structure pointer.
The following CTDBTABLE structure members keep the length of the fixed portion of the record buffer:
| CTDBTABLE Structure Member | Explanation |
|---|---|
| VRLEN fixreclen | length of fixed portion of record buffer |
By the time CTDB_ON_TABLE_GET_RECLEN callback is invoked, the fixreclen member of CTDBTABLE structure has already been calculated. You can use this event to modify the length of the fixed portion of the record buffer.
FairCom DB API C API Example
CTDBRET ctdbDECL OnTableGetReclen(CTHANDLE Handle)
{
pCTDBTABLE pTable = (pCTDBTABLE)Handle;
if (pTable)
pTable->fixreclen = 16;
return CTDBRET_OK;
}CTDB_ON_TABLE_GET_EXT_INFO
FairCom DB API maintains extended field information that is not present in the DODA object, but is necessary for optimal FairCom DB SQL operation. Each DODA entry describes the field name, the field offset in the record buffer, the field type and length, but for optimal SQL operation we also need additional information such as the field precision, scale and if the field allows NULL values.
A field precision is the total number of digits necessary to represent the value, while the field scale is the number of digits to the left of the decimal point. For example CT_NUMBER fields have a precision of 32 and scale from 0 to 32. CT_INT4 fields have precision of 10 and scale of 0. A NULL value for a field indicates that the field has no value (i.e., no value was assigned to the field when the record was written to the table.)
A CTDB_ON_TABLE_GET_EXT_INFO callback is invoked after a FairCom DB data and index files are open and the CTDB_ON_TABLE_OPEN and CTDB_ON_TABLE_GET_SCHEMA, CTDB_ON_TABLE_GET_DODA and CTDB_ON_TABLE_GET_RECLEN callbacks have been invoked. This callback is also invoked after the FairCom DB API open table code has tried to load the extended field information resource.
You can use the CTDB_ON_TABLE_GET_EXT_INFO callback event to add new, or modify existing, extended field information kept by the table handle. See the example below to see how to modify the extended field information. The handle passed to this callback is always a table handle and you can safely typecast the table handle to a pCTDBTABLE structure pointer.
FairCom DB API C API Example
struct extinfo_tag
{
CTBOOL nonull; /* YES = no NULL values accepted */
COUNT fprec; /* field precision */
COUNT fscale; /* field scale */
} extinfo [] =
{
{YES, 10, 0}, /* "id", 0, CT_INT4, 4 */
{NO, 5, 0}, /* "who", 4, CT_INT2, 2 */
{NO, 8, 0}, /* "when", 8, CT_TIMES, 8 */
{NO, 0, 0} /* "text", 16, CT_STRING, 0 */
};
CTDBRET ctdbDECL OnTableGetExtInfo(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBTABLE pTable = (pCTDBTABLE)Handle;
if (!pTable)
Retval = CTDBRET_NOTTABLE;
else
{
NINT count = (pTable->fields) ? pTable->fields->count : 0;
NINT i;
for (i = 0; i < count; i++)
{
pCTDBFIELD pField = (pCTDBFIELD)pTable->fields->list[i];
if (pField)
{
pField->nonull = extinfo[i].nonull;
pField->fprec = extinfo[i].fprec;
pField->fscale = extinfo[i].fscale;
}
}
}
return Retval;
}CTDB_ON_TABLE_GET_VTABLE_INFO
CTDB_ON_TABLE_GET_VTABLE_INFO is called when FairCom DB API looks for the virtual table resource, typically at the time of a virtual table open or when calling ctdbGetVTableInfoFromTable(). This callback should be implemented when using alternative (to the resource as defined) methods of tracking virtual table definitions. (For example, FairCom RTG COBOL support implements this callback.)
CTDB_ON_TABLE_ALTER
A CTDB_ON_TABLE_ALTER callback is invoked at the beginning of the ctdbAlterTable function call, before any operations are performed on the table. This callback should be used to indicate if alter table operations are to be performed on a particular table.
If the alter table operation is allowed, then the callback function must returns CTDBRET_OK value. On the other hand, if alter table operations are not to be allowed, the callback function must return an error code. In case of error, the suggested error code is CTDBRET_NOTSUPPORTED.
The handle passed as a parameter with this callback is a table handle and you can safely typecast the handle parameter to a pCTDBTABLE structure pointer.
Below demonstrates an AlterTable() not allowed if table name is "mytable".
FairCom DB API C API Example
CTDBRET ctdbDECL OnTableAlter(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBTABLE pTable = (pCTDBTABLE)Handle;
if (pTable && pTable->name && strcmp(pTable->name, "mytable") == 0)
{
/* if table is 'mytable' alter table not allowed */
Retval = CTDBRET_NOTSUPPORTED;
}
return Retval;
}CTDB_ON_TABLE_REBUILD
The FairCom DB API alter table function was designed and implemented to allow the modification of table properties after it was created and possibly already populated with data.
Depending on which properties are modified, a full table rebuild may be necessary. A full table rebuild is accomplished by creating a temporary table with the correct properties, considering all changes made to the table, and then moving all of the records from the original table to the new temporary table. Once all of the data records have been moved, the original table is deleted and the temporary table renamed with the name of the original table.
During the full table rebuild phase of moving records, a CTDB_ON_TABLE_REBUILD callback is invoked for every percentage point of progress. The progress percentage is calculated by dividing the number of records moved by the total number of records in a table. The percentage value is stored in the table handle and the callback function can access this value to display, for example, a progress report during lengthy alter table operations.
If a CTDB_ON_TABLE_REBUILD callback returns a value other than CTDBRET_OK, the rebuild process will be aborted, and the alter table function will return the value.
The handle passed as a parameter with this callback is a table handle and you can safely typecast the handle parameter to a pCTDBTABLE structure pointer. You can access the percentage counter by accessing the following CTDBTABLE structure member:
| CTDBTABLE Structure Member | Explanation |
|---|---|
| NINT rebuild_perc | percentage of table rebuild with values from 0 to 100 |
Before any records are moved, the CTDB_ON_TABLE_REBUILD callback is called with rebuild_perc set to zero to indicate that the operation is about to start. After all records are copied, CTDB_ON_TABLE_REBUILD is called again, this time with rebuild_perc set to 100 to indicate the end of rebuild.
Since CTDB_ON_TABLE_REBUILD callback is called inside the alter table’s record moving loop, care must be taken with the implementation of the callback since it may adversely affect the performance of the alter table operation.
You can also call the ctdbGetRebuildProgress() function to retrieve the table rebuild percentage counter.
FairCom DB API C API Example
CTDBRET ctdbDECL OnTableRebuild(CTHANDLE Handle)
{
pCTDBTABLE pTable = (pCTDBTABLE)Handle;
/* display a dot for every 5% rebuild completed */
if (pTable)
{
if (pTable->rebuild_perc > 0 && (pTable->rebuild_perc % 5) == 0)
printf(".");
}
return CTDBRET_OK;
}CTDB_ON_RECORD_INIT
A CTDB_ON_RECORD_INIT callback is invoked when the record is initialized because it is being used for the first time or after an alter table. The handle passed as a parameter with this callback is a record handle and you can safely typecast the handle parameter to a pCTDBRECORD structure pointer.
The record init callback can be used as an indication that the record handle is becoming active and records operations may occur from this point.
The following CTDBRECORD structure members keep information regarding the record buffer kept by the record handle:
| CTDBRECORD Structure Member | Explanation |
|---|---|
| pUTEXT recbuf | record buffer |
| VRLEN recbuf_size | size in bytes of memory allocated for record buffer |
| VRLEN recbuf_len | actual length of data in record buffer |
FairCom DB API C API Example
CTDBRET ctdbDECL OnRecordInit(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBRECORD pRecord = (pCTDBRECORD)Handle;
if (!pRecord)
Retval = CTDBRET_NOTRECORD;
else
{
/* allocate a paralell record buffer and store it in the localTag */
pRecord->localTag = pRecord->pSession ? pRecord->pSession->onAlloc(pRecord->recbuf_size) : NULL;
if (!pRecord->localTag)
Retval = CTDBRET_OK;
}
return Retval;
}CTDB_ON_RECORD_RESET
A CTDB_ON_RECORD_RESET callback is invoked when a record buffer is going inactive because the table is being closed. The handle passed as a parameter with this callback is a record handle and you can safely typecast the handle parameter to a pCTDBRECORD structure pointer.
The following CTDBRECORD structure members keep information regarding the record buffer kept by the record handle:
| CTDBRECORD Structure Member | Explanation |
|---|---|
| pUTEXT recbuf | record buffer |
| VRLEN recbuf_size | size in bytes of memory allocated for record buffer |
| VRLEN recbuf_len | actual length of data in record buffer |
FairCom DB API C API Example
CTDBRET ctdbDECL OnRecordReset(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBRECORD pRecord = (pCTDBRECORD)Handle;
if (!pRecord)
Retval = CTDBRET_NOTRECORD;
else
{
/* release memory allocated for the localTag */
if (pRecord->localTag)
pRecord->pSession->onFree(pRecord->localTag);
pRecord->localTag = NULL;
}
return Retval;
}CTDB_ON_RECORD_BEFORE_READ
A CTDB_ON_RECORD_BEFORE_READ callback is invoked just before a record is read by one of the FairCom DB record reading functions. This callback is useful to prepare a record buffer just before data is read.
The handle passed as parameter for this callback is a record handle and you can safely typecast the handle parameter to a pCTDBRECORD structure pointer.
The following CTDBRECORD structure members keep information regarding the record buffer kept by the record handle:
| CTDBRECORD Structure Member | Explanation |
|---|---|
| pUTEXT recbuf | record buffer |
| VRLEN recbuf_size | size in bytes of memory allocated for record buffer |
| VRLEN recbuf_len | actual length of data in record buffer |
FairCom DB API C API Example
CTDBRET ctdbDECL OnRecordBeforeRead(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBRECORD pRecord = (pCTDBRECORD)Handle;
if (!pRecord)
Retval = CTDBRET_NOTRECORD;
else
{
/* swap the record buffer with the localTag */
pUTEXT ptr = pRecord->recbuf;
pRecord->recbuf = pRecord->localTag;
pRecord->localTag = ptr;
}
return Retval;
}CTDB_ON_RECORD_AFTER_READ
A CTDB_ON_RECORD_AFTER_READ callback is invoked just after a record is read by one of the FairCom DB record reading functions. This callback is useful to adjust a record buffer just after data is read.
The handle passed as parameter for this callback is a record handle and you can safely typecast the handle parameter to a pCTDBRECORD structure pointer.
The following CTDBRECORD structure members keep information regarding the record buffer kept by the record handle:
| CTDBRECORD Structure Member | Explanation |
|---|---|
| pUTEXT recbuf | record buffer |
| VRLEN recbuf_size | size in bytes of memory allocated for record buffer |
| VRLEN recbuf_len | actual length of data in record buffer |
FairCom DB API C API Example
CTDBRET ctdbDECL OnRecordAfterRead(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBRECORD pRecord = (pCTDBRECORD)Handle;
if (!pRecord)
Retval = CTDBRET_NOTRECORD;
else
{
/* swap the record buffer with the localTag */
pUTEXT recptr = pRecord->recbuf;
int i;
pRecord->recbuf = pRecord->localTag;
pRecord->localTag = (pVOID)recptr;
/* make any CT_DATE field compatible with FairCom DB API */
for (i = 0; (i < pRecord->fields_count && Retval == CTDBRET_OK); i++)
{
if (pRecord->fields[i].ftype == CT_DATE)
{
time_t* t;
struct tm* ptr;
CTDATE date;
/* get the non standard date into t */
t = (time_t*)&recptr[pRecord->fields[i].offset];
/* convert a C time_t date into CT_DATE */
if ((ptr = gmtime(t)) != NULL)
{
if ((Retval = ctdbDatePack(&date, (ptr->tm_year + 1900), (ptr->tm_mon + 1), ptr->tm_mday)) == CTDBRET_OK
{
/* put converted CTDB date back into record buffer */
memcpy(&pRecord->recptr[pRecord->fields[i].offset], &date, sizeof(CTDATE));
}
}
else
Retval = CTDBRET_INVDATE;
}
}
}
return Retval;
}CTDB_ON_RECORD_BEFORE_BUILD_KEY
A CTDB_ON_RECORD_BEFORE_BUILD_KEY callback is invoked just before a call to FairCom DB BuildKey() function. The FairCom DB BuildKey() function perform key segment translation from record buffer to target key. This callback is useful to prepare a record buffer just before an index key is built.
The handle passed as parameter for this callback is a record handle and you can safely typecast the handle parameter to a pCTDBRECORD structure pointer.
The following CTDBRECORD structure members keep information regarding the record buffer kept by the record handle:
| CTDBRECORD Structure Member | Explanation |
|---|---|
| pUTEXT recbuf | record buffer |
| VRLEN recbuf_size | size in bytes of memory allocated for record buffer |
| VRLEN recbuf_len | actual length of data in record buffer |
FairCom DB API C API Example
CTDBRET ctdbDECL OnRecordBeforeBuildKey(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBRECORD pRecord = (pCTDBRECORD)Handle;
if (!pRecord)
Retval = CTDBRET_NOTRECORD;
else
{
/* change any CT_DATE dates to C time_t dates */
pUTEXT recptr = pRecord->recbuf;
int i;
for (i = 0; (i < pRecord->fields_count && Retval == CTDBRET_OK); i++)
{
if (pRecord->fields[i].ftype == CT_DATE)
{
struct tm m;
CTDATE* dateptr;
/* get the CTDB date */
dateptr = (CTDATE*)&recptr[pRecord->fields[i].offset];
/* convert a CT_DATE date to a C time_t date */
if ((Retval = ctdbDateUnpack(*dateptr, &m.tm_year, &m.tm_mon, &m.tm_mday)) == CTDBRET_OK)
{
time_t* tptr = (time_t*)&recptr[pRecord->fields[i].offset];
m.tm_year -= 1900;
m.tm_mon--;
m.tm_sec = 0;
m.tm_min = 0;
m.tm_hour = 0;
m.tm_isdst = 0;
*t = mktime(&m);
}
}
}
}
return Retval;
}CTDB_ON_RECORD_AFTER_BUILD_KEY
A CTDB_ON_RECORD_AFTER_BUILD_KEY callback is invoked just after a call to the FairCom DB BuildKey() function. The FairCom DB BuildKey() function performs key segment translation from record buffer to target key. This callback is useful to adjust a record buffer just after a target key was built.
The handle passed as a parameter with this callback is a record handle and you can safely typecast the handle parameter to a pCTDBRECORD structure pointer.
The following CTDBRECORD structure members keep information regarding the record buffer kept by the record handle:
| CTDBRECORD Structure Member | Explanation |
|---|---|
| pUTEXT recbuf | record buffer |
| VRLEN recbuf_size | size in bytes of memory allocated for record buffer |
| VRLEN recbuf_len | actual length of data in record buffer |
FairCom DB API C API Example
CTDBRET ctdbDECL OnRecordAfterBuildKey(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBRECORD pRecord = (pCTDBRECORD)Handle;
if (!pRecord)
Retval = CTDBRET_NOTRECORD;
else
{
/* change any CT_DATE dates to C time_t dates */
pUTEXT recptr = pRecord->recbuf;
int i;
for (i = 0; (i < pRecord->fields_count && Retval == CTDBRET_OK); i++)
{
if (pRecord->fields[i].ftype == CT_DATE)
{
struct tm m;
CTDATE* dateptr;
/* get the CTDB date */
dateptr = (CTDATE*)&recptr[pRecord->fields[i].offset];
/* convert a CT_DATE date to a C time_t date */
if ((Retval = ctdbDateUnpack(*dateptr, &m.tm_year, &m.tm_mon, &m.tm_mday)) == CTDBRET_OK)
{
time_t* tptr = (time_t*)&recptr[pRecord->fields[i].offset];
m.tm_year -= 1900;
m.tm_mon--;
m.tm_sec = 0;
m.tm_min = 0;
m.tm_hour = 0;
m.tm_isdst = 0;
*t = mktime(&m);
}
}
}
}
return Retval;
}CTDB_ON_RECORD_BEFORE_WRITE
A CTDB_ON_RECORD_BEFORE_WRITE callback is invoked just before a record is written with one of the FairCom DB record writing or updating functions. This callback is useful to prepare a record buffer just before data is written to disk.
The handle passed as a parameter with this callback is a record handle and you can safely typecast the handle parameter to a pCTDBRECORD structure pointer.
The following CTDBRECORD structure members keep information regarding the record buffer kept by the record handle:
| CTDBRECORD Structure Member | Explanation |
|---|---|
| pUTEXT recbuf | record buffer |
| VRLEN recbuf_size | size in bytes of memory allocated for record buffer |
| VRLEN recbuf_len | actual length of data in record buffer |
FairCom DB API C API Example
CTDBRET ctdbDECL OnRecordBeforeWrite(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBRECORD pRecord = (pCTDBRECORD)Handle;
if (!pRecord)
Retval = CTDBRET_NOTRECORD;
else
{
/* change any CT_DATE dates to C time_t dates */
pUTEXT recptr = pRecord->recbuf;
int i;
for (i = 0; (i < pRecord->fields_count && Retval == CTDBRET_OK); i++)
{
if (pRecord->fields[i].ftype == CT_DATE)
{
struct tm m;
CTDATE* dateptr;
/* get the CTDB date */
dateptr = (CTDATE*)&recptr[pRecord->fields[i].offset];
/* convert a CT_DATE date to a C time_t date */
if ((Retval = ctdbDateUnpack(*dateptr, &m.tm_year, &m.tm_mon, &m.tm_mday)) == CTDBRET_OK)
{
time_t* tptr = (time_t*)&recptr[pRecord->fields[i].offset];
m.tm_year -= 1900;
m.tm_mon--;
m.tm_sec = 0;
m.tm_min = 0;
m.tm_hour = 0;
m.tm_isdst = 0;
*t = mktime(&m);
}
}
}
}
return Retval;
}CTDB_ON_RECORD_AFTER_WRITE
A CTDB_ON_RECORD_AFTER_WRITE callback is invoked just after a record is written with one of the FairCom DB record writing or updating functions. This callback is useful to adjust a record buffer just after data is written to disk.
The handle passed as a parameter with this callback is a record handle and you can safely typecast the handle parameter to a pCTDBRECORD structure pointer.
The following CTDBRECORD structure members keep information regarding the record buffer kept by the record handle:
| CTDBRECORD Structure Member | Explanation |
|---|---|
| pUTEXT recbuf | record buffer |
| VRLEN recbuf_size | size in bytes of memory allocated for record buffer |
| VRLEN recbuf_len | actual length of data in record buffer |
FairCom DB API C API Example
CTDBRET ctdbDECL OnRecordAfterWrite(CTHANDLE Handle)
{
CTDBRET Retval = CTDBRET_OK;
pCTDBRECORD pRecord = (pCTDBRECORD)Handle;
if (!pRecord)
Retval = CTDBRET_NOTRECORD;
else
{
/* change any CT_DATE dates to C time_t dates */
pUTEXT recptr = pRecord->recbuf;
int i;
for (i = 0; (i < pRecord->fields_count && Retval == CTDBRET_OK); i++)
{
if (pRecord->fields[i].ftype == CT_DATE)
{
struct tm m;
CTDATE* dateptr;
/* get the CTDB date */
dateptr = (CTDATE*)&recptr[pRecord->fields[i].offset];
/* convert a CT_DATE date to a C time_t date */
if ((Retval = ctdbDateUnpack(*dateptr, &m.tm_year, &m.tm_mon, &m.tm_mday)) == CTDBRET_OK)
{
time_t* tptr = (time_t*)&recptr[pRecord->fields[i].offset];
m.tm_year -= 1900;
m.tm_mon--;
m.tm_sec = 0;
m.tm_min = 0;
m.tm_hour = 0;
m.tm_isdst = 0;
*t = mktime(&m);
}
}
}
}
return Retval;
}CTDB_ON_RECORD_MAPTOPARENT
Called only if the the child table has the field mapping resource activated.Overrides the field mapping information,that is, if the callback function pointers are set, then the field mapping information stored in the child table resource is ignored and the callback function is responsible for copying the appropriate fields from the child record buffer to the parent record buffer (and vice-versa).
The ctrouter.exe utility should be used to create a field mapping resource to the child table. Once the field mapping resource is set, and after the table is open by calling ctdbOpenTable() function, the two callback type mentioned above can be set to provide custom field mapping to and from the child table. It is recommended to set both CTDB_ON_RECORD_MAPTOPARENT and CTDB_ON_RECORD_MAPTOCHILD using a table handle, as this will automatically set the callbacks for any records allocated using the table handle. Example:
/* open the child table */
if ((Retval = ctdbOpenTable(hTable, "child", CTOPEN_NORMAL)) == CTDBRET_OK)
{
ctdbSetCallback(hTable, CTDB_ON_RECORD_MAPTOPARENT, MapToParent);
ctdbSetCallback(hTable, CTDB_ON_RECORD_MAPTOCHILD, MapToChild);
}CTDB_ON_RECORD_MAPTOCHILD
Called only if the the child table has the field mapping resource activated. Overrides the field mapping information, that is, if the callback function pointers are set, then the field mapping information stored in the child table resource is ignored and the callback function is responsible for copying the appropriate fields from the child record buffer to the parent record buffer (and vice-versa).
The ctrouter.exe utility should be used to create a field mapping resource to the child table. Once the field mapping resource is set, and after the table is open by calling ctdbOpenTable() function, the two callback type mentioned above can be set to provide custom field mapping to and from the child table. It is recommended to set both CTDB_ON_RECORD_MAPTOPARENT and CTDB_ON_RECORD_MAPTOCHILD using a table handle, as this will automatically set the callbacks for any records allocated using the table handle. Example:
/* open the child table */
if ((Retval = ctdbOpenTable(hTable, "child", CTOPEN_NORMAL)) == CTDBRET_OK)
{
ctdbSetCallback(hTable, CTDB_ON_RECORD_MAPTOPARENT, MapToParent);
ctdbSetCallback(hTable, CTDB_ON_RECORD_MAPTOCHILD, MapToChild);
}Working with Callbacks
You must register your callback functions before they are invoked by FairCom DB API code. The callback function is registered with a call to the ctdbSetCallback() function, passing the appropriate FairCom DB API handle, the callback function type and the address of a function to receive the callback calls.
CTDBRET ctdbSetCallback(CTHANDLE Handle, CTDB_CALLBACK_TYPE CallBackType,
ctdbCallbackFunction CallBackFunc);FairCom DB API C API Example
/* allocate a new session handle */
CTHANDLE hSession = ctdbAllocSession(CTSESSION_CTREE);
/* set table open callback */
if (ctdbSetCallback(hSession, CTDB_ON_TABLE_OPEN, OnTableOpen) != CTDBRET_OK)
printf("ctdbSetCallback failed\n");You can register any of the defined callback functions using the session handle and every time a database, table or record handle is allocated, they will automatically inherit their callbacks from the session handle. Conversely, if you register callbacks with a database handle, every time a table or a record handle is allocated they will automatically inherit their callbacks from the database handle. Record handles will inherit any callbacks registered with the table handle.
You clear callbacks from a handle by calling either ctdbClearCallback() or ctdbClearAllCallback().
FairCom DB API C API Example
/* allocate a record handle */
CTHANDLE hRecord = ctdbAllocRecord(hTable);
/* make sure there are no callbacks */
ctdbClearAllCallback(hRecord);You can check if a given callback has been registered with a session, database, table or record handle by calling the ctdbGetCallback() function. If a callback function was set, ctdbGetCallback() returns the address of the function. If a particular callback is not set, ctdbGetCallback() returns NULL.
FairCom DB API C API Example
/* allocate a table handle */
CTHANDLE hTable = ctdbAllocTable(hDatabase);
/* make sure CTDB_ON_TABLE_OPEN callback is set */
if (ctdbGetCallback(hTable, CTDB_ON_TABLE_OPEN) == NULL)
if (ctdbSetCallback(hTable, CTDB_ON_TABLE_OPEN, OnTableOpen) != CTDBRET_OK)
printf("ctdbSetCallback failed\n");
Allocating and Freeing Memory Inside Callbacks
When a callback function is executed, it may need to allocate, re-allocate and release memory that was allocated by the FairCom DB API internal code. The callback function must use exactly the same memory allocation function that was used by the FairCom DB API code or a heap corruption, and most certainly a memory exception, will occur.
The FairCom DB API session handle has a function pointer called onAlloc that must be used by the callback functions to allocate memory. The onAlloc function pointer has the following type:
typedef pVOID (ctdbDECL* ctdbAllocFunc)(VRLEN size);size is the number of bytes to be allocated. The returned value is a pointer to void.
The FairCom DB API session handle also has a function pointer called onFree that must be used by callback function to release memory. The onFree function pointer has the following type:
typedef void (ctdbDECL* ctdbFreeFunc)(pVOID ptr);ptr points to memory to be released. No value is returned.
Please note that the FairCom DB API database, table and record handles have a member variable called pSession which points to the current session handle. You can use this pSession member variable to obtain the reference to the onAlloc and onFree function pointers. In some cases you should typecast the pSession member as a pointer to a CTDBSESSION structure.