Dataset ID storage

Datenbank Bits

As already announced in the article Version 5.8 Part 1 RecID unlimited, this article presents solution approaches for the adaptation of dataset IDs if they are saved as a reference to a target dataset.

Background

64-bit data set IDs were introduced with version 5.8. As a result, the IDs of data sets differ between versions 5.7 and 5.8. This article describes what needs to be taken into account when switching to version 5.8 if an application persistently saves data set IDs in order to reference data.
Beispiel

With version 5.7, a table is created and then a data record is inserted. The data record is then read and the data record ID (decimal and hexadecimal) is output in the debugger:

@A+
@C+

main
{
  RecRead(100,1,0);
  DbgTrace('Version: ' + CnvAI(DbaInfo(_DbaClnRelMaj)) + '.' +
                         CnvAI(DbaInfo(_DbaClnRelMin)) +
           ' - Datensatz-ID: ' + CnvAI(RecInfo(100,_RecID)) +
           ' / 0x' + CnvAI(RecInfo(100,_RecID),_FmtNumHex | _FmtNumLeadZero,0,8));
}

The following line presents the result:

Version: 5.7 - Datensatz-ID: 16.777.216 / 0x01000000

When executed with version 5.8, the following result is obtained:

Version: 5.8 - Datensatz-ID: 1 / 0x00000001

The determined data record ID therefore differs between the versions. As the data record ID is only used temporarily in most cases, this is not a problem. However, the situation is different if the data record ID is saved as a field in the data record, for example, in order to reference a specific data record in another table (Fig. 1).

Referenzierung eines Datensatzes über seine Datensatz-IDAbb.1: Referencing a data record via its data record ID

In the example, the data record ID is contained as a field in the source table. A RecRead() via the stored record ID in the source table (e.g. RecID1) to determine the record in the target table would come to nothing with version 5.8, as its record ID is now a different one (red dashed arrow).

Solution approach

The approach is to calculate a data record ID (version 5.8) from the data record ID (version 5.7). This would turn the value 0x01000000 back into 0x00000001, 0x02000000 back into 0x00000002 and so on. The reference would be restored in this way. The following code shows a function for calculating the data record ID from an int value:

// Converts a RecID that is stored in an int value
sub ConvertSingleRecIDInt
(
  aRecID                : int;   // Data record ID (Version 5.7)
  aTarTblIsSequential   : logic; // Target file (to which the RecIDs refer)
                                 // has “sequential insertion” activated?
)
: int;                           // Data record ID (Version 5.8)
{
  if (aTarTblIsSequential)
  {
    if (aRecID & 0x80 != 0)
      ErrSet(_ErrValueOverflow);
    else
      return (
          (aRecID & 0xFF000000) >> 24 & 0xFF |
          (aRecID & 0x00FF0000) >> 8         |
          (aRecID & 0x0000FF00) << 8         |
          (aRecID & 0x000000FF) << 24
      );
  }
  else
  {
    if (aRecID > 0)
      ErrSet(_ErrValueOverflow);
    else
      return -aRecID;
  }
}

The function converts a data record ID version 5.7 (1st argument) into a data record ID version 5.8 (return value). Depending on whether the “Sequential insert” option is set for the target table or not, the database server assigns the data record IDs in a different order. The calculation then also differs. If “Sequential insert” is set for the table, the function is passed true in the second argument, otherwise false. The function sets the global error code _ErrValueOverflow if the resulting record ID >= 0x80000000, as these cannot be mapped in the value range of int. The function should therefore only be used if the prime counter of the target file is < 0x80000000. If it is greater, the following function should be used:

// Converts a RecID that is stored in a bigint value
sub ConvertSingleRecIDBigInt
(
  aRecID                : bigint; // Data record ID (Version 5.7)
  aTarTblIsSequential   : logic;  // Target file (to which the RecIDs refer)
                                  // has “sequential insertion” activated?
)
: bigint;                         // Data record ID (Version 5.8)
{
  if (aTarTblIsSequential)
  {
    return (
        (aRecID & 0x00000000FF000000\b) >> 24\b |
        (aRecID & 0x0000000000FF0000\b) >> 8\b  |
        (aRecID & 0x000000000000FF00\b) << 8\b  |
        (aRecID & 0x00000000000000FF\b) << 24\b)
    ;
  }
  else
  {
    if (aRecID < 0\b)
      return -aRecID;

    return (0x100000000\b - aRecID);
  }
}

