.ig >>
<STYLE TYPE="text/css">
<!--
        A:link{text-decoration:none}
        A:visited{text-decoration:none}
        A:active{text-decoration:none}
        OL,UL,P,BODY,TD,TR,TH,FORM { font-family: arial,helvetica,sans-serif;; font-size:small; color: #333333; }

        H1 { font-size: x-large; font-family: arial,helvetica,sans-serif; }
        H2 { font-size: large; font-family: arial,helvetica,sans-serif; }
        H3 { font-size: medium; font-family: arial,helvetica,sans-serif; }
        H4 { font-size: small; font-family: arial,helvetica,sans-serif; }
-->
</STYLE>
<title>shsql: record locking</title>
<body bgcolor=99cc99 vlink=0000FF>
<br>
<br>
<center>
<table cellpadding=2 bgcolor=FFFFFF width=550 ><tr>
<td align=right><a href="shsql_home.html">
<img src="img/shsql.gif" border=0><br><small>SQL database system</a> &nbsp; </td></tr>
<td>
.>>

.TH record locking TDH "30-APR-2004   TDH scg@jax.org" 

.SH Record locking
\fBshsql\fR has a built-in facility for record locking.  \fBshsql\fR record locking is an 
.ig >>
<a href="tablelocking.html">
.>>
\0advisory
.ig >>
</a>
.>>
facility that allows 
database records to be reserved while changes are pending.  It prevents other users
from reserving or modifying records to which changes are pending.
Record locking is recommended for any table that could be updated by more than one
user or process concurrently.  If used properly, record locking can alleviate some problems that 
are addressed by transaction control in full-blown DBMS systems.
.LP
Successful use of record locking requires database accesses to be coded to detect
attempts to access/update records that someone else has already locked.  
It is best if all programs that update a table that uses record locks be coded using
the same conventions.  Some 
.ig >>
<a href="#example">
.>>
\0procedural examples
.ig >>
</a>
.>>
are given below.  Developers should understand how record locking interacts with
.ig >>
<a href="tablelocking.html">
.>>
\0table locking,
.ig >>
</a>
.>>
and be familiar with strategies for avoiding deadlock.

.LP
\fBshsql\fR's record locking facility is intended to be convenient and
easy to work with, with special consideration for web-based applications.  The
.ig >>
<a href="select.html">
.>>
\0SELECT .. FOR UPDATE
.ig >>
</a>
.>>
command is used to lock records.
Locked records are automatically unlocked upon the next 
.ig >>
<a href="iud.html">
.>>
\0UPDATE or DELETE
.ig >>
</a>
.>>
executed by the same 
.ig >>
<a href="identity.html">
.>>
\0user/identity
.ig >>
</a>
.>>
who locked the record.  Record locks will also
automatically expire (time out) after a certain amount of time (30 minutes by default,
.ig >>
<a href="config.html#dbrecordlock_timeout">
.>>
\0configurable).
.ig >>
</a>
.>>
This is useful in web-based applications where users can
lock records for editing but then aren't forced to follow through and save or cancel.

.ig >>
<br><br><br>
.>>

.LP
Here are the rules for using \fBshsql\fR record locking:
.IP \(bu
Tables for which record locking is to be used must have \fC_locktime\fR and \fC_lockowner\fR fields.
These fields (using these exact names) may be specified when the table is
.ig >>
<a href="create.html#table">
.>>
\0CREATEed
.ig >>
</a>
.>>
or by using
.ig >>
<a href="alter.html">
.>>
\0ALTER,
.ig >>
</a>
.>>
and may be at any logical location within the record.

.ig >>
<br><br><br>
.>>
.IP \(bu
Any process that will lock a record or modify a locked record must have an
.ig >>
<a href="identity.html">
.>>
\0identity
.ig >>
</a>
.>>
associated with it.

.ig >>
<br><br><br>
.>>
.IP \(bu
In order to lock record(s), the \fCSELECT..FOR UPDATE\fR command is used.
.nf
\fC	select * from \fItablename\fC where \fIcondition\fC FOR UPDATE\fR
.fi
The asterisk (\fC*\fR) is required, and the command must be a simple, single-table 
\fCSELECT\fR command (\fCJOIN\fR, \fCGROUP BY\fR, \fCINTO\fR, \fCLIMIT\fR etc. 
cannot be used).  All eligible records matching the \fIcondition\fR will be locked
(a limit of 200 records applies).

.ig >>
<br><br><br>
.>>

.IP \(bu
The record lock will be released automatically upon the next \fCUPDATE\fR or \fCDELETE\fR
that affects the record, executed by the identity holding the lock.  
No special syntax is required on the \fCUPDATE\fR or \fCDELETE\fR commands.

.ig >>
<br><br><br>
.>>
.IP \(bu
Once record locking is set up for a table it is
best for all processes to use record locking when accessing it.  However,
nothing prevents an unlocked record from being modified by \fCUPDATE\fR or \fCDELETE\fR 
command directly, without locking the record first.  

.ig >>
<br><br><br>
.>>
.IP \(bu
\fCSELECT .. FOR UPDATE\fR is used to lock one record or multiple records.
If feasible, applications should be designed so that single record locks accomplish 
necessary protection, since locking multiple records can add complexity.

.ig >>
<br><br><br>
.>>
.IP \(bu
If someone has locked a record, then \fBsingle record\fR
\fCUPDATE\fR, \fCDELETE\fR, and \fCSELECT..FOR UPDATE\fR commands issued by someone else
on that record will not be successful while the lock is in effect.  
An error code# 7 will be returned by the API,
with no error message generated, since this is a "normal" occurance.  

.ig >>
<br><br><br>
.>>
.IP \(bu
\fCSELECT .. FOR UPDATE\fR
commands that attempt to lock \fBmultiple records\fR will receive an error code# 7 only when 
fetching a record that is already locked by someone else.  This allows applications to
ensure that they can reserve all necessary records before modifying them.
NOTE: A multi-row \fCSELECT .. FOR UPDATE\fR causes a table write 
lock to be in effect until all rows are fetched, so \fBall rows should be retrieved without delay\fR,
even if some of them involve an error.

.ig >>
<br><br><br>
.>>
.IP \(bu
Suppose the following occurs: 1) a certain identity acquires a lock; 2) then the lock expires (times out);
3) the same identity issues an \fCUPDATE\fR or \fCDELETE\fR.  The operation will be allowed unless some 
other identity has locked (and perhaps unlocked) the record in the meantime.

.ig >>
<br><br><br>
.>>
.IP \(bu
Subsequent \fCSELECT..FOR UPDATE\fR commands issued by the lock owner on a locked record will reset the 
expiration clock for a fresh 30 minutes (or whatever).

.ig >>
<br><br><br>
.>>
.IP \(bu
To release a record lock without changing any data, an update command
such as this may be used: \fCupdate mytable set _locktime = null where ....\fR.

.ig >>
<a name=example></a>
.>>
.ig >>
<br><br><br>
.>>

.SH Example of record locking procedure - single record
.IP
1. middleware issues an \fCIDENTITY\fR command and then a \fCSELECT .. FOR UPDATE\fR to 
retrieve desired record.
.IP
2. middleware captures retrieval return code.  If it is \fC7\fR, this indicates 
that record(s) are already locked by someone else.  Inform user and exit.
QUISP example:
.nf
\0  #set USER = $getenv( "REMOTE_ADDR" )
\0  #sql identity @USER
\0  #sql select * from mytable where id = @reqid for update
\0  #if $sqlerror() = 7
\0   <h3>Record in use.. try later</h3>
\0   #exit
\0  #endif
.fi
.IP
3. otherwise, user has reserved the record and can edit it
.IP
4. user submits saved content
.IP
5. middleware issues an IDENTITY command and then
an UPDATE command for record.
.IP
6. middleware captures update return code.  If it is \fC7\fR, this indicates that the record
lock timed out, and someone else has locked (and perhaps unlocked)
the record in the meantime.  Inform user that her updates cannot be saved.
QUISP example:
.nf
\0 #set USER = $getenv( "REMOTE_ADDR" )
\0 #sql
\0   #sqlbuild  @formmode  mytable  quote  noquote=id
\0 #endsql
\0 #if $sqlerror() = 7
\0  <h3>Your lock timed out.. 
\0    record now in use by someone else.. 
\0    updates can't be saved</h3>
\0  #exit
\0 #endif
.fi 
.IP
7. otherwise, updates are saved, and record automatically unlocked.

.ig >>
<br><br><br>
.>>
.SH Example of record locking procedure - multiple records
.IP
1. middleware issues an \fCIDENTITY\fR command and then a \fCSELECT .. FOR UPDATE\fR to 
retrieve desired records.
.IP
2. middleware captures retrieval return code.  If it is \fC7\fR for any
record, this indicates that the complete record set is not available.  
Release the record locks, inform user and exit.
.IP
3. otherwise, user has reserved the records and can edit them.
.IP
4. user submits saved content
.IP
5. middleware issues an IDENTITY command and then issues the above \fCSELECT .. FOR UPDATE\fR
command again to ensure that all records still available.  If this returns \fC7\fR for any
record, the locks have expired and someone else has locked record(s) in the meantime; inform 
user that her updates cannot be saved.
.IP
6. middleware issues UPDATE command for records, and checks return code to be sure it is \fC0\fR.
If so, updates are saved and records automatically unlocked.

.ig >>
<br><br><br>
.>>

.SH Operational details
.LP
\fCSELECT..FOR UPDATE\fR locks a record by recording the current time and identity in the
record's \fC_locktime\fR and \fC_lockowner\fR fields.
\fCUPDATE\fR unlocks a record by setting the \fC_locktime\fR field to null (but the \fC_lockowner\fR
field is left alone).

.LP
A record is considered unlocked if the \fC_locktime\fR field is null or contains a
time value that is old enough to be considered expired.

.LP
Time values are stored using \fIYMDhm\fR notation.
0 - 9 are represented naturally; 10-35 are represented using characters \fCa\fR through \fCz\fR; 
36-61 are represented using characters \fCA\fR through \fCZ\fR.

.LP
The data returned by a \fCSELECT..FOR UPDATE\fR is retrieved immediately before the record 
lock is set; hence the returned \fC_locktime\fR and \fC_lockowner\fR fields are not current.

.LP 
Since setting record lock(s) involves updating the table, a 
.ig >>
<a href="tablelocking.html">
.>>
\0table write lock
.ig >>
</a>
.>>
is set to cover the duration of the update.
A multi-row \fCSELECT .. FOR UPDATE\fR causes the table write 
lock to be in effect until all rows are fetched, so all rows should be retrieved without delay.

.LP
If someone attempts to \fCUPDATE\fR or \fCDELETE\fR a set of records without locking them
first, and the record set includes one or more records locked by others, the locked records 
will not be affected but all other records in the target set will be.  The \fCUPDATE\fR or 
\fCDELETE\fR will return an error code of 7 to indicate that one or more target records was 
locked and not modified.  This can be avoided if all processes lock records
before \fCUPDATING\fR or \fCDELETING\fR them and, for multi-row updates where the record 
locks could time out, re-issueing the \fCSELECT ..FOR UPDATE\fR just before update to 
ensure that the records are still eligible for update (see multi-record example above).



.ig >>
<br>
<br>
</td></tr>
<td align=right>
<a href="shsql_home.html">
<img src="img/shsql.gif" border=0></a><br>
<a href="Copyright.html">Copyright Steve Grubb</a> &nbsp;
</td></tr>
</table>
.>>
