Question Julian Matthews · Apr 18, 2024

Does Embedded SQL support Table Hints?

Hi everyone.

I have a function that may end up being called from a number of transformations at the same time, and within the function there's some Embedded SQL to first check if a local table has an entry, and then adds the entry if it doesn't exist.

To prevent a race condition where the function is called by two transformations and they both end up attempting to insert the same value, I'm looking to use the table hint "WITH TABLOCK" on the insert, but this seems to be failing the syntax checks within vscode.

Are table hints supported with embedded sql?

If not, is there a way to prevent the scenario from unfolding? The code I'm working with for the function is:

Class Example.Functions Extends Ens.Rule.FunctionSet
{

ClassMethod VisitIDShortener(inVisitID As%String, SourceSystem As%String) As%String
{
    //Firstly,  check to see if the table we're working with exists | This should only be an issue on first deployment to a new environmentSet tSC = $SYSTEM.SQL.Schema.TableExists("Example.VisitIDLookup")
    If tSC'=1{
        //Buid table if it doesn't exist
        &SQL(CREATETABLE Example.VisitIDLookup
            (SourceSystem varchar(10), SourceVisitID varchar(250), TargetVisitID varchar(15))
        )
    }
    //Secondly, check to see if the Global we're using for our increment exist, and create if it doesn't existSet GBLCHK = $DATA(^$GLOBAL("^EMPVisitID"))
	If GBLCHK = 0{
		Set^EMPVisitID = 0
		}

    //Check table for entry
    &SQL(Select TargetVisitID into :pTargetID
        FROM Example.VisitIDLookup
        Where SourceVisitID = :inVisitID And SourceSystem = :SourceSystem
    )
    //If SQLCODE is 0, we have an entry and we wnat to simply return the resultIf SQLCODE=0 {
        Set outVisitID = pTargetID
    }
    Else{
        //Increment the GlobalDo$INCREMENT(^EMPVisitID)
        
        //Use incremented value for output (with a prefix)Set outVisitID = "TIEGEN"_^EMPVisitID//Add new entry to table
        &SQL(InsertINTO Example.VisitIDLookup
            Set SourceSystem=:SourceSystem, SourceVisitID = :inVisitID, TargetVisitID = :outVisitID
        )
    }

    Quit outVisitID
}

}

Comments

Nick Petrocelli · Apr 18, 2024

I'm not sure about TABLOCK specifically, but there are a couple other features that might help you solve this:

First, there is the LOCK TABLE statement: https://docs.intersystems.com/iris20241/csp/docbook/Doc.View.cls?KEY=RS…

There's also the objectscript LOCK command: https://docs.intersystems.com/iris20241/csp/docbook/Doc.View.cls?KEY=RC…

You could use either of these to block the method while other instances of it are running and prevent the race condition. I'm not sure how efficient this method would be compared to TABLOCK, but it would fix the bug.

I hope this helps!

0
Julian Matthews  Apr 18, 2024 to Nick Petrocelli

Thanks Nick.

Having done some tests (and reading the link shared), the table locking still allows SELECT statements to be executed so the objectscript locking will be my approach.

0
Julius Kavay · Apr 18, 2024

In the above code just insert a Lock and Unlock, and the problem is solved

//Buid table if it doesn't existlock +^LockOutOthers// or any other name you wish// No timeout! At this point everybody has to wait// as long as the current job does a table check/update
  &SQL(
  ...
  ...
  }
  
  lock -^LockOutOthers  // let the others in
  
  Quit outVisitID
  
0
Julian Matthews  Apr 18, 2024 to Julius Kavay

Thanks Julius - exactly what I'm looking for!

0