Oracle FAQ Your Portal to the Oracle Knowledge Grid
HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US
 

Home -> Community -> Usenet -> c.d.o.misc -> Re: lock Oracle db objects, but only a lock for others?

Re: lock Oracle db objects, but only a lock for others?

From: Eric Fortin <emfortin_at_attbi.com>
Date: Mon, 21 Apr 2003 13:06:40 GMT
Message-ID: <z1Soa.581640$S_4.634168@rwcrnsc53>

"Eric Fortin" <emfortin_at_attbi.com> wrote in message news:3%Joa.551141$F1.76351_at_sccrnsc04...
> "Bob Kilmer" <rprgrmr_at_yahoo.com> wrote in message
> news:FbHoa.222550$o8.3779878_at_twister.tampabay.rr.com...
> > "Daniel Morgan" <damorgan_at_exesolutions.com> wrote in message
> > news:3EA32BBB.1A412583_at_exesolutions.com...
> > > Eric Fortin wrote:
> > >
> > > > "Daniel Morgan" <damorgan_at_exesolutions.com> wrote in message
> > > > news:3EA31A5F.C26DAB1A_at_exesolutions.com...
> > > > > > > On Error Goto
> > > > > >
> > > > > > What is the correct way to Error Handle in VB6?
> > > > >
> > > > > To handle the errors in the back-end, or middle-tier ifone exists:
> Not
> > in
> > > > the
> > > > > front-end. VB should only be used to display messages returned
from
> > the
> > > > serer.
> > > >
> > > > >
> > > > > > >
> > > > > > > If things like this have been written expect that the entire
> code
> > base
> > > > > > needs to
> > > > > > > be rewritten.
> > > > > > >
> > > > > >
> > > > > > Can you please give an example of a correct construction for VB6
> and
> > > > Oracle?
> > > > > >
> > > > > > (or is it the order, begin trans followed by error handling)
> > > > > > (or is it that vb6 should not be used with Oracle)
> > > > > >
> > > > > > I guess I'm not clear on how it should be done.
> > > > > >
> > > > > > Thanks.
> > > > >
> > > > > No problem. I teach an entire certificate program, three quarters,
> at
> > the
> > > > > University of Washington on the subject. So I'll just include all
of
> > that
> > > > > information here in this usenet post
> > > > >
> (http://www.outreach.washington.edu/extinfo/certprog/oad/oad_crs.asp)
> > just
> > > > > kidding. Seriously this is not
> > > > > something that can be done generically. Just to give you an
example
> of
> > > > what I
> > > > > would consider minimal information required to construct Oracle
> error
> > > > handling
> > > > > ... I would say that you must understand all of the following:
> > > > >
> > > > > TooManyDays EXCEPTION;
> > > > > EXCEPTION WHEN TooManyDays THEN
> > > > > EXCEPTION WHEN NO_DATA_FOUND THEN
> > > > > EXCEPTION WHEN OTHERS THEN
> > > > > PRAGMA EXCEPTION_INIT
> > > > > RAISE;
> > > > > RAISE_APPLICATION_ERROR(-20001, 'Date Can Not Be In The Future');
> > > > > AUTONOMOUS TRANSACTIONS
> > > > >
> > > > > And likely I'll think of something I missed as soon as press the
> SEND
> > > > button.
> > > > >
> > > > > Daniel Morgan
> > > > >
> > > >
> > > > On error goto is in the VB6 code
> > > >
> > > > if that exception is thrown in the db and not handled in the VB app,
> the
> > vb
> > > > app will get a terminating ora-440 error. (Then, especially if no
> begin
> > > > trans, all the sql up to that point will be auto committed and the
sql
> > after
> > > > that 440 error will never be executed)
> > > >
> > > > Here is the Oracle Doco ( bottom of the page): on how to do it with
> Ora
> > OLE
> > > > DB.
> > > >
> > > >
> >

>
(http://download-west.oracle.com/docs/cd/B10501_01/win.920/a95498/using.htm#
> > > > 1006186)----code sample:Dim Oracon As ADODB.Connection
> > > > Dim recset As New ADODB.Recordset
> > > > Dim cmd As New ADODB.Command
> > > > Dim param1 As New ADODB.Parameter
> > > > Dim param2 As New ADODB.Parameter
> > > > Dim objErr As ADODB.Error
> > > > Dim Message, Title, Default, EmpNoValue
> > > >
> > > > Message = "Enter an employee number (5000 - 9000)"
> > > > Title = "Choose an Employee"
> > > > Default = "7654"
> > > >
> > > > On Error GoTo err_test
> > > >
> > > > EmpNoValue = InputBox(Message, Title, Default)
> > > > If EmpNoValue = "" Then Exit Sub
> > > > If EmpNoValue < 5000 Or EmpNoValue > 9000 Then EmpNoValue = 7654
> > > >
> > > > Set Oracon = CreateObject("ADODB.Connection")
> > > > Oracon.ConnectionString = "Provider=OraOLEDB.Oracle;" & _
> > > > "Data Source=exampledb;" & _
> > > > "User ID=scott;" & _
> > > > "Password=tiger;"
> > > >
> > > > Oracon.Open
> > > > Set cmd.ActiveConnection = Oracon
> > > > Set param1 = cmd.CreateParameter("param1", adSmallInt, adParamInput,
,
> > > > EmpNoValue)
> > > > cmd.Parameters.Append param1
> > > > Set param2 = cmd.CreateParameter("param2", adSmallInt,
adParamOutput)
> > > > cmd.Parameters.Append param2
> > > >
> > > > ' Enable PLSQLRSet property
> > > > Cmd.Properties ("PLSQLRSet") = TRUE
> > > >
> > > > cmd.CommandText = "{CALL Employees.GetDept(?, ?)}"
> > > > Set recset = cmd.Execute
> > > >
> > > > ' Disable PLSQLRSet property
> > > > Cmd.Properties ("PLSQLRSet") = FALSE
> > > >
> > > > MsgBox "Number: " & EmpNoValue & " Dept: " &
> > recset.Fields("deptno").Value
> > > >
> > > > Exit Sub
> > > >
> > > > err_test:
> > > > MsgBox Error$
> > > > For Each objErr In Oracon.Errors
> > > > MsgBox objErr.Description
> > > > Next
> > > > Oracon.Errors.Clear
> > > > Resume Next
> > > >
> > > > ----End code sample.
> > > >
> > > > Is this incorrect?
> > >
> > > Properly handled errors in the database never produce errors in a
> > front-end.
> > > That you are handling errors as you are indicates the database is not
> > handling
> > > them: Nothing more.
> > >
> > > Daniel Morgan
> > >
> > >
> >
> > I only mentioned error handling at all (On Error Goto!) to emphasize
that
> it
> > is so difficult in this app to be sure that a lock is on or off, it is
> > necessary to be ready for an error. Whether it is handled in VB or
Oracle
> or
> > by careful routing of the spaghetti or at all is beside my point.
> >
> > The code I walked into looks something like the following. Even if you
> have
> > never heard of Oracle, you can see that this is a bad idea.
> >
> > (If I might borrow the VB code context of an earlier post)
> >
> > '(__calling code__)
> > Set Oracon = CreateObject("ADODB.Connection")
> > Oracon.ConnectionString = "blah, blah, not important"
> > Oracon.Open
> > Set cmd.ActiveConnection = Oracon
> > cmd.ActiveConnection.BeginTrans
> > cmd.CommandText = _
> > "{Begin Select colName From tblName For Update; End;}"
> > blnLockIsOn = True
> > 'Do stuff
> > 'call other code
> >
> >
> > '(__called code__)
> > 'rollback to end "select for update" lock
> > 'so this routine can access the data
> > If blnLockIsOn then
> > cmd.ActiveConnection.RollbackTrans
> > blnLockIsOn = False
> > End If
> > '
> > 'Do stuff,
> > 'including selects, updates, inserts, whatever
> > '
> > cmd.ActiveConnection.BeginTrans
> > cmd.CommandText = _
> > "{Begin Select colName From tblName For Update; End;}"
> > blnLockIsOn = True
> > 'return to caller
> >
> >
> > '(__back to calling code__)
> > 'do stuff
> > 'call other code
> > 'setting and removing locks all the while until
> > 'eventually, somewhere, hopefully.
> > If blnLockIsOn then
> > cmd.ActiveConnection.RollbackTrans
> > blnLockIsOn = False
> > End If
> >
> > So, given that this is bad, I want to know how best to lock what needs
to
> be
> > locked, if it needs to be locked at all (if not, I don't), in such a way
> as
> > to assure reasonable security (or concurrency, or whatever - you Oracle
> > types can help me out here) for the data while simplifying the code and
> code
> > maintenance until such time as this company Does The Right Thing and
> > rewrites the entire code base, carefully coached by duely qualified
Oracle
> > gurus. That is all.
> >
> > I feel like, on the whole, I am getting the run around here. I can
> > appreciate that I may be ignorant of the scope of my ignorance, but I
have
> > to start somewhere, and that has been by reading as much of the
plentiful
> > documentation available to me as I can and posting what I had hoped
would
> be
> > taken as a well worded question. Since I cannot hire a consultant and
> > because the company is not likely to hire an Oracle consultant soon, at
> > least not for this problem, one that they have been willing to live
with,
> I
> > will continue reading. If anyone here has any more helpful pointers,
such
> as
> > the list noted by Daniel, and the "Oracle Concurrency Model" noted by
> Simeon
> > Rice, I'll make good use of it.
> >
> > Thanks,
> > Bob Kilmer
> >
> >

>
>

> I didn't comprehend your situation the first time, so let me make sure I
> understand.
>

> 1. You have a set of client workstations
> 2. One client accesses data for update (the for update is so no one else
> can access it)
> 3. other client workstations [may] try to access that data, and your code
> (at least maintenance wise) uses a flag that says someone else is updating
> the record so exit the procedure and rollback any transactions (and
probably
> gives a try later message). (The flags are actually in the VB code?)
> 4. You want to know what to do about these flags/locks to clean the code?
>

> The simple answer is that oracle has exceptional row level locking (unlike
> sql server).
>

> Thus,
>

> you could eliminate the flagging altogether.
>
>

> ie:
> You could create a recordset
>

> select [cols] from emp [ + where clause] for update
>
>

> All the rows (and only those rows) retreived in the recordset will be
locked
> to that user that first accesses them
>

> When the second (and third, and forth, etc) users run that same sql, they
> will not get access to those rows until the previous recordset releases
> them. Oracle handles all the locking for you. You have to do nothing.
The
> subsequent client ws (workstation) just waits for the previous client ws
to
> complete.(Oracle's row level locking is so fine grained, that user1 can
get
> one set of rows, lock them, and user 2 can get a different set of rows and
> lock them without affecting either user --as long as the recordsets
> (cursors, in oraclespeak) do not access any of the rows in another active
> cursor).
>

> However, there is one potential problem:
>

> The second, third, (...) client ws all 'hang' (Not Responding, in the task
> manager), waiting for the previous recordset to clear when one of the
> retreived rows is already locked by a previous recordset. The only way to
> get the machine to 'respond' is end the task or wait for the previous user
> to commit, (update in some cases) or rollback. In the real world, this
can
> be a problem: ie: the first user gets the records, two other users
> subsequently execute the same query, the workstation hangs on users 2 and
3
> (because the first user has them), and during this process, user 2 decides
> to get a cup of coffee and restroom, and then gets side tracked. User 1
> releases the recordset, and because the locks are created and released
FIFO,
> user 3 (who really needs the records) has to wait and wait until user 2
> gets back or someone kills the session.
>

> If it were me, I would not mess with the flags you are talking about
> (impossible to read and maintain and debug). Let oracle handle the
locking,
> and force the person in charge to enforce the no walking away rules.
>

> If it is really a problem, and you get 'Not Responding' complaints, I'm
> afraid you do need some redesign. (and not of the flag sort)--(and, btw,
in
> this case the begin-commit trans is essential, as you will have users that
> will do the ctrl-alt-delete and end the task after a millisecond passes).
>
>

> Also, Oracle is smart enough not to allow the same application to lock
> itself out (deadly embrace). (however, if you execute the same for update
> query in a vb6 app and sqlplus on the same workstation at the same time,
> oracle will lock the records appropriately!)
>

> Hopefully this helped a little.
>

> E
>
>
>

Another possible scenario I came up with is that you are trying to lock out certain rows for the application logic (outside of the database) for longer periods of time and you need to prevent other users from accessing the rows, but not halting production processing (for update when accessed by multiple users on the same rows, as I said earlier, will halt processing until the locks are released)

I usually add a column called session_id to the table, maintain a unique session_id for each client ws and update the session_id with the ws's session id when it's in use, and null it when it frees. The sql I use for processing includes select... where... and session_id is null.

For a particular process, we have a picklist type app with some old batch handheld devices, assign certain pulls to a hand held terminal (so that other hand helds don't get the pull request), and then upload the results for the assigned pulls (by session id) when the user/handheld device is finished processing them (along with updating any discreps).

We also have reportage on session_id so that admins can tell which device is assigned the parts (in case something gets 'hot')

That way the rows are locked for what I need (but not the table) and processing can continue without 'hanging' everyone else

Again, I think this is clearer and easier to maintain than coded flags. Received on Mon Apr 21 2003 - 08:06:40 CDT

Original text of this message

HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US