firebird icon indicating copy to clipboard operation
firebird copied to clipboard

Can't connect into the FB6x database with 32bit Embeded connection

Open TommiPrami opened this issue 7 months ago • 22 comments

Yellow,

Restore worked fine with FB 6 embedded. (From 5.0.2 backup).

FB6 embedded is of version: 6.0.0.726

I Put a break point to (Devart Ib)DAC code and can give this kind of information :

Errorcode: -922 ErrorNumber: 335544323 SQLErrorMessage: 'Connection authorization failure.' ErrorMessage: 'file C:\DATA\DBUPGRADE_TESTDATA\THDAT_FB25X.FDB is not a valid database'

Names are from the variable n ames, so not sure are ther terminology even closely related to Firebird Engine.

FB6 is not officially supported by the DAC, but it worked not in so distant past. Maybe 3-4 weeks ago it worked for sure....

Is this intentional that can I give any more information? Might be that DAC needs code chjanges, but that does not, most likely, happen before FB6 is relasased, If there is some problems in FB6, let me know what I can do to solve this.

-tee-

TommiPrami avatar Apr 22 '25 09:04 TommiPrami

Made followiong test.

Did database backup restore to FB6. Then copied database to the virtual machine and I could open the database nicely with latest FB6 nightly build of server installed.

So there is some weirdness in Embedded conection or FB6 embedded needs something else/more than FB5 one.

So as bottom line is that, database is OK, so the error I get with embedded is not real, or some kind of side effect of soimething.

Will the Embedded write some kind of log, or can I set up to write log, to maybe get more information on the problem.

TommiPrami avatar Apr 24 '25 03:04 TommiPrami

Yellow,

Found the iussue. Not sure that what causes it.

Process I do is, as defautl I'll try to open database with FB4x embedded, if I get error, parse error to get right Firebird ODS version for it the target DB.

When target DB is of versionn fb6x and I'll probe the firebird version with FB4x embedded connection, that fb4x engine will stay in memory or something like that.

(might be DAC problem don't know, I have encountered same kind of error, if I started from fb259 embedded as default for probing the target db ODS version, then I encountered some Engine already in memory or some shared memory allocated error, can't remember anymore. With FB4 or fb5 version of embedded connection, With the FB6 the error message is different for sure).

-Tee-

TommiPrami avatar Apr 24 '25 03:04 TommiPrami

Few instances of the firebird.dll in the same process is not supported. There can be just one dispatcher in process.

Try to unload v4 firebird.dll before attempt to connect to using v6 firebird.dll. Call fb_shutdown() before unloading of firebird.dll I can't guarantee it will work, though.

What definitely will work is to manually detect ODS version and load correct firebird.dll after.

Or you may try to put engine12.dll (v4 or v5) into v6's plugins folder and include it into providers list at firebird.conf. In latter case you don't need no have two folders with v4 and v6.

hvlad avatar Apr 24 '25 05:04 hvlad

Or you may try to put engine12.dll (v4 or v5)

For v4 and 5 it's engine13.dll

mrotteveel avatar Apr 24 '25 06:04 mrotteveel

Or you may try to put engine12.dll (v4 or v5)

For v4 and 5 it's engine13.dll

Sure, thanks

hvlad avatar Apr 24 '25 06:04 hvlad

Anyhow, this worked few weeks ago, somethign has changed. But would be beter to be able to tell DAC to unoad the firebird...

Maybe I can force DAC to unload Firbird embedded dll:s etc, somehow, on disconnect and/connection error.

Have to check that out, if there is change to do that, some how...

TommiPrami avatar Apr 24 '25 06:04 TommiPrami

What definitely will work is to manually detect ODS version and load correct firebird.dll after.

That is what I am doing, but just inside one process... Can some Firebird commandline tool help me with that. Would be nice to have super simple tool that would output ODS-version of database onto the std-out (or what it is called, I am blankin out)

