Question What's wrong with my code?

Dave Reyes

New Member
Hi. I'm quite new to Progress programming, so I'm not really familiar with all the codes.
I'm trying to figure out what's wrong with this one, can anyone help me?

Code:
ON 'WINDOW-CLOSE':U OF wMain_SNL.r:
DO:
    FIND CURRENT Users NO-LOCK NO-ERROR.
    FIND FIRST LogRest WHERE LogRest.Username = Users.Username.
    IF AVAIL LogRest THEN DO:
        DELETE LogRest.
    END.
    RETURN.
END.

I have two tables here, Users and LogRest(Login Restriction).
My idea is to delete the table entry in LogRest when the main window closes.
 

RealHeavyDude

Well-Known Member
What exactly is your issue?

But:
  • The FIND FIRST is bad practice - assuming that you don't allow more than 1 LogRest records per User.
  • Using plural for talbe names is not good practice.
  • FIND CURRENT will only work when there is already a record in the Users buffer.
  • You might miss the APPLY CLOSE TO THIS-PROCEDURE if your have a corresponding WAIT-FOR in the main block.

Heavy Regards, RealHeavyDude.
 

Cringer

ProgressTalk.com Moderator
Staff member
ON 'WINDOW-CLOSE':U OF wMain_SNL.r:

is wMain_SNL.r really the name of your window?
 

GregTomkins

Active Member
1. FIND CURRENT is probably not what you want. That only really works if you're sure there is already a record in the buffer. You probably want something like 'find users where users.username = some variable that you use to keep track of the current user'.

2. The FIND FIRST (or just FIND, if you take RHD's advice, which is common) should probably have a NO-ERROR on it. It doesn't make sense to have an IF AVAIL ... test without a NO-ERROR on the related FIND. Generally you should have both (NO-ERROR and IF AVAIL) but having one and not the other is usually wrong.
 

TomBascom

Curmudgeon
Everything that RHD said is bang-on.

As Cringer says, that looks like an odd window name. Also, the colon after the window name looks like an error to me. It might be legal -- but it is certainly not normal.

Are you really planning to do translation? Or are you just sprinkling :U on strings for the joy of it?

Where do you expect FIND CURRENT to be looking for a current record? This snippet of code requires it to look outside the scope of the trigger so you are relying on something which is globally scoped and potentially introducing side effects by changing the LOCK status. So that's a problem.

You're not actually doing anything with "users" other than (possibly) changing the lock status and using the username field to look up a logRest record -- so refinding it by way of a borrowed buffer and then changing the lock status makes even less sense.

You suppress the possibility of an error but there is no test for success of your FIND CURRENT. If FIND CURRENT fails you will quietly go on to the next statement.

The FIND of logRest does not have a NO-ERROR. So the IF AVAILABLE test isn't going to be effective -- an error will be thrown before you get there.

If LogRest is actually unique then FIRST serves no purpose and confuses people.

If LogRest is not unique then what are you doing with the other members of the set?

Either way, the FIND LogRest does not specify a LOCK and is only weakly scoped. It is a default share-lock and if this buffer is being borrowed from a globally scoped buffer (with code written like this that seems likely) then you are at risk of running into or creating lock conflicts.

I would rewrite this as:

Code:
on "window-close" of this-procedure
do:
 
  define buffer del_logRest for logRest.
 
  if available users then
    do for del_logRest transaction:
      find del_logRest exclusive-lock where del_logRest.userName = users.userName no-error.
      if available del_logRest then delete del_logRest.
    end.
  else
    do:
      /* some sort of appropriate error handling or notification */
    end.
 
  return.
 
end.
 

Dave Reyes

New Member
I actually have three procedures.
The first one is to create an entry on the LogRest table whenever a user logs into the system.
The second one is to block/deny access to the system whenever the procudure sees the user has a LogRest entry.
And the third one is this.

Sorry for my bad programming practices, I'm still learning and I am currently on my third week with this language.
 

Dave Reyes

New Member
Yes, I have tried what you all have said, and it worked.
I am now on my testing phase with this code. Thanks for your help guys. :)
 

RealHeavyDude

Well-Known Member
You should be aware that you might run into an issue when the client session for whatever reason crashes. The corresponding LogRest record might remain in the database effectively locking the user out of the system without a valid reason. You need to have some remediation strategy in place for that - an administrative function to allow somebody to delete that superflous LogRest record.

In the past I took a different approach to achieve a similar functionality by deliberately - YES - using a SHARE-LOCK:
  1. I had a record for each user in an associated table that got created when the user record got created.
  2. When the user logged into the system the login logic tried to fetch the record in the associated table with an EXCLUSIVE-LOCK.
  3. When the logic could get the record with an EXCLUSIVE-LOCK it knew that the user was not logged in and immediately downgraded to a SHARE-LOCK.
  4. When the logic could not get the record with an EXCLUSIVE-LOCK it knew that the user was already logged in locking that particular record with a SHARE-LOCK.
  5. Whenever the session ended gracefully the SHARE-LOCK was automatically released at the end of it's buffer scope.
  6. Whenever the session crashed or disappeared for whatever reaseon the database engine would detect it and automatically release the SHARE-LOCK on the record.
No need to have any administrative action when a session crashed or did not end gracefully.

DISCLAIMER: Avoid SHARE-LOCK - they are most likely the road to hell. But there are exceptions to every rule and such a scenario - if you exactly know what you are doing - might be a valid one.

Heavy Regards, RealHeavyDude.
 
Top