/* dbio.p Usage: run dbio.p (INPUT cAction, INPUT sessid, INPUT-OUTPUT TABLE tt_user, OUTPUT cMsg). ACTION= add_rec Add new record update_rec Update existing record get_rec Get record & prepare for update find_rec Get record with no update preparation Add/Update/Find the record identified by UserID in the passed tt_user record. */ DEFINE TEMP-TABLE tt_user NO-UNDO LIKE user. DEFINE TEMP-TABLE TT_OPTIMISTIC LIKE user. DEFINE INPUT PARAMETER cACTION AS CHAR NO-UNDO. DEFINE INPUT PARAMETER sessid AS CHAR NO-UNDO. DEFINE INPUT-OUTPUT PARAMETER TABLE FOR tt_user. DEFINE OUTPUT PARAMETER cMSG AS CHAR NO-UNDO. DEFINE VARIABLE WKEQUAL AS LOGICAL NO-UNDO. DEFINE VARIABLE iCount AS INTEGER NO-UNDO. DEFINE VARIABLE iLockRetryCount AS INTEGER INITIAL 5 NO-UNDO. FIND FIRST tt_user NO-LOCK NO-ERROR. IF NOT AVAILABLE tt_user THEN DO: cMsg = "No temp-table passed.". RETURN. END. CASE cACTION: WHEN "add_rec" THEN DO: /* ADD */ /* We don't allow specifying a UserID in an ADD. if UserID is non-zero, this is an error. */ IF tt_user.UserID NE 0 THEN DO: cMSG = "Not allowed to create a record with a specific UserID.". RETURN. END. DO TRANSACTION: CREATE user. ASSIGN user.UserID = NEXT-VALUE(UserID). BUFFER-COPY tt_user EXCEPT UserID TO user. /* Assign the temp-table UserID to return with buffer. */ tt_user.UserID = user.UserID. END. RETURN. END /* WHEN add_rec */. WHEN "update_rec" THEN DO TRANSACTION: /* UPDATE */ /* Get Record to Update */ DO iCount = 1 TO iLockRetryCount: /* Attempt to lock the record but don't wait, as there is no "user" to hit CTRL-C to break a dead-lock. */ FIND user EXCLUSIVE-LOCK WHERE user.UserID = tt_user.UserID NO-WAIT NO-ERROR. IF AVAILABLE user THEN LEAVE. /* If the record is not available, the 3 possibilities: It's locked, ambiguous (not with a unique key) or there is no record with this UserID. */ IF NOT AVAIL user THEN DO: IF LOCKED user THEN DO: IF iCount > iLockRetryCount THEN DO: cMSG = "Record is locked by another user or process.". RETURN. END. /* Otherwise, wait one second and try again. */ PAUSE 1 NO-MESSAGE. iCount = iCount + 1. NEXT. END. /* Otherwise, there is no record for this UserID. */ cMSG = "Record #" + STRING(tt_user.UserID) + " Not Found". RETURN. END /* IF NOT AVAIL user */. END /* DO iCount EQ 1 TO iLockRetryCount */. /* To be here, we must have an exclusive-lock on our record. */ /* Optimistic Locking Logic */ FIND FIRST SESSREC WHERE SESSREC.SESSID = sessid AND SESSREC.FILENAME = "customer" AND SESSREC.RECROWID = STRING(ROWID(user)) EXCLUSIVE-LOCK NO-ERROR. IF NOT AVAIL SESSREC THEN DO: cMSG = "No Optimistic Locking for " + STRING(user.UserID). RETURN. END. CREATE TT_OPTIMISTIC. RAW-TRANSFER FIELD SESSREC.RECDATA TO TT_OPTIMISTIC. BUFFER-COMPARE TT_OPTIMISTIC TO user SAVE RESULT IN WKEQUAL. IF NOT WKEQUAL THEN DO: cMSG = "Record has changed!". RETURN. END. BUFFER-COPY tt_user TO user. cMsg = "Record updated.". END. WHEN "get_rec" OR WHEN "find_rec" THEN DO: FIND user NO-LOCK WHERE user.UserID = tt_user.UserID NO-ERROR. IF NOT AVAIL user THEN DO: cMSG = "Record #" + STRING(tt_user.UserID) + " Not Found". RETURN. END. IF cAction EQ "get_rec" THEN DO TRANSACTION: FIND FIRST SESSREC EXCLUSIVE-LOCK WHERE SESSREC.SESSID = sessid AND SESSREC.FILENAME = "customer" AND SESSREC.RECROWID = STRING(ROWID(user)) NO-ERROR. IF NOT AVAIL SESSREC THEN DO: CREATE SESSREC. ASSIGN SESSREC.SESSID = sessid SESSREC.FILENAME = "customer" SESSREC.RECROWID = STRING(ROWID(user)). END. RAW-TRANSFER user TO FIELD SESSREC.RECDATA. END. /* DO TRANSACTION */ DELETE tt_user. CREATE tt_user. BUFFER-COPY user TO tt_user. END. END CASE.