(Sure I can make one, but only with DAC and it is going to be quite huge exe, for just to output ODS version. Withn C++ and Firebird sources, it would be versi small utility, or small addition to some existing one)

As there is no simple header in the Database file, that would always have ODS version nicely in certain position of the file (As far as I know it is on some header page of the database, not in fixed position on the file for all Firebird versions)...

Then I could just parse the returned string/output super easily. without trying to open connection with the DAC.

TommiPrami avatar Apr 24 '25 06:04 TommiPrami

What definitely will work is to manually detect ODS version and load correct firebird.dll after.

That is what I am doing, but just inside one process... Can some Firebird commandline tool help me with that. Would be nice to have super simple tool that would output ODS-version of database onto the std-out (or what it is called, I am blankin out)

gstat -h

(Sure I can make one, but only with DAC and it is going to be quite huge exe, for just to output ODS version. Withn C++ and Firebird sources, it would be versi small utility, or small addition to some existing , one)

As there is no simple header in the Database file, that would always have ODS version nicely in certain position of the file (As far as I know it is on some header page of the database, not in fixed position on the file for all Firebird versions)...

Then I could just parse the returned string/output super easily. without trying to open connection with the DAC.

The hdr_ods_version is 2-bytes integer at the offset of 18 bytes from the start of the database file. Its size and position is the same for ods11-ods14. You don't have to use DAC to open file and read 2 bytes at given offset.

hvlad avatar Apr 24 '25 07:04 hvlad

On Wed, 23 Apr 2025 23:51:34 -0700 Tommi Prami @.***> wrote:

