3

My code does the following:

  1. Set up a design time target TFDConnection
  2. Create a run time source TFDConnection
  3. Drop all indices and tables in target
  4. Recreate those
  5. Copy all data from source to target
  6. Drop one of those tables (and indices), named TT_SYS_WEEKS
  7. Recreate it and fill it

The TFDConnections can be embedded Firebird or not. This works fine in all combinations except when both are embedded.

In step 6, after executing

DROP INDEX <OWNER>TT_I1_SYS_WEEKS
ALTER TABLE <OWNER>TT_SYS_WEEKS DROP CONSTRAINT TT_I0_SYS_WEEKS

the statement

DROP TABLE TT_SYS_WEEKS

fails with [FireDAC][FB][Phys]unsuccesful metadata update Table TT_SYS_WEEKS already exists.

The exact same operations of dropping and creating tables/indices were already performed in steps 3,4,5. TT_SYS_WEEKS is not the last table that was copied.

The design time target connection and its TFDPhysFBDriverLink are set up as follows:

AConnection.TxOptions.AutoCommit := true;
AFDPhysDriverLink.DriverID := 'FBEmbeddedBase';  // JD 28-3-2018
AFDPhysDriverLink.VendorLib := 'fbembed.dll';  // 32-bits only
AConnection.Params.DriverID := 'FBEmbeddedBase'; // AConnection
AConnection.Params.Database := 'full GDB file';
SetFireBirdMapRules(AConnection); // Some mapping rules
AConnection.UpdateOptions.LockWait := False;
AConnection.UpdateOptions.LockMode := lmNone;

The runtime source connection and TFDPhysFBDriverLink are set up as follows:

// Create 'own' TFDPhysFBDriverLink for embedded connection
// https://stackoverflow.com/questions/46691699/setting-up-a-second-tfdphysfbdriverlink-possible-and-necessary
lDriverLink := TFDPhysFBDriverLink.Create(Application);
lDriverLink.DriverID := 'FBEmbedded';
lDriverLink.VendorLib := 'fbembed.dll';  // 32-bits embedded
LRestoreDB := TFDConnection.Create(Application);
LRestoreDB.UpdateOptions.RequestLive     := false; 
LRestoreDB.ResourceOptions.AutoReconnect := true;
LRestoreDB.Params.DriverID := lDriverLink.DriverID; 
with LRestoreDB do
begin
   Params.Database := AFBFileName;
   Params.UserName := '***';
   Params.Password := '***';
   LoginPrompt := False;
   // ResourceOptions.KeepConnection is default true
   FetchOptions.Mode := fmAll;
end;
SetFireBirdMapRules(LRestoreDB); // Some mapping rules

What can be going on?
Anything else I can investigate?

Other background info:

  • Data was copied to the target database with parameterized INSERT queries, for many tables. A transaction with explicit commit was around each table transfer.
  • In the table copy operations TxOptions.AutoCommit is true for the target database
  • Delphi Tokyo 10.2.3 Win32 app, Firebird 2.5.3.25778 Win32
  • This user had an issue with CREATE following DROP. In the answer, Mark writes the use of execute statement adds additional locks iirc which conflict with a subsequent DDL for the same table name. That is PSQL under Firebird 2.1, does not mention embedded, and I don't have the deadlock error.
Cœur
  • 37,241
  • 25
  • 195
  • 267
Jan Doggen
  • 8,799
  • 13
  • 70
  • 144
  • Wrap every DDL (schema changing) operation into an explicit commit also. Your "DROP TABLE TT_SYS_WEEKS" statement seems to be deleting the table, but the error clearly says you tried to CREATE the table or view - "Table TT_SYS_WEEKS already exists.". – Arioch 'The Mar 28 '18 at 10:44
  • Well, I'm not creating it, that's for sure. I'll try your suggestion, though I'm not happy with it. My steps 3 and 4 will then be littered with explicit transactions too. – Jan Doggen Mar 28 '18 at 11:05
  • You do not issue the creating statement at this moment, but you seem to trigger the commit, whish is creating - and failing. Committing after dropping is quite recommended, against "object in use" error. Maybe FireDAC has auto-commit-DDL option, maybe it works reliably. – Arioch 'The Mar 28 '18 at 13:20

1 Answers1

1

You need to perform a commit after step 4, 5 and 6 (and - of course - 7). Some of the DDL in Firebird is only really executed on commit, so if you run everything in one transaction, you won't have actually dropped and recreated the indexes yet in steps 3 and 4, the table drop in step 6 might be blocked by the earlier DML in step 5, and the table drop in step 6 will not have been executed yet when you try to recreate it in step 7.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • Would this be the case for Interbase as well? And do MSSQL, MySQL and Oracle (the other DB types that we handle) not have this issue? – Jan Doggen Mar 28 '18 at 11:19
  • OK, this works. Luckily all my SQL goes through two statements so I can easily check there if I need to do transactions (for now: if FireBird and DDL). One caveat though: The default UpdateOptions.LockWait=false has to be set TRUE, otherwise I get *Lock conflict on no wait transaction* errors – Jan Doggen Mar 28 '18 at 12:22
  • There was a discussion about waiting transactions and SP dropping, it was explained from implementation point of view. – Arioch 'The Mar 28 '18 at 13:21
  • @JanDoggen Can't recall specifics for those other systems, but some database systems will implicitly commit after a DDL statement, and others will simply disallow usage DML and DDL usage of the same objects in a single transaction, etc. Regarding the behavior with wait vs no wait, that is an oddity that I think is a bug, but IIRC the Firebird core developers disagree on that. – Mark Rotteveel Mar 28 '18 at 13:34
  • @JanDoggen You might also want to try if upgrading to Firebird 2.5.8 helps, IIRC there were some changes in this area. Right now I don't have time to double-check the release notes between 2.5.3 and 2.5.8. – Mark Rotteveel Mar 28 '18 at 14:15
  • That is why Firebird/Interbase specific library UIB in its `TUIBScript` component has an `AutoDDL` property :-D – Arioch 'The Mar 29 '18 at 20:44