SQL Native Client Version

I wrote a solution to log blocking in NAV with the help of Microsoft SQL Native Client.  I wanted to be able to use this solution for a client but then ran into a problem with the SQL Native Client.  There are three versions out there and the connection string must include the version installed on the client machine.

The first step is to find the “Program Files” folder
[code] LOCAL PROCEDURE GetProgramFilesPath@1200050010() ProgramFilesPath : Text[1024];
VAR
EnviormentPath@1200050000 : Code[50];
BEGIN
IF ISCLEAR(SystemShell) THEN
CREATE(SystemShell);
EnviormentPath := ‘PROCESS’;
SystemEnviroment := SystemShell.Environment(EnviormentPath);
ProgramFilesPath := SystemEnviroment.Item(‘ProgramW6432’);
IF ProgramFilesPath = ” THEN
ProgramFilesPath := SystemEnviroment.Item(‘ProgramFiles’);
END;[/code]and the System32 folder[code] LOCAL PROCEDURE GetSystemRootPath@1100408000() SystemRootPath : Text[1024];
VAR
EnviormentPath@1200050000 : Code[50];
BEGIN
IF ISCLEAR(SystemShell) THEN
CREATE(SystemShell);
EnviormentPath := ‘PROCESS’;
SystemEnviroment := SystemShell.Environment(EnviormentPath);
SystemRootPath := SystemEnviroment.Item(‘SystemRoot’) + ‘\System32’;
END;[/code]

Then I use the EXISTS function to check for the client version.

[code] LOCAL PROCEDURE OpenConnection@1200050006(ConnectAsUserID@1200050003 : Text[30];ConnactAsPassword@1200050002 : Text[30]);
VAR
MyServer@1200050001 : Record 2000000047;
MyDatabase@1200050000 : Record 2000000048;
ClientVersion@1100408000 : Text[30];
ProgramFilesPath@1100408001 : Text[50];
SystemRootPath@1100408002 : Text[50];
BEGIN
IF ISCLEAR(ADOConnection) THEN
IF NOT CREATE(ADOConnection) THEN
ERROR(Text001);

IF ISCLEAR(ADORecordset) THEN
CREATE(ADORecordset);

IF ISCLEAR(ADOStream) THEN
CREATE(ADOStream);

IF ADOConnection.State = 1 THEN
EXIT;

IF ISSERVICETIER THEN BEGIN

IF ISCLEAR(DomDoc) THEN
CREATE(DomDoc);

DomDoc.load(APPLICATIONPATH + ‘CustomSettings.config’);
DomNode := DomDoc.selectSingleNode(‘//appSettings/add[@key=”DatabaseServer”]’);
MyServerName := DomNode.attributes.item(1).text;

DomNode := DomDoc.selectSingleNode(‘//appSettings/add[@key=”DatabaseName”]’);
MyDatabaseName := DomNode.attributes.item(1).text;

END ELSE BEGIN
MyServer.SETRANGE("My Server",TRUE);
MyServer.FINDFIRST;
MyServerName := MyServer."Server Name";

MyDatabase.SETRANGE("My Database",TRUE);
MyDatabase.FINDFIRST;
MyDatabaseName := MyDatabase."Database Name";
END;

ProgramFilesPath := GetProgramFilesPath;

CASE TRUE OF
EXISTS(ProgramFilesPath + ‘\Microsoft SQL Server\90\SDK\Include\sqlncli.h’):
ClientVersion := ‘SQLNCLI’;
EXISTS(ProgramFilesPath + ‘\Microsoft SQL Server\100\SDK\Include\sqlncli.h’):
ClientVersion := ‘SQLNCLI10’;
EXISTS(ProgramFilesPath + ‘\Microsoft SQL Server\110\SDK\Include\sqlncli.h’):
ClientVersion := ‘SQLNCLI11’;
ELSE
BEGIN
SystemRootPath := GetSystemRootPath;
CASE TRUE OF
EXISTS(SystemRootPath + ‘\sqlncli.dll’):
ClientVersion := ‘SQLNCLI’;
EXISTS(SystemRootPath + ‘\sqlncli10.dll’):
ClientVersion := ‘SQLNCLI10’;
EXISTS(SystemRootPath + ‘\sqlncli11.dll’):
ClientVersion := ‘SQLNCLI11’;
ELSE
ERROR(Text002);
END;
END;
END;

IF ConnectAsUserID <> ” THEN
ADOConnection.Open(
STRSUBSTNO(‘Provider=%5;Server=%1;Database=%2;Uid=%3;Pwd=%4;’,
MyServerName,MyDatabaseName,ConnectAsUserID,ConnactAsPassword,ClientVersion))
ELSE
ADOConnection.Open(
STRSUBSTNO(‘Provider=%3;Server=%1;Database=%2;Trusted_Connection=yes;’,
MyServerName,MyDatabaseName,ClientVersion));

ADOConnection.CommandTimeout(0);
END;[/code]

where Text002@1100408002 : TextConst ‘ENU=Microsoft SQL Native Client not found;ISL=Microsoft SQL Native Client finnst ekki.’;

Job Queue stops when lock time out occurs

Running NAS with Job Queue will start a timer to process Job Queue Entries every two seconds. In the original code the Timer is disabled before checking the Job Queue Entries and then enabled again after the process. If NAS will not be able to read the Job Queue Entry then the function will exit without enabling the Timer and nothing will be processed.

The original code in Codeunit 448 is
[code htmlscript=”false”]HandleRequest()
JobQueueSetup.GET;
IF NOT JobQueueSetup."Job Queue Active" THEN
EXIT;

NavTimer.Enabled := FALSE;

ThisSessionIsActive := UpdateJobQueueSession(JobQueueEntry,FALSE);

CleanUpJobQueue;
COMMIT;

NavTimer.Enabled := TRUE;[/code]
I suggest that you will find a suitable period of time where everything in the Queue should be processed and change the behaviour. Do not disable the timer, just change the interval.  Here I change the interval to five minutes.

The replacement code would then be
[code htmlscript=”false”]HandleRequest()
JobQueueSetup.GET;
IF NOT JobQueueSetup."Job Queue Active" THEN
EXIT;

NavTimer.Enabled := FALSE;
NavTimer.Interval := 5 * 60 * 1000; // 5 min
NavTimer.Enabled := TRUE;

ThisSessionIsActive := UpdateJobQueueSession(JobQueueEntry,FALSE);

CleanUpJobQueue;
COMMIT;

NavTimer.Enabled := FALSE;
NavTimer.Interval := 2 * 1000; // 2 sec.
NavTimer.Enabled := TRUE;[/code] 

Batch Job to Quit if it is blocking

I have a batch job that is running regularly to adjust items.  Since this is running during working hours I want to stop the batch if it is blocking another user. This is the loop that my batch does.
[code htmlscript=”false”]FOR i := 1 TO 50 DO BEGIN
IF AmILocking(LockingForUserID) THEN BEGIN
COMMIT;
ERROR(Text001,LockingForUserID);
END;
AdjustItems;
END;[/code]
Text001 is “Locking for user id %1, quitting !” and AmILocking function is
[code htmlscript=”false”]AmILocking(VAR LockingForUserID : Text[30]) Locking : Boolean
Session.SETRANGE(Blocked,TRUE);
Locking := Session.FINDFIRST;
LockingForUserID := Session."User ID";[/code]
where Session is the virtual table Session as a local variable.

Blocking Log

Today I needed to see who was blocking another user and why.  I created a few objects to monitor the Session table and log the information.

I use ADO to connect to the database server to get the current SQL statement for each session to be able to see what is beeing locked.

Objects attached.

Blocking Log