Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi Datenbank per Code umbenennen (https://www.delphipraxis.net/113626-datenbank-per-code-umbenennen.html)

OG Karotte 11. Mai 2008 22:25

Datenbank: SQLite • Version: 3.5.8 • Zugriff über: ZEOS

Datenbank per Code umbenennen
 
Hallo zusammen,

ich versuche eine Datenbank per Code umzubenennen. Hierzu schließe ich die Datenbank per
Delphi-Quellcode:
Db.Disconnect
um sie anschließend per
Delphi-Quellcode:
RenameFile(OldName, NewName)
umzubenennen. Dummerweise meldet Windows, das die Datei nicht umbenannt werden kann, da sie noch in Benutzung sei.

Ich vermute, das das Disconnect nicht ganz ausreicht, ich möchte aber auch das Datenbankobjekt nicht freigeben / zerstören, um anschließend die Datei umzubenennen und unter dem neuen Namen wieder zu erstellen.
Gibt es noch andere Möglichkeiten???

Nuclear-Ping 11. Mai 2008 22:35

Re: Datenbank per Code umbenennen
 
Versuch mal folgendes:

1.)
SQL-Code:
RENAME DATABASE[list=1] TO <newname>;
2.)
SQL-Code:
CREATE DATABASE <newname> FROM TEMPLATE[list=1];
DROP DATABASE oldname;
Sonst verhindert wahrscheinlich der ganze Prozess deiner Anwendung das Umbenennen der Datenbank. In dem Falle wird dir nichts anderes übrig bleiben, als die Datenbank irgendwie vor dem Start deiner eigentlichen Anwendung umzubenennen, zB per Batch-Script.

OG Karotte 11. Mai 2008 22:57

Re: Datenbank per Code umbenennen
 
