delphifiredacdelphi-10.2-tokyo

How to solve [FireDAC][Phys][SQLite] ERROR: unable to open database file, When app is installed in programFiles?


I developed a tool using Firedac with the database as SQLite.

after finishing the project and making an installer (InnoSetup) I get an error

[FireDAC][Phys][SQLite] ERROR: unable to open database file

when I launch the app (double click).

This is the connection parameters I use

constructor TDbInteract.Create(const aDatabasePath: string; const aOnNeedCredentials: TOnNeedCredentials);
var
  aParams: array of string;
begin
  if not TFile.Exists(aDatabasePath) then
    raise Exception.Create('Database file not found');

  aParams := ['DriverID=SQLite',
              'Database=' + aDatabasePath,
              'OpenMode=CreateUTF16',
              'LockingMode=Normal',
              'JournalMode=WAL',
              'StringFormat=Unicode',
              'Synchronous=Full',
              'UpdateOptions.LockWait=True',
              'BusyTimeout=30000',
              'SQLiteAdvanced=temp_store=MEMORY;page_size=4096;auto_vacuum=FULL'];
  InitiateResource(aParams, aOnNeedCredentials);
end;

procedure TDbInteract.InitiateResource(const aParams: array of string; const aOnNeedCredentials: TOnNeedCredentials);
var
  I: Integer;
  Credentials: TStringDynArray;
begin
  FRowsAffected := 0;
  FIsForeignKeyHonored := True;
  FOwnsResultDataSets := True;
  FDataSetContainer := TDataSetContainer.Create(nil);

  FConnection := TFDConnection.Create(nil);
  try
    for I := Low(aParams) to High(aParams) do
    begin
      FConnection.Params.Add(aParams[I]);
    end;

    if Assigned(aOnNeedCredentials) then
    begin
      aOnNeedCredentials(Self, Credentials);

      for I := Low(Credentials) to High(Credentials) do
      begin
        FConnection.Params.Add(Credentials[I]);
      end;
    end;

    FConnection.Open;
  except
    raise;
  end;
end;

**Identified problems:

  1. I read somewhere (do not remember the page I was in) that SQLite engine requires full lock on the directory that it wants to write to. and this is the problem. How ever I run the tool as invoker and my account is an admin so that is not a problem. Also I have the same tool written in c# and this problem never occurs.

Solutions I found:

  1. Run the tool as administrator
  2. Do not Install the tool in ProgramFiles directory

I really don't like these solutions. and would like to run my tool from program Files directory as it is part of a bigger project.

Note: The database file is in programdata directory. It is created by the tool (this works).

Edit: I just tried putting the DB file in C:\Users\Nacereddine\AppData\Roaming\MyTool And I still have the same problem when the tool is installed in C:\Program Files (x86)\MyTool

This how I create the DB file

class procedure TDbInteract.CreateSQLiteDb(const aDatabasePath: string; const aTables: TStringDynArray);
var
  I: Integer;
  aParams: array of string;
  aConnection: TFDConnection;
begin
  aParams := ['DriverID=SQLite',
              'Database=' + aDatabasePath,
              'OpenMode=CreateUTF16',
              'LockingMode=Normal',
              'JournalMode=WAL',
              'StringFormat=Unicode',
              'Synchronous=Full',
              'UpdateOptions.LockWait=True',
              'BusyTimeout=30000',
              'SQLiteAdvanced=temp_store=MEMORY;page_size=4096;auto_vacuum=FULL'];

  aConnection := TFDConnection.Create(nil);
  try
    for I := Low(aParams) to High(aParams) do
    begin
      aConnection.Params.Add(aParams[I]);
    end;

    aConnection.Open();

    for I := Low(aTables) to High(aTables) do
    begin
      aConnection.ExecSQL(aTables[I]);
    end;
  finally
    aConnection.Close;
    aConnection.Free;
  end;
end;

Note: I do not know if this makes any difference but the Db file is encrypted.


Solution

  • Sorry for the trouble folks.

    The problem was that we had a Localization db file installed with the tool in ProgramFiles.

    What made me exclude that from my investigation is that, when opening this file I set the OpenMode to ReadOnly

    FConnection.Params.Add('OpenMode=ReadOnly');
    

    but as I said before in my question SQLite engine requires full access to the folder containing the db file so it preforms a lock on it (still did not find the page I read this on).

    I checked this by playing around with open modes and debugging the tool each time. once I changed the permissions of both the file and the directory the error was gone.

    at the end I decided to move the localization file to the programData directory with the main db file and all is well.

    I realized (Thank you for this @Ken and @David) that the programData directory also requires admin permissions to write to, and therefore I will move the db files to a more appropriate dir (i.e Users).

    What is useful from this problem is that even if you connect to the Sqlite db file with OpenMode=ReadOnly, you still need write access for the path to that file.