【问题标题】:Error using SQLite ATTACH - DETACH in Delphi在 Delphi 中使用 SQLite ATTACH - DETACH 时出错
【发布时间】:2016-03-15 14:51:11
【问题描述】:

我在 SQLite 中使用 Delphi (Firedac) 遇到了一个无法解决的 ATTACH 和 DETACH 问题。

我连接了一个数据库文件并附加了第二个:

FDConnection1.ExecSQL('ATTACH DATABASE "' + Import_DB_filename + '" AS IMPORTDB;');

其中,“Import_DB_filename”变量包含数据库文件的完整路径和文件名。

这工作正常,我可以通过 FireDac 查询访问连接内的两个数据库,并且可以毫无问题地进行编码。 但是,分离时出现问题:

FDConnection1.ExecSQL('DETACH DATABASE IMPORTDB;');

在调试模式下,总是报错:

调试器异常通知
E Project My_Program.EXE 引发异常类 $C0000005 并带有消息“在 0x00405d7b 处的访问冲突:读取地址 0x00000000”。

显然内存分配出了问题,因为调试器在GETMEM.INC 中的(汇编)函数SysFreeMem(P:Pointer): Integer; 中停止。

无论我尝试什么,错误都会持续存在并与内存泄漏相关联,最终导致编译器崩溃(Delphi Seattle Enterprise)。

即使在不传递任何代码的情况下附加和随后分离数据库也会导致相同的错误。

(FDconnection: 锁定模式 = lmNormal; JournalMode = jmOff 或 jmWALL 或 jmdelete)

我确实希望你能帮助我解决这个长期存在的问题。

【问题讨论】:

  • 可以肯定:所以你已经有一个合适的Firedac connection set up,现在你想使用SQLite ATTACH向当前连接添加另一个数据库文件?请edit您的问题包括Delphi版本。
  • 为什么需要对两个数据库文件使用相同的 FDConnection?为什么不为他们每个人建立一个单独的连接。
  • 说实话,我不确定我是否已经尝试过了。我会查一下。感谢您的建议。
  • 我想使用单个 FDConnection,因为我使用“insert...select”语句将数据从第二个数据库复制到现有数据库中。
  • 好吧,无论如何,如果您在代码中生成 Sql 语句,您都可以这样做。

标签: sqlite delphi delphi-10-seattle


【解决方案1】:

如果你运行下面的项目,你应该会发现:

  • 您可以使用单独的 FDConnections 和 FDTables 非常愉快地访问两个 Sqlite 数据库。

  • 您可以使用 FireDAC FDDataMove 组件将数据从一个数据库中的表移动到另一个数据库中的同名表。

代码:

unit BatchMoveu;

interface

[...]

type
  TForm3 = class(TForm)
    FDConnection1: TFDConnection;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    DBNavigator1: TDBNavigator;
    FDGUIxWaitCursor1: TFDGUIxWaitCursor;
    FDPhysSQLiteDriverLink1: TFDPhysSQLiteDriverLink;
    Button1: TButton;
    FDTable1: TFDTable;
    FDConnection2: TFDConnection;
    DataSource2: TDataSource;
    DBGrid2: TDBGrid;
    btnBatchMove: TButton;
    FDDataMove1: TFDDataMove;
    FDTable2: TFDTable;
    procedure btnBatchMoveClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    procedure PopulateTable1;
    procedure TestDataMove;
  public
    procedure CreateDatabase(DBName : String; FDConnection : TFDConnection;
      FDTable : TFDTable);
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

const
  DBName1 = 'd:\delphi\code\sqlite\db1.sqlite';
  DBName2 = 'd:\delphi\code\sqlite\db2.sqlite';

procedure TForm3.Button1Click(Sender: TObject);
begin
  CreateDatabase(DBName1, FDConnection1, FDTable1);
  CreateDatabase(DBName2, FDConnection2, FDTable2);

  PopulateTable1;

  FDTable2.Open;

end;

procedure TForm3.CreateDatabase(DBName : String; FDConnection : TFDConnection;
  FDTable : TFDTable);
var
  AField : TField;
  i : Integer;
begin
  if FileExists(DBName) then
    DeleteFile(DBName);

  AField := TLargeIntField.Create(Self);
  AField.FieldName := 'ID';
  AField.DataSet := FDTable;
  AField.Name := AField.DataSet.Name + 'IDField';

  AField := TWideStringField.Create(Self);
  AField.Size := 80;
  AField.FieldName := 'Name';
  AField.DataSet := FDTable;
  AField.Name := AField.DataSet.Name + 'NameField';

  FDConnection.Params.Values['database'] := DBName;
  FDConnection.Connected:= True;
  FDTable.CreateTable(False, [tpTable]);
end;

procedure TForm3.PopulateTable1;
var
  i : Integer;
begin
  FDTable1.Open;

  for i:= 1 to 1000 do begin
    FDTable1.InsertRecord([i, 'Row ' + IntToStr(i)]);
  end;
  FDTable1.Close;
  //FDConnection1.Commit;

  FDTable1.Open;
end;

procedure TForm3.TestDataMove;
var
  Item : TFdMappingItem;
begin
  Item := FDDataMove1.Mappings.Add;
  Item.SourceFieldName := 'ID';
  Item.DestinationFieldName := 'ID';

  Item := FDDataMove1.Mappings.Add;
  Item.SourceFieldName := 'Name';
  Item.DestinationFieldName := 'Name';

  FDDataMove1.Source := FDTable1;
  FDDataMove1.Destination := FDTable2;
  FDDataMove1.Options := FDDataMove1.Options - [poOptimiseSrc];
  FDDataMove1.Execute;

  FDConnection2.Connected := False;
  FDTable2.Open;
end;
procedure TForm3.btnBatchMoveClick(Sender: TObject);
begin
  TestDataMove;
end;

procedure TForm3.FormDestroy(Sender: TObject);
begin
  FDConnection1.Close;
end;

end.

【讨论】:

  • 谢谢你,MartynA,
  • 谢谢 MartynA,您让我相信使用两个 FDConnection 组件是规避使用 ATTACH 和 DETACH 的好方法。我已经相应地更改了我的代码。但是,错误仍然存​​在。显然,它与附加无关,而与内存分配有关。我一定在某个地方搞砸了。也许您可以提示我如何解决此类错误。我已经安装了 MadExcept,但它没有给我任何线索。不幸的是,十六进制地址对我来说就像中文。
  • 好吧,“读取地址 0x00000000”表明某些东西没有正确初始化,可能是在调用 .Create() 之前正在访问的 Delphi 对象。确保在项目选项中启用了“使用调试 DCU”,然后进行项目构建并运行。发生错误时,在 IDE 中转到 View |调试窗口 |调用堆栈,然后您应该能够查看堆栈并查看异常发生的位置。
猜你喜欢
  • 1970-01-01
  • 2013-05-17
  • 2011-04-07
  • 2014-12-18
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
  • 2011-12-22
  • 1970-01-01
相关资源
最近更新 更多