TommiPrami left a comment (FirebirdSQL/firebird#8530)

What definitely will work is to manually detect ODS version and load correct firebird.dll after.

That is what I am doing, but just inside one process... Can some Firebird commandline tool help me with that. Woulöd be nice to have supersimple tool that would put ODS-version of database onto the std-out (or what it is called, I am blankin out)

On windows console:

echo show db; | "%FIREBIRD_BIN%"\isql.exe employee | find "ODS"

Powershell:

echo "show db;" | & $env:FIREBIRD_BIN\isql.exe employee | grep ODS

Bash:

echo "show db;" | $FIREBIRD_BIN/isql employee | grep ODS

Paul

Paul Reeves https://www.ibphoenix.com Supporting users of Firebird

reevespaul avatar Apr 24 '25 10:04 reevespaul

The hdr_ods_version is 2-bytes integer at the offset of 18 bytes from the start of the database file. Its size and position is the same for ods11-ods14. You don't have to use DAC to open file and read 2 bytes at given offset.

This would help a lot, there is somethign that I could not figure out.

From FB259 DB

Image

FB4x DB

Image

FB4 there is 13 as decimal in first byte, that would make sence, buit in FB25 it is 11, so there is some kind of encoding in here. Next byt is 80 as hex in both, not sure what that is...

Tried to search trough FB code, but did not find the way it is written and or read from the file.

I think I found hint that minor version would be nextr word, in the position of 20, in the file. But could be wildly wron here.

-tee-

TommiPrami avatar Apr 28 '25 11:04 TommiPrami

Why doesn't that make sense? Firebird 3.0 is ODS 12.0, Firebird 4.0 is ODS 13.0, while Firebird 2.5 is ODS 11.2 (the minor ODS is encoded elsewhere).

In any case, the 0x80 in the second byte is from the 0x8000 mask used to distinguish InterBase ODS from Firebird ODS.

mrotteveel avatar Apr 28 '25 11:04 mrotteveel

Tried to search trough FB code, but did not find the way it is written and or read from the file.

You don't need to search where it is written. Whole ODS is described in ods.h. Particularly "next byte" contain inline constexpr USHORT ODS_FIREBIRD_FLAG = 0x8000; which is used as a mask.

aafemt avatar Apr 28 '25 11:04 aafemt

See also https://www.firebirdsql.org/file/documentation/html/en/firebirddocs/firebirdinternals/firebird-internals.html#fbint-page-1 (though technically, I believe everything beyond the ODS major might be subject to change in major ODS changes, and that document hasn't been updated in a while). According to that document, ODS minor is at bytes 0x3e and 0x3f (but again, I believe this is subject to change, and in fact has changed significantly in Firebird 6).

mrotteveel avatar Apr 28 '25 11:04 mrotteveel

For example, compare the ods.h from Firebird 5 (https://github.com/FirebirdSQL/firebird/blob/v5.0-release/src/jrd/ods.h) with the one Dimitry linked from master (Firebird 6): https://github.com/FirebirdSQL/firebird/blob/master/src/jrd/ods.h), specifically the struct header_page.

mrotteveel avatar Apr 28 '25 12:04 mrotteveel

in Pascal, it should be something like this :-) but in the 98 interbase doc, on page 50 of ‘Api guide’, it talks about isc_vax_integer :-(

unit ODSVersion;

interface

const ODS_VERSION8 = 8; ODS_VERSION9 = 9; ODS_VERSION10 = 10; ODS_VERSION11 = 11; ODS_VERSION12 = 12; ODS_VERSION13 = 13; ODS_VERSION14 = 14;

function ENCODE_ODS(major, minor: Word): Word; inline; function DECODE_ODS_MAJOR(ods_version: Word): Word; inline; function DECODE_ODS_MINOR(ods_version: Word): Word; inline;

const ODS_8_0 = ENCODE_ODS(ODS_VERSION8, 0); ODS_8_1 = ENCODE_ODS(ODS_VERSION8, 1); ODS_9_0 = ENCODE_ODS(ODS_VERSION9, 0); ODS_9_1 = ENCODE_ODS(ODS_VERSION9, 1); ODS_10_0 = ENCODE_ODS(ODS_VERSION10, 0); ODS_10_1 = ENCODE_ODS(ODS_VERSION10, 1); ODS_11_0 = ENCODE_ODS(ODS_VERSION11, 0); ODS_11_1 = ENCODE_ODS(ODS_VERSION11, 1); ODS_11_2 = ENCODE_ODS(ODS_VERSION11, 2); ODS_12_0 = ENCODE_ODS(ODS_VERSION12, 0); ODS_13_0 = ENCODE_ODS(ODS_VERSION13, 0); ODS_13_1 = ENCODE_ODS(ODS_VERSION13, 1); ODS_14_0 = ENCODE_ODS(ODS_VERSION14, 0);

ODS_FIREBIRD_FLAG = $8000;

implementation

function ENCODE_ODS(major, minor: Word): Word; inline; begin Result := (major shl 4) or minor; end;

function DECODE_ODS_MAJOR(ods_version: Word): Word; inline; begin Result := (ods_version and $7FF0) shr 4; end;

function DECODE_ODS_MINOR(ods_version: Word): Word; inline; begin Result := ods_version and $000F; end;

end.

NorpaNet avatar Apr 28 '25 12:04 NorpaNet

but in the 98 interbase doc, on page 50 of ‘Api guide’, it talks about isc_vax_integer :-(

Mentioning isc_vax_integer just means it's little-endian encoded, though as far as I'm aware, the encoding depends on the platform (so little-endian on little-endian CPUs like x86, and big-endian on big-endian CPUs)

mrotteveel avatar Apr 28 '25 12:04 mrotteveel

Why doesn't that make sense? Firebird 3.0 is ODS 12.0, Firebird 4.0 is ODS 13.0, while Firebird 2.5 is ODS 11.2 (the minor ODS is encoded elsewhere).

In any case, the 0x80 in the second byte is from the 0x8000 mask used to distinguish InterBase ODS from Firebird ODS.

Looked major ODS versions wrongly of FB259, but that 0x8000 helped out a lot.

But mainly was looking at the possible minor version, which seems that should be 2 byte int at position 20, and all FB db:s checked had it "03 00"

Tested FB259, FB40, FB5 and FB6 nighty

TommiPrami avatar Apr 29 '25 05:04 TommiPrami

function ENCODE_ODS(major, minor: Word): Word; inline; begin Result := (major shl 4) or minor; end;

function DECODE_ODS_MAJOR(ods_version: Word): Word; inline; begin Result := (ods_version and $7FF0) shr 4; end;

function DECODE_ODS_MINOR(ods_version: Word): Word; inline; begin Result := ods_version and $000F; end;

I think, not sure tough, this is optimization of Firebird version comparisons, internally in the engione, and most likely other tools also.

Gets rid of ned to if both major andminor versions in many places. Or it seemed like that, when skimmin trough the code.

But thanks anyway...

Here is parts of my Pascal code so far.

type
  TDBFileHeader = packed record
    Padding: array [0..17] of Byte;
    EncodedODSMajorVersion: Word;
    ODSMinorVersion: Word;
  end;

procedure DecodeODSVersion(const AODSHeader: TDBFileHeader; var AMajorVersion, AMinorVersion: Integer);
const
  ODS_FIREBIRD_FLAG = $8000; // is it FB or IB
begin
  // Minor version osalta KESKEN
  AMajorVersion := 0;
  AMinorVersion := 0;

  var LIsFirebird := (AODSHeader.EncodedODSMajorVersion and ODS_FIREBIRD_FLAG) <> 0;

  if not LIsFirebird then
    Exit;

  AMajorVersion := AODSHeader.EncodedODSMajorVersion and (not ODS_FIREBIRD_FLAG);
  AMinorVersion := AODSHeader.ODSMinorVersion;
end;

function ReadDatabaseFileODSHeader(const AFileName: string; var ADBFileHeader: TDBFileHeader): Boolean;
begin
  Result := False;
  FillChar(ADBFileHeader, SizeOf(TDBFileHeader), 0);

  if FileExists(AFileName) then
  begin
    var LFileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
    try
      var LDataRead := LFileStream.Read(ADBFileHeader, SizeOf(TDBFileHeader));

      if LDataRead <> SizeOf(TDBFileHeader) then
        FillChar(ADBFileHeader, SizeOf(TDBFileHeader), 0)
      else
        Result := True;
    finally
      LFileStream.Free;
    end;
  end;
end;

function GetDatabaseFileODSVersion(const AFileName: string; var AMajorVersion, AMinorVersion: Integer): Boolean;
begin
  AMajorVersion := 0;
  AMinorVersion := 0;

  var LDBFileHeader: TDBFileHeader;

  Result := ReadDatabaseFileODSHeader(AFileName, LDBFileHeader);
  if Result then
  begin
    DecodeODSVersion(LDBFileHeader, AMajorVersion, AMinorVersion);

    Result := (AMajorVersion >= 11) and (AMinorVersion >= 0);
  end;
end;

If I would get the minor ODS version, would be very happy camper...

TommiPrami avatar Apr 29 '25 05:04 TommiPrami

For example, compare the ods.h from Firebird 5 (https://github.com/FirebirdSQL/firebird/blob/v5.0-release/src/jrd/ods.h) with the one Dimitry linked from master (Firebird 6): https://github.com/FirebirdSQL/firebird/blob/master/src/jrd/ods.h), specifically the struct header_page.

This helped a lot, Minor version seems to be very different place than in the code I checked out.

Did not check which version had the minoir version at position 20.

I think I can get that done now. Have to ponder a bit...

Thank,

-tee-

TommiPrami avatar Apr 29 '25 05:04 TommiPrami

This is pretty close.

type
  TDBFileHeader = packed record
    Padding: array [0..17] of Byte;
    EncodedODSMajorVersion: Word; // Offset 18..19
    ODSMinorVersionFB6: Word; // Offset 20..21
    Paddinng2: array [0..39] of Byte; // offset 22-61 (40 bytes)
    ODSMinorVersionFB25x: Word; // offset 62
    ODSMinorVersionFB4_5x: Word; // offset 64
    ODSMinorVersionFB30x: Word; // offset 66
  end;

  {
    FB Version  ODS Version

    2.5         11.2
    3.0         12.0
    4.0	        13.0
    5.0         13.1
    6.0         14.0
  }
procedure DecodeODSVersion(const AODSHeader: TDBFileHeader; var AMajorVersion, AMinorVersion: Integer);
const
  ODS_FIREBIRD_FLAG = $8000; // is it FB or IB
begin
  AMajorVersion := 0;
  AMinorVersion := 0;

  var LIsFirebird := (AODSHeader.EncodedODSMajorVersion and ODS_FIREBIRD_FLAG) <> 0;

  if not LIsFirebird then
    Exit;

  AMajorVersion := AODSHeader.EncodedODSMajorVersion and (not ODS_FIREBIRD_FLAG);
  case AMajorVersion of
    11: AMinorVersion := AODSHeader.ODSMinorVersionFB25x;
    12: AMinorVersion := AODSHeader.ODSMinorVersionFB30x;
    13: AMinorVersion := AODSHeader.ODSMinorVersionFB4_5x;
    14: AMinorVersion := AODSHeader.ODSMinorVersionFB6;
  end;
end;

function ReadDatabaseFileODSHeader(const AFileName: string; var ADBFileHeader: TDBFileHeader): Boolean;
begin
  Result := False;
  FillChar(ADBFileHeader, SizeOf(TDBFileHeader), 0);

  if FileExists(AFileName) then
  begin
    var LFileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
    try
      var LDataRead := LFileStream.Read(ADBFileHeader, SizeOf(TDBFileHeader));

      if LDataRead <> SizeOf(TDBFileHeader) then
        FillChar(ADBFileHeader, SizeOf(TDBFileHeader), 0)
      else
        Result := True;
    finally
      LFileStream.Free;
    end;
  end;
end;

function GetDatabaseFileODSVersion(const AFileName: string; var AMajorVersion, AMinorVersion: Integer): Boolean;
begin
  AMajorVersion := 0;
  AMinorVersion := 0;

  var LDBFileHeader: TDBFileHeader;

  Result := ReadDatabaseFileODSHeader(AFileName, LDBFileHeader);
  if Result then
  begin
    DecodeODSVersion(LDBFileHeader, AMajorVersion, AMinorVersion);

    Result := (AMajorVersion >= 11) and (AMinorVersion >= 0);
  end;
end;

For reason or other FB6x Employee db returns minor version of 3.

Have to do I have old version of the DB and there has been ODS changes in between...

If som eone is willing and time, fell free to double check my code so far....

-tee-

TommiPrami avatar Apr 29 '25 05:04 TommiPrami

Forget about minor version, you don't need it. And it is not placed next after major version, this is wrong assumption. All you need it to read 2 bytes at offset 18, check that second is 0x80 (else it is not Firebird ODS) and use first one as major ODS version. Don't make simple things complex.

hvlad avatar Apr 29 '25 06:04 hvlad

ODSMinorVersionFB6: Word; // Offset 20..21
Paddinng2: array [0..39] of Byte; // offset 22-61 (40 bytes)
ODSMinorVersionFB25x: Word; // offset 62
ODSMinorVersionFB4_5x: Word; // offset 64
ODSMinorVersionFB30x: Word; // offset 66

You may want to consider specifying those in terms of the major ODS, e.g. ODSMinorForODS14 instead of ODSMinorVersionFB6, and ODSMinorForODS11 instead of ODSMinorVersionFB25x as ODS 11 is used for Firebird 2.0 (minor 0), 2.1 (minor 1) and 2.5 (minor 2).

mrotteveel avatar Apr 29 '25 06:04 mrotteveel