Search code examples
oracleblob

Oracle SQL and BLOBs: why do I need to lock the row of a BLOB am I modifying if I'm in a transaction anyway?


This question could reflect some misunderstanding from my part, but there's something that is puzzling me for days.

According to the Oracle docs (19c), it explicitly recommends to lock a row of a table containing a BLOB if I'm planning to modify it. However, I don't understand why do I need to do that if the first write operation will start a transaction.

If, for example, I update some normal column of some row of some table, if I'm not wrong, that specific row will be automatically locked to prevent updates from other transactions. But, why that doesn't apply for LOBs?

The only solution I see here is that writting to a LOB through a locator doesn't start a transaction, which makes no sense because at the same time the docs says that opening a BLOB will give me read-consistency until the locator is closed. In summary, I don't understand what bad things could happen if I'm reading and/or writting a BLOB through a locator without locking the table. The information feels a bit contradictory to me.


Solution

  • The documentation is speaking about the use of OCI or DBMS_LOB, the low-level API for manipulating LOBs directly (not using SQL):

    Persistent LOBs: You must lock the row you are selecting the LOB from prior to updating a LOB value if you are using the PL/SQL DBMS_LOB Package or OCI. While the SQL INSERT and UPDATE statements implicitly lock the row, locking the row can be done explicitly using the SQL SELECT FOR UPDATE statement in SQL and PL/SQL programs, or by using an OCI pin or lock function in OCI programs.

    Calls to DBMS_LOB write directly to specified byte positions with an open handle/pointer. This doesn't invoke the SQL engine at all, so doesn't take out row locks. However, if you are using SQL to INSERT, UPDATE, etc. a LOB value, then you are getting row locking already and don't need to worry about it.

    If you are using DBMS_LOB calls on a persistent (non-temporary LOB) and therefore have a concurrency concern should two sessions attempt to write to the same LOB at the same time, hopefully you've selected the LOB locator with FOR UPDATE or have just inserted it (or updated it) with EMPTY_BLOB()and obtained the pointer with RETURNING and so in either case (assuming you haven't yet committed), you already have a locked row to safely use dbms_lob calls on without interference from other sessions who try to do the same thing. If it's a temporary LOB then there is no concurrency concern - your session is the only session that can write to it.

    So, in your situation, your update of the row will block any other session from updating that same row. Since that prevents the code from proceeding to any subsequent DBMS_LOB calls on a LOB belonging that row, that will serialize your modification and all is good. What you have to avoid is using a normal SELECT without the FOR UPDATE clause and then using the LOB locator obtained to do DBMS_LOB writes, if there's any chance another session might be doing the same thing at the same time to the same LOB.