As the return value of the function is bigint, all data set IDs to be converted can be displayed. However, this also means that a bigint field must now be provided to store the data record ID.

The ConvertRecID58 function

If the data record ID is stored in a database field, calculating the data record ID alone is not sufficient. The original record ID must also be replaced by the new value:

  • On-demand
    The data record ID is replaced when it is required for the first time. The advantage of this is that not all stored data set IDs need to be replaced, but only those that are actually referenced. The disadvantage is that all places in the source code where the original data set ID is accessed must be adapted. Furthermore, an additional flag would be required in the data record to indicate whether the data record ID has already been converted or not.
  • All data records
    The data record IDs of all data records are calculated and changed in the data record. The advantage here is that no changes need to be made to the source code if the same database field is used for the new data record ID. However, a separate run must be carried out that reads all data records once, enters the new data record ID in the database field and then updates the data record in the database.

The ConvertRecID58 function can be used for the practical implementation of the second method:

sub ConvertRecID58
(
  aTblNo                : int;          // Table number in which the RecIDs are located
  aSbrNoSrc             : int;          // Partial data record number in aTblNo (conversion source)
  aFldNoSrc             : int;          // Field number in aSbrNo (conversion source)
  aTarTblIsSequential   : logic;        // Target file (to which the RecIDs refer) has “sequential insertion” activated?
  opt aSbrNoTar         : int;          // Partial data record number in aTblNo (conversion target). If not specified, aSbrNoSrc is used.
  opt aFldNoTar         : int;          // Field number in aTblNo (conversion target). If not specified, aSbrNoSrc is used.
)

The function reads all data records in the aTblNo file one after the other and updates the data records with the newly calculated data record ID. The field that stores the data record ID (version 5.7) is specified via the arguments aTblNo, aSbrNoSrc (partial data record no.) and aFldNoSrc (field no.). It must have the data type int or bigint. If nothing else is specified, the data record ID is saved in the same field. If the original data record ID is to be retained, a different database field can be specified in the same data record via aSbrNoTar and aFldNoTar.

Example database

You can use the sample database at the end of the blog article to test the function described. The database contains famous movie pairs. Instructions on how to use the example can be found in the ReadMe.rtf document, which is also included in the archive.

Download

Beispiel-Datenbank FamousCouples.zip (70.94 KB)
Sie müssen angemeldet sein, um die Datei herunterladen zu können.

Klicken Sie hier, um die Nutzungsbedingungen für unseren Blog zu lesen.

Leave a Reply

Your email address will not be published. Required fields are marked *

Leave the field below empty!

Requests, questions or feedback are welcome:

Terms of use of the comment function in the blog

1. General information

Vectorsoft AG (‘Provider’) provides a public blog for Internet users (‘Users’) on the vectorsoft.de website. The public blog is intended for the exchange of information and ideas. Users who participate with contributions and comments undertake to comply with the blog terms of use and are responsible for the accuracy, appropriateness and freedom from legal infringements of their contributions. By using the comment function in our blog, you accept these terms of use.

2. Netiquette

We ask you to refrain from personal attacks and provocations based on other opinions. Please argue objectively and maintain a constructive discussion culture. Your comment should always be related to the topic in question in order to avoid digressions into other topics. Posting the same comment or several similar comments more than once is not permitted.

3. Prohibition of illegal content

By submitting your comment, you confirm that you are not violating any copyrights or other rights of third parties. Inciting, racist statements, instructions for criminal offences and their glorification, depictions of violence, pornographic content and statements that violate personal rights are prohibited.

4. No advertising

The use of the comment function is not permitted for commercial or party-political purposes. Advertising contributions of any kind will be deleted immediately.

5. Details of the name

When entering your name, pay attention to the principles mentioned above.

6. Source references

If you intend to publish quotes or contributions from third parties, please indicate the respective sources and explain how they relate to the blog post.

7. Violation of the terms of use

Posts that violate this policy will be deleted immediately. If you notice any violations yourself, please send us the link to the comment in question by e-mail to . We expressly point out that we will exclude individual users in the event of repeated or serious violations of these terms of use.

As of: Sept. 2024

Get your Trial Version now!

Test yeet free of charge

IHRE EVALUIERUNGSLIZENZ - JETZT ANFORDERN!

TESTEN SIE DIE CONZEPT 16 VOLLVERSION - UNVERBINDLICH und KOSTENFREI

Subscribe to our newsletter

[cleverreach_signup]
WordPress Cookie Notice by Real Cookie Banner