Funktioniert leider nicht, da SQLite diese Befehle leider nicht kennt :(

omata 11. Mai 2008 23:10

Re: Datenbank per Code umbenennen
 
Ich machte so etwas ähnliches mit ZEOS + Embedded Firebird.

Da mache ich auch nur ein Disconnect dann allerdings ein CopyFile, das geht aber problemlos.

Eventuell liegt es ja auch an SQLite.

Das ist auf jeden Fall sehr mysteriös.

Gruss
Thorsten

OG Karotte 11. Mai 2008 23:23

Re: Datenbank per Code umbenennen
 
Nun, an ein Kopieren der Datei habe ich auch schon gedacht, empfinde dies aber als etwas umständlicher als ein einfaches umbenennen...

... ich werde es aber trotzdem mal probieren (und wenn es nur als Zwischenlösung ist).

... funktioniert auch nicht (gleiches Ergebnis wie bei Rename) :wall:

sx2008 12. Mai 2008 02:34

Re: Datenbank per Code umbenennen
 
Zitat:

Zitat von OG Karotte
Dummerweise meldet Windows, das die Datei nicht umbenannt werden kann, da sie noch in Benutzung sei.

Ein beliebter Fehler ist, dass die Datenbank noch von der Delphi IDE offengehalten wird,
da man ja zur Entwicklungszeit Tabellen oder Queries (manchmal auch unbewusst) aktiv schalten kann.

OG Karotte 12. Mai 2008 09:07

Re: Datenbank per Code umbenennen
 
Zitat:

Zitat von sx2008
Ein beliebter Fehler ist, dass die Datenbank noch von der Delphi IDE offengehalten wird,
da man ja zur Entwicklungszeit Tabellen oder Queries (manchmal auch unbewusst) aktiv schalten kann.

Leider ist auch dies nicht der Fall, da ich keine Datensensitiven Elemente und daher auch keine Designtime-Connection verwende. Der Fehler tritt ebenfalls bei geschlossener IDE auf.

Im Prinzip mache ich nichts weiter als:
  • Datenbank anlegen (geschieht unter ZEOS automatisch bei setzen des Datenbank Namens und dem ersten Zugriff auf selbige)
  • Tabellen und ein paar Grunddaten per SQL-Script anlege (ExecSQL)
und dann ein Datenbank.Disconnect ausführe um die DB umbenennen zu können. => Error 32

Nuclear-Ping 12. Mai 2008 09:17

Re: Datenbank per Code umbenennen
 
Wie schon gesagt wird deine ganze Anwendung der Prozess sein, der den Zugriff verhindert. Vielleicht ist ZEOS oder dein SQlite Server auch nur so eingerichtet, dass sie die Datei so öffnen, dass kein geteilter Zugriff ermöglicht wird?

Daher der Vorschlag:
- Anwendung erzeugt Batch, startet Batch und beendet sich selber
- Batch:
--- Warten, bis Anwendung wirklich zu ist
--- REN[list=1] <newdb>
--- Anwendung.exe
- Anwendung löscht Batch

OG Karotte 12. Mai 2008 10:20

Re: Datenbank per Code umbenennen
 
Zitat:

Zitat von Nuclear-Ping
Wie schon gesagt wird deine ganze Anwendung der Prozess sein, der den Zugriff verhindert. Vielleicht ist ZEOS oder dein SQlite Server auch nur so eingerichtet, dass sie die Datei so öffnen, dass kein geteilter Zugriff ermöglicht wird?

Daher der Vorschlag:
- Anwendung erzeugt Batch, startet Batch und beendet sich selber
- Batch:
--- Warten, bis Anwendung wirklich zu ist
--- REN[list=1] <newdb>
--- Anwendung.exe
- Anwendung löscht Batch

Danke für den Vorschlag, aber das ist mir zu umständlich für diese "einfache" Funktionalität. Dann laß ich lieber kein Umbenennen zu :(

Was ZEOS oder SQLite angeht: Mir ist dort keine Einstellung bekannt, die dieses Verhalten ändert.

...

Ich hab' jetzt mal ein Testprojekt aufgesetzt und siehe da es funktioniert (Ich war mir auch sicher das es vorher schonmal funktioniert hat). Stellt sich nun also die Frage warum funktioniert es nicht im eigentlichen Projekt??? :wall:

Hier mal ein wenig Code und Hintergrund:

Ich öffne die Datenbank so:
Delphi-Quellcode:
function TMasterDB.OpenCatalog(AOwner: TComponent; ACatalogName: Widestring): TCatalog;
begin
  Result := TCatalog.Create(AOwner, ACatalogName, '', False);

  if Assigned (Result) then
      FListedCatalogs.Add(Result); // TObjectlist (zur Verwaltung der Datenbankknoten im VST
                                   //(Anzeigeelemnent für DB und Datensätze)
end;
Definition TCatalog:
Delphi-Quellcode:
type
  TCatalog = class(TDataModule)
    ConDB: TZConnection;
  private
    fCatID     : Cardinal;       // MasterDB-ID
    fCatName   : WideString;     // Name (VST OnGetText)
    fCatDescr  : Widestring; // Beschreibung (VST OnHint)
    procedure SetCatalogID(const Value: Cardinal);
    procedure SetCatalogName(const Value: Widestring);
    procedure SetCatalogDescr(const Value: Widestring);
    function CreateCatalog: Boolean;
  public
    property CatalogID: Cardinal read fCatID write SetCatalogID;
    property CatalogName: Widestring read fCatName write SetCatalogName;
    property CatalogDescr: Widestring read fCatDescr write SetCatalogDescr;

    constructor Create(AOwner: TComponent; AName: WideString; ADesc: WideString;
      New: Boolean = True); overload;
    destructor Destroy; override;

    function Open: Boolean;
    function Close: Boolean;
end;

{==============================================================================
  Datenbankverbindung herstellen (Vorhandene DB)
===============================================================================}
function TCatalog.Open: Boolean;
begin
  Result := False;
  // Katalog öffnen (verbinden)
  with conDB do
    try
      Database := pyPathes.DBPath + fCatName + pyCatDBExt;
      Connect;
      Result := Connected;
    finally
//
    end;
end;

{==============================================================================
  Datenbankverbindung schliessen
===============================================================================}
function TCatalog.Close: Boolean;
begin
  ConDB.Disconnect;
  Result := not ConDB.Connected;
end;

{==============================================================================
  Anlegen Neue DB
===============================================================================}
function TCatalog.CreateCatalog: Boolean;
var
  OK: Boolean;
begin
  Result := false;
  OK := False;
  // Katalog anlegen
  with conDB do
    try
      Database := pyPathes.DBPath + fCatName + pyCatDBExt;
      Connect;
      with TZQuery.Create(nil) do
        try
          Connection := conDB;
          // Tabelle Prog internas
          SQL.Add('CREATE TABLE [tblInternal] (');
          SQL.Add('[IntType] INTEGER NOT NULL DEFAULT 1,');    // MasterDB(0) oder Katalog(1) oder ReferenzDB(2)
          SQL.Add('[IntProgVersion] TEXT NOT NULL,'); // Erstellt mit Pythia Version
          SQL.Add('[IntDBVersion] TEXT NOT NULL,');  // Datenbankversion
          SQL.Add('[IntSQLVersion] TEXT NOT NULL,'); // Erstellt mit SQLite Version
          SQL.Add('[IntMagic] TEXT NOT NULL,');       // Magicnr der MasterDB (wird bei jedem ERZEUGEN der MasterDB neu angelegt)
          SQL.Add('[IntCatCreation] DATE NOT NULL,');       // Katalog erstellt am/um
          SQL.Add('[IntCatLastChange] DATE NOT NULL,');       // Letzte Änderungen im Katalog am/um
          SQL.Add('[IntCatDescr] TEXT NULL');       // Beschreibung des Katalogs
          SQL.Add(');');
          // 
          // Erzeugung weiterer Tabellen
          //   
          // Zeitpunkt der Erstellung und Versionsnr. eintragen
          SQL.Add('INSERT INTO tblInternal');
          SQL.Add('(IntProgVersion, IntDBVersion, IntSQLVersion, IntMagic, IntCatCreation, IntCatLastChange)');
          SQL.Add(' VALUES (:AVersion, :ADBVersion, :ASQLVersion, :AMNr, DATETIME(''now''), DATETIME(''now''));');
          ParamByName('AVersion').Value := pyVersion.ProgVersion;
          ParamByName('ADBVersion').Value := pyVersion.DBVersion;
          ParamByName('ASQLVersion').Value := pyVersion.SQLiteVersion;
          ParamByName('AMNr').Value := pyVersion.MagicNo;
          ExecSQL;
          OK := True;
        finally
          Free;
        end;
    finally // ConDB
      Disconnect;
    end;
    Result := OK; // Alles gut gelaufen
end;

{==============================================================================
  Erzeugen des Objektes und Anlegen des Katalogs
===============================================================================}
constructor TCatalog.Create(AOwner: TComponent; AName: WideString;
                            ADesc: WideString; New: Boolean);
begin
  inherited Create(AOwner);
  fCatName := AName;
  fCatDescr := ADesc;
  if not New then
    Open
  else begin
    CreateCatalog;
  end;
end;

destructor TCatalog.Destroy;
begin
  inherited Destroy;
end;
Hier die Routine zum Umbenennen:

Delphi-Quellcode:
function TMasterDB.RenameCatalog(ACatalog: TCatalog;
  ANewCatName: Widestring): Boolean;
var
  ACatID: Cardinal;
  fo, fn: WideString;
begin

  Result := False;

  fo := pyPathes.DBPath + ACatalog.CatalogName + pyCatDBExt;
  fn := pyPathes.DBPath + ANewCatName + pyCatDBExt;
  // Umbenennen nur möglich wenn Verbindung getrennt
  // (Öffnen erst wieder bei einer Aktion am Katalog)
  if ACatalog.Close then begin
    if WideRenameFile(fo, fn) then begin
      // Datei erfolgreich umbenannt => auch in MasterDB umbenennen?
      if RenameCatalogEntry(ACatalog.CatalogID, ANewCatName) then begin
        ACatalog.CatalogName := ANewCatName;
      end
      else
        WideRenameFile(fn, fo); // Umbenennen in MasterDB fehlgeschlagen -> zurück zum ursprünglichen Namen
    end;
  end;
end;
Die Datenbanken (Typ TCatalog) werden über eine Objektliste verwaltet. D.h. jedesmal wenn ein TCatalogObject erzeugt wird, wird es der Objektliste FlistedCatalogs hinzugefügt. Wie oben zu sehen ist, hat jedes TCatalog auch eine Eigenschaft CatalogName, die im VST zur Anzeige benutzt wird.

Vor der Umstellung gab es keine TObjectlist, sondern nur eine StringList mit den Namen der vorhandenen DB's. Die Daten für das VST, die nun aus dem Object TCatalog kommen, kamen vorher aus Record's.

Liegt vielleicht hier der Hase im Pfeffer begraben??? Klappt es wegen der Objectlist nicht??? Müsste nur 'ne Kleinigkeit am Code / Design umgestellt werden ???

Ach ja, es gibt noch eine MasterDB, in der die Namen und eine ID pro DB verwaltet werden. Aus diesem Modul kommt auch der Aufruf
Delphi-Quellcode:
RenameCatalog(ACatalog: TCatalog;
  ANewCatName: Widestring)

OG Karotte 12. Mai 2008 16:44

Re: Datenbank per Code umbenennen
 
Zitat:

Zitat von OG Karotte
Stellt sich nun also die Frage warum funktioniert es nicht im eigentlichen Projekt??? :wall:

Weil der Fehler ein Fehler zwischen den Kopfhörern war: :wall: :wall: :wall:

Wer eine DB "zweimal" öffnet sollte sie auch entspr. schliessen... :duck:

Daher: gelöst


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:24 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz