Begin
Mark beginning of transaction.
Short Name
TRANBEG()
Type
Low-Level data file function
Declaration
LONG Begin(COUNT trnmod)Description
Begin() marks the beginning of a transaction. All files opened by this user, that have been created with a file mode of either ctPREIMG or ctTRNLOG, will have file updates held until they are committed with a matching call to Commit(). For a full description of transaction processing, see the chapter on Data Integrity.
trnmod values presently supported are summarized in the following table:
| trmmod value | Operation |
|---|---|
| ctTRNLOG | Full transaction processing functionality, including auto-recovery. Mutually exclusive to ctPREIMG. |
| ctPREIMG | Transaction atomicity only, auto-recovery is not available. Mutually exclusive to ctTRNLOG. |
| ctENABLE | Automatically implements LockISAM(ctENABLE) when applicable. Mutually exclusive with ctREADREC. |
| ctREADREC | Automatically implements LockISAM(ctREADREC) when applicable. Mutually exclusive with ctENABLE. |
| ctLK_BLOCK | Automatically implements blocking locks when OR-ed with ctREADREC or ctENABLE. |
| ctLK_RECR | Enables recursive locking. |
| ctSAVENV | Saves current ISAM position after each successful ISAM file update operation (i.e., ReWriteRecord(), DeleteRecord(), etc. ). |
| ctAUTOSAVE | Automatically invokes savepoints after each successful ISAM or resource update. Disables ctSAVENV. |
| ctTWOFASE | Enables two-phase transaction support. |
| ctDEFERBEG | Enables a deferred transaction begin state. The TRANBEG log entry is delayed until an update is made within the transaction. This can benefit performance when no updates are made as the transaction flush can ultimately be skipped. |
Either ctPREIMG or ctTRNLOG must be used to specify the transaction logging mode.
Note: Using ctPREIMG with a file created with ctTRNLOG results in a TTYP_ERR (99).
Either ctENABLE or ctREADREC may also be OR-ed in to automatically invoke LockISAM(). If either is used, ctLK_BLOCK can be OR-ed in to convert the locks to blocking locks. This sleeps the process until the lock is available instead of returning DLOK_ERR (42).
If ctSAVENV is used, or if the user profile in InitCTreeXtd() or InitISAMXtd() includes USERPRF_SAVENV, then the current ISAM position of the files updated during the transaction is saved. This allows a subsequent Abort() or RestoreSavePoint() to reset the current ISAM position of the files.
Automatic Savepoints
ctAUTOSAVE automatically follows each successful ISAM update or resource update with a special savepoint. The savepoint permits an update error to be rolled-back so that a compound transaction can continue its updates and subsequently Commit() the transaction. This automatic savepoint does NOT support the ctSAVENV profile setting. ctSAVENV is disabled if ctAUTOSAVE is on.
This approach is faster than explicit savepoints after each update since no network traffic is required between updates, and the auto savepoint is more efficient than an explicit savepoint.
If an error occurs in the middle of a set of updates within a single transaction, and if ctAUTOSAVE was in trnmod, the application simply calls RestoreSavePoint(-1) to roll-back the error, and continues update processing.
Explicit calls to SetSavePoint() should not be made when ctAUTOSAVE is effective. Error ASAV_ERR (532) will be returned by SetSavePoint() if called after updates have occurred. A typical usage pattern in pseudo-code would be:
Begin(ctTRNLOG | ctAUTOSAVE | ctENABLE)
loop: update operation
if error
RestoreSavePoint(-1)
if not done
goto loop
Commit(...)Deferred Begin
It is not uncommon for a higher-level API to start transactions without knowledge of whether or not any updates will occur. To reduce the overhead of unnecessary log flushes, a deferred mode is available. ctDEFERBEG results in the actual TRANBEG entry in the log to be delayed until an attempt is made to update a transaction controlled file, and if a TRANEND() or TRANABT() is called without any updates, then the transaction begin and end log entries are not flushed to disk. ctDEFERBEG has no affect for ctPREIMG transactions.
Note: When using a transaction mode of deferred begin, Begin(ctTRNLOG | ctDEFERBEG) returns a value of 1 as the actual transaction number assignment is delayed until the log write of the first file update takes place.
Two-Phase Transactions
Two-Phase transaction support allows, for example, a transaction to span multiple servers. This is useful for updating information from a master database to remote databases in an all-or-nothing approach.
To start a transaction that supports a two-phase commit, you would include the ctTWOFASE attribute in the transaction mode passed to the Begin() function. Call the TRANRDY() function to execute the first commit phase, and finally Commit() to execute the second commit phase.
Note: You may need additional caution with regard to locking and unlocking of records as your transactions become more complex in a multi-server environment to avoid performance problems.
Example
(Note that this example could also use individual threads of operation for the different c-tree Server connections, avoiding the c-tree instance calls.)
COUNT rc1,rc2,filno1,filno2,rcc1,rcc2;
TEXT databuf1[128],databuf2[128];
/* Create the connections and c-tree instances */
...
if (!RegisterCtree("server_1")) {
SwitchCtree("server_1");
InitISAMXtd(10, 10, 64, 10, 0, "ADMIN", "ADMIN", "FAIRCOMS1");
filno1 = OPNRFIL(0, "mydata.dat", ctSHARED);
FirstRecord(filno1, databuf1);
memcpy (databuf1, "new data", 8);
/* Prepare transaction on c-tree server 1 */
Begin(ctTRNLOG | ctTWOFASE | ctENABLE);
ReWriteRecord(filno1, databuf2);
rc1 = TRANRDY();
}
if (!RegisterCtree("server_2")) {
SwitchCtree("server_2");
InitISAMXtd(10, 10, 64, 10, 0, "ADMIN", "ADMIN", "FAIRCOMS2");
filno2 = OPNRFIL(0, "mydata.dat", ctSHARED);
FirstRecord(filno2, databuf2);
memcpy (databuf2, "new data", 8);
/* Prepare transaction on c-tree server 2 */
Begin(ctTRNLOG | ctTWOFASE | ctENABLE);
ReWriteRecord(filno2, databuf2);
rc2 = TRANRDY();
}
/* Commit the transactions */
if (!rc1 && !rc2) {
SwitchCtree("server_1");
rcc1 = Commit(ctFREE);
SwitchCtree("server_2");
rcc2 = Commit(ctFREE);
if (!rcc1 && !rcc2) {
printf("Transaction successfully committed across both servers.\n");
} else {
printf("One or more units of the second commit phase of the transaction failed: rcc1=%d rcc2=%d\n", rcc1, rcc2);
}
} else {
printf("One or more of the transactions failed to be prepared: rc1=%d rc2=%d\n", rc1, rc2);
printf("Pending transactions will be aborted.\n");
SwitchCtree("server_1");
Abort();
SwitchCtree("server_2");
Abort();
}
/* Done */
SwitchCtree("server_1");
CloseISAM();
SwitchCtree("server_2");
CloseISAM();
Note: Two-Phase transactions can become extremely difficult to debug should there be communications problems between servers at any time during the second commit phase. This can result in out of sync data between the servers as one server may have committed while another server failed. It is always appropriate to check the return codes of the individual Commit() functions to ensure a complete successful transaction commit across multiple servers.
Return
Begin() returns a transaction number (low word for six-byte transaction numbers) if successful, or zero on error. With six-byte transaction number support, call ctGETHGH() for the high word of the transaction number.
It an error occurs Begin() returns 0 and uerr_cod will return the appropriate c-tree error code.
| Value | Symbolic Constant | Explanation |
|---|---|---|
| 70 | TEXS_ERR | Transaction already pending - you cannot start one until the prior one is committed or aborted. |
| 99 | TTYP_ERR | Transaction type / trnmod conflict. |
| 150 | SHUT_ERR | Server is shutting down. |
See FairCom DB Error Codes for a complete listing of valid FairCom DB error values.
Example
COUNT savepoint;
void domaster() {
Begin(ctENABLE|ctTRNLOG); /* start transaction with locks */
while(another()); { /* get next record to add */
savepoint = SetSavePoint(); /* get save point at
beginning of each master record */
if (add_master() < 0)
Abort(); /* Begin if can't add master rec */
dodetail(); /* process detail records */
}
if (Commit(ctFREE) != 0)
printf("\nError %d in transaction",uerr_cod);
return;
}
void dodetail() {
while(moredetail()); { /*get next detail record to add */
if (add_detail()<0) { /* add details, if possible */
RestoreSavePoint(savepoint) /* with error, return
to savept */
return;
}
}
}See also
Abort, AbortXtd, Commit, ClearSavePoint, ctGETHGH, ctSETHGH, DeleteRecord, InitCTreeXtd, InitISAMXtd, LockISAM, RestoreSavePoint, ReWriteRecord, SetSavePoint, SetOperationState, TRANRDY
BlockingISAMRead
c-tree ISAM function to support a blocking record read.
Short Name
BLKIREC()
Type
ISAM function
Declaration
COUNT BlockingISAMRead(FILNO filno,NINT opcode,LONG timeoutsec,
pTEXT blockcond,pVOID target,pVOID recptr,pVRLEN plen)Description
In V12 the file number typedef was formally changed from COUNT, a two-byte value to FILNO, a four-byte value. Refer to this link for compatibility details. Four Byte File Numbering
A blocking record read permits an application to attempt to read a record, and if no existing record satisfy the read request, BlockingISAMRead() blocks until such a record exists or the times out period expires.
BlockingISAMRead() supports blocking ISAM reads on fixed or variable-length records subject to an optional blocking condition that may be a logical expression or a function callback, just as in a set filter call (SETFLTR()).
- filno is a file number of an open c-tree data or index file.
- opcode specifies one of the following ISAM read operations:
| ISAM Read Operation | Explanation |
|---|---|
| ctBLKIREC_FIRST | Read first record in physical or key order |
| ctBLKIREC_NEXT | Read next record in physical or key order |
| ctBLKIREC_PREV | Read previous record in physical or key order |
| ctBLKIREC_LAST | Read last record in physical or key order |
| ctBLKIREC_GT | Read record with key value greater than target key |
| ctBLKIREC_GTE | Read record with key value greater than or equal to target key |
| ctBLKIREC_EQL | Read record with key value equal to target key |
| ctBLKIREC_LTE | Read record with key value less than or equal to target key |
| ctBLKIREC_LT | Read record with key value less than target key |
- timeoutsec specifies the number of seconds the blocking read will wait before returning if no record satisfies the optional blockcond argument or no record is found. Set timeoutsec to zero to return immediately.
- blockcond is an optional conditional expression which may be a logical expression of the same form as used for conditional index support or a function callback as in a SetDataFilter() call. Set blockcond to an empty string ("") to specify no condition.
- target should be non-NULL only if filno specifies an index and the opcode requires a target value (for example, ctBLKIREC_GT).
- The record is read into the buffer pointed to by recptr.
- plen should be NULL for a fixed-length read, and should point to the length of the output buffer (recptr) for a variable-length read. *plen is updated to the actual record length upon successful return.
The function operates by performing the requested ISAM operation (FIRST(), NEXT(), etc.). If the requested ISAM operation successful, BlockingISAMRead() then sees if the optional blocking condition is satisfied. If no record was found or if the record does not satisfy the blocking condition, and timeoutsec is non-zero, then the read blocks (sleeps) for timeoutsec. The sleep is interrupted if the target file is updated, and this process repeats until a record is found that satisfies the condition, or the block times out. A time out condition is indicated by returning NTIM_ERR (156). BlockingISAMRead() requires server notification support to operate correctly.
Return
| Value | Symbolic Constant | Explanation |
|---|---|---|
| 0 | NO_ERROR | No error occurred. |
| 156 | NTIM_ERR | Timeout error |
See FairCom DB Error Codes for a complete listing of valid FairCom DB error values.
Example
struct {
TEXT job_name[256];
.....
LONG start_time;
.....
} JOBMAN;
TEXT blockcond[128];
ChangeISAMContext
/*
** setting the first character to zero means no
** blocking condition
*/
blockcond[0] = '\0';
start:
rc = BLKIREC( keyno, ctBLKIREC_FIRST, WAITTIME, blockcond, NULL, &JOBMAN, NULL);
if (rc == NTIM_ERR) {
/* time out occurred. check whether to continue
** job management processing
*/
if **continue** {
blockcond[0]= '\0';
goto start;
} else
goto end;
} else if (rc) {
/* unexpected error */
handle possible error conditions
[examin BLKIREC source for possible error cases]
} else {
/* found job record. check job start time */
if **JOBMAN.start_time <= current_time** {
launch job
update record (so no longer at front of file)
blockcond[0] = '\0';
goto start;
} else {
/*
** job not ready to run yet. create a
** blocking condition so that if a job with
** an earlier time is added to the job file
** the BLKIREC will return
*/
ctrt_sprintf(blockcond, "start_time < %ld",JOBMAN.start_time);
goto start;
}
}
end:
See also
To double performance, see ChangeISAMContext.
BuildKey
BuildKey() assembles a key value from the supplied record buffer using the ISAM key segment definitions, then calls TransformKey().
Short Name
frmkey()
Type
ISAM-level index file function
Declaration
COUNT frmkey(FILNO keyno, pTEXT recptr, pTEXT txt, ctRECPT pntr, VRLEN datlen)Description
In V12 the file number typedef was formally changed from COUNT, a two-byte value to FILNO, a four-byte value. Refer to this link for compatibility details. Four Byte File Numbering
BuildKey() extracts a key value for index number keyno from the data record image of datlen bytes pointed to by recptr. The key value is assembled at the location pointed to by txt. If the index supports duplicate key values, then the 4-byte value given by pntr is appended to the end of the key value in most significant to least significant byte order. BuildKey() uses the information in the Incremental ISAM structure, or ISAM parameter file, to extract the key, concatenating key segments after they have been transformed according to the segment mode parameter.
BuildKey() may be called directly from an application program in a situation where a target key value can be created according to the ISAM specifications, but TransformKey() is not convenient. Consider the following example in which the key value is made up of two segments:
- The first 6 bytes of the name field converted to upper case with a segment mode value of 2.
- The 4 bytes of the id field.
In an 80X86 environment, the 4-byte id field will be automatically reversed using a segment mode value of 1 (INTSEG - unsigned integer).
The value of pntr is only used when an index supports duplicate entries. CurrentFileOffset() can be used to determine the current file offset. In most cases, pass a long zero (0L) for this parameter. Use 0xffffffff for pntr before a call to GetLTERecord() for an index supporting duplicates.
Return
BuildKey() returns the length of the key value assembled or a zero if the key is composed entirely of the empty character, as defined in the parameter file. See FairCom DB Error Codes for a complete listing of valid FairCom DB error values.
A non-zero return code indicates success. Zero means either no key or an error occurred. When the function returns zero, check isam_err. If it is zero, the specified record image produced no key value (for example due to a condition on the index, or the null key check is enabled and the key consists entirely of the empty character). If it is non-zero, an error occurred.
Example
FILNO keyno;
COUNT i;
struct {
COUNT delflg[4];
TEXT name[24];
LONG id;
TEXT desc[128];
} cur_info, /* current ISAM buffer */
upd_info, /* rewrite update buffer */
tar_info; /* target key staging area */
TEXT target[10]; /* target key buffer */
printf("\n\nEnter name field ");
scanf("%24s",tar_info.name); /* load name field */
for (i = strlen(tar_info.name); i < 24; i++) /* pad name */
tar_info.name[i] = PADDING;
printf("\nEnter id field ");
scanf("%ld",&tar_info.id); /* load id */
/* now the raw elements of the target are ready */
frmkey(keyno, &tar_info,target,0L,sizeof(tar_info));
/* frmkey performs the necessary transformations on tar_info
and places the result in target. Now search isam files. */
GetRecord(keyno,target,&cur_info);Limitations
The recbyt parameter in this function is a 4-byte value capable of addressing at most 4 gigabytes. If your application supports HUGE files (greater than 4 gigabytes), you must use the ctSETHGH() and ctGETHGH() functions to set or get the high-order 4 bytes of the file offset.
See also
CurrentFileOffset, GetLTERecord, TransformKey