Interbase - blobs

Top  Previous  Next

InterBase and Blobs - A Technical Overview

By Paul Beach

InterBase and Blobs

InterBase pioneered the use of Blobs in relational database technology, an InterBase Blob can be considered as an arbitrarily long sequence of bytes that is modified under transaction control. The name blob itself does not mean anything, it wasn't an originally defined as an acronym for Binary Large Object, or Basic Large Object, the name actually comes from the "B" film "The Blob", the Blob being a creature from outer space that ate large chunks of the USA.

 

Blob Sub-types

All blobs are sequences of bytes, however this sequence can have different interpretations. InterBase provides a sub-type declaration as a convenient mechanism to track the potential different uses for blobs. In so much that InterBase blob fields are not all the same. They actually consist in a variety of forms, or sub-types of the general blob type. Knowing which sub-type of a blob field to use is useful when creating database applications that incorporate InterBase blob fields. The documentation refers to two pre-defined subtypes, will in actuality there are more. Blob fields come in a number pre-defined varieties: sub-type 0 through sub-type 8 (the pre-defined sub-types), and then there are your own user-defined sub-types.

 

The pre-defined blob sub-types can be found in types.h

 

TYPE ("TEXT",                      1,nam_f_sub_type)

TYPE ("BLR",                       2,nam_f_sub_type)

TYPE ("ACL",                       3,nam_f_sub_type)

TYPE ("RANGES",                    4,nam_f_sub_type)

TYPE ("SUMMARY",                   5,nam_f_sub_type)

TYPE ("FORMAT",                    6,nam_f_sub_type)

TYPE ("TRANSACTION_DESCRIPTION",   7,nam_f_sub_type)

TYPE ("EXTERNAL_FILE_DESCRIPTION", 8,nam_f_sub_type)

 

Sub-type 0 blob fields are created by default when a CREATE command is issued and a sub-type is not specified. It is possible to explicitly indicate that the blob field is to be of sub-type 0 when a type 0 blob is being created. This sub-type of a blob field is typically used for the storage of binary data or data of an indeterminate type.

 

InterBase makes no analysis of the data stored in a blob, it just stores it in the blob field on a byte-for-byte basis. The most common intended use for blob fields in various applications is the storage of binary image data, typically for display for example in a Delphi or C++Builder TDBImage component. Use the blob field sub-type 0 or create your own user-defined sub-type for this purpose. 

 

Sub-type is 1. This blob field sub-type is designed for the storage and manipulation of text. Typically, this is free-form memo or notes data displayed and edited by the Delphi or C++Builder TDBMemo component. Typically you would use this blob field sub-type is for storing large amounts of text data. This is more convenient that a large VARCHAR because, unlike a VARCHAR, there is no 32K limit. 

 

Sub-type 2 Is used to store BLR (Binary Language Representation), InterBase's internal compiled language. All requests to the database engine are ultimately made in BLR. You can find this column definition where SQL or GDML syntax has been converted into BLR for the engine to use. For example triggers and stored procedures. Look in RDB$PROCEDURES for column RDB$PROCEDURE_BLR. This is what gets executed when the stored procedure runs, not the original SQL or GDML source code.

 

Sub-type 5 is is a compiled, binary form of a relation's metadata to avoid expensive join processing when loading that metadata (includes C runtime format descriptors, etc.), whilst sub-type 6 contains the description of a multi-database (two phase commit) transaction that failed.

 

Another specialized type of blob is in fact an array. The blob mechanism is actually used to store both blob and array data. 

 

In SQL the sub-type 1 blob is created by defining after the blob keyword the appropriate SUB_TYPE. e.g.

 

CREATE TABLE EMPLOYEES

(NAME CHAR(20),

HISTORY BLOB SUB_TYPE TEXT);Or 

 

CREATE TABLE EMPLOYEES

(NAME CHAR(20) NOT NULL PRIMARY KEY,

HISTORY BLOB SUB_TYPE 1);Aside from the predefined blob field sub-types, you can define your own sub-types. User-defined sub-types are designated by a negative integer value in association with the sub-type keyword. The actual integer value, that you use, is arbitrary and up to the discretion of the table creator. So long as it's negative - use sub-type -1 to sub-type -32,678. The only issue, when creating and using user-defined sub-types is making sure that the same type of binary data is stored for every row in the table for a blob field of a given user-defined sub-type. 

 

InterBase in itself, will not evaluate whether this criteria is met, in that no error will occur from the InterBase if an incorrect type of binary data is stored in a user-defined blob field sub-type, but an application can have problems if it is expecting to find one type of binary data but finds something else

 

CREATE TABLE IMAGE_DATA

( FILENAME CHAR(12NOT NULL PRIMARY KEY

BMP BLOB SUB_TYPE -1

JPEG BLOB SUB_TYPE -2);If you hadn't thought about it before, you now have a small understanding of how you would consider implementing blob filters that would allow the database to convert data from one sub type to another. In that - the blob filter mechanism relies on knowing what the various sub-types are to provide its functionality. A discussion of blob filters is to be saved for a later date, although the ability that InterBase has that allows you to read and write to a textual blob by default is functionality that is implemented in the engine by a simple text blob filter. 

 

When defining TField objects for InterBase blob fields in Delphi or C++Builder, the various blob field sub-types are assigned TField derivative types as follows: 

 

Sub-type 0: TBlobField

Sub-type 1: TMemoField

User-defined: TBlobFieldBecause both the predefined sub-type 0 and user-defined sub-types are recognized as TBlobField objects, make sure that when you design an application that you do not confuse a field of one sub-type for that of another. The only way to differentiate between a field of sub-type 0 from that of a user-defined type is by viewing the metadata information for the relevant table.

 

When defining a blob and its sub-type, you can also define its segment length. A segment is a chunk of a blob, or unit that can be written or read in a pre-determined amount. For example a text blob will automatically have a segment length of 80 bytes, historically the size of a terminal screen. The largest segment length that you can define is 32k or 32,767 bytes. 

 

CREATE TABLE EMPLOYEES

(NAME CHAR(20) NOT NULL PRIMARY KEY,

HISTORY BLOB SUB_TYPE 1 SEGMENT SIZE 80);The documentation refers to the fact that you must read or write to blobs a segment at a time. This is not specifically true, as there are internal utilities in InterBase that do make the manipulation of blobs a little easier. 

 

Blob Internal Storage

Blobs are created as part of a data row, but because a blob could be of unlimited length, what is actually stored with the data row is a blobid, the data for the blob is stored separately on special blob pages elsewhere in the database. The blobid is an 8 byte value that allows an InterBase to uniquely identify a blob and locate it. The blobid's can be either temporary or permanent, a temporary blob is one which has been created, but has not yet been stored as part of a table, permanent blobs have been stored in a table. The first 4 bytes represent the relation id for the blob (like data rows, blobs are bound to a table), the second four bytes represent the id of the blob within the tableFor temporary blobs the relation id part is set to 0

 

A blob page stores data for a blob, for large blobs, the blob page could actually be a blob pointer page, i.e. is used to store pointers to other blob pages. For each blob that is created a blob record is defined, the blob record contains the location of the blob data, and some information about the blobs contents that will be useful to the engine when it is trying to retrieve the blob. The blob data could be stored in three slightly different ways. The storage mechanism is determined by the size of the blob, and is identified by its level number (01 or 2). All blobs are initially created as level 0, but will be transformed to level 1 or 2 as their size increases.

 

level 0 blob, is a blob that can fit on the same page as the blob header record, for a data page of 4096 bytes, this would be a blob of approximately 4052 bytes (Page overhead - slot - blob record header). Although the documentation states that the segment length does not affect the performance of InterBase, the actually physical size of a blob, or its segment length can become useful in trying to improve I/O performance for the blob, especially if you can size the segment (typically) or blob to a page, and this is especially true is you plan to manipulate the blob using certain low level InterBase Blob calls.

 

When a blob is too large to fit on a single page (level 1), and the data will be stored on one or more blob data pages, then the initial page of the blob record will hold a vector of blob page numbers.

 

level 2 blob occurs when the initial page of the blob record is not big enough to contain the vector of all the blob data page numbers. Then InterBase will create blob pointer pages, i.e. multiple vector pages that can be accessed from the initial blob header record, that now point to blob data pages. The maximum size of a level 2 blob is a product of the maximum number of pointer pages, the number of data pages per pointer page, and the space available on each data page.

 

There are three different types of objects that use the blob storage mechanism. Segmented blobs are broken up into segments, or pieces of data (chunks). A segment is stored on disk as a two byte segment length followed by the segment. A stream blob on the other hand, is stored exactly as it was passed to the engine. An array is implemented as a stream blob, except that a description of the array is stored first, followed by the array data.

 

Manipulating Blobs

Above we defined a simple table containing a text blob, outputting blob data is easy, however inputting and updating text into that blob is more complicated.....

 

In SQL a normal SELECT statement will automatically retrieve the textual data (and any other blob data for that matter). Use the command at the ISQL command line:

 

SET BLOB [ALL | n]ALL will display all blob sub-types whilst n can be used to define a specific subtype. 

 

However inserting and updating a blob in SQL is another matter, and in the documentation it states that this can only be done programmatically. Using a tool for example that automatically understands the basic blob sub-types e.g. Delphi or C++Builder, by using specialist InterBase components, or by utilizing a 3rd party User Defined Functions library that allows for some blob manipulation functionality.

 

Rather than try and go into all the various work-arounds, add-ins etc that there are available, the purpose of this paper is to look specifically at how blobs can be manipulated simply and programatically. But before we do that, it might be worth letting you all know, that you can put text directly into a blob on windows by doing the following.

 

At the DOS prompt.

 

SET EDITOR=editRemember to set ISC_USER and ISC_PASSWORD variables too.

 

QLI

QLI> ready employee.gdb

QLI> store employees

Enter name: TestUp comes your editor, to allow you to insert history automatically.

 

On other platforms, just define the EDITOR to be your favourite editor e.g. vi, emacs etc.

 

The current and V5.x InterBase Programmers Guide, Language Reference and API guide all refer to the manipulation of blobs using embedded SQL and cursors that allow you to manipulate a blob a segment at a time. All fairly complex and time consuming, mainly because the SQL standard still doesn't really support blobs. Please refer to the documentation on how this is done. However before InterBase 4.0 there were a number of simple basic blob library routines available that would allow a developer to manipulate blobs simply and easily. These functions allowed the interchange of data between blob fields and operating system files.

 

Pre 4.0 Blob Routines

To compile the sample program, use the following:

 

1. Make sure you pre-compile the program using gpre. e.g 

 

Gpre -c -either -manual -no sample.eGpre -c (extended C program), -either (accept upper case or lower case DML in C) -manual (do not automatically attach to the database -no (do not generate C debug lines) sample.e

 

This will create a .c file (sample.c), that can be compiled by C++Builder, or your favourite C/C++ compiler.

 

Sample Code to: 

 

Load the contents of an operating system file into a blob: BLOB_load. 

Dump the contents of a blob into an operating system file: BLOB_dump. 

Read a blob field, dump it a file, and call an editor with the contents of a blob: BLOB_display. 

Write or update the contents of a blob via an editor: BLOB_edit. 

Simple Database Definition

 

SET SQL DIALECT 3;

/* CREATE DATABASE*/

 

Create database 'D:\test\Test.gdb' PAGE_SIZE 1024

 

/* DEFAULT CHARACTER SET  */

 

/* Table: BLOB_TEST, Owner: SYSDBA */

 

CREATE TABLE "BLOB_TEST" 

(

  "PKEY"        INTEGER NOT NULL,

  "DATA"        BLOB SUB_TYPE 0 SEGMENT SIZE 80,

 PRIMARY KEY ("PKEY")

);Sample Code

 

#include <stdio.h>

#include <stdlib.h>

 

int main(int argc, char* argv[])

{

char filename[40];

 

EXEC SQL WHENEVER SQLERROR GOTO ExitError;

 

EXEC SQL SET DATABASE DB1 = 'd:/test/test.gdb';

 

EXEC SQL BEGIN DECLARE SECTION;

BASED ON BLOB_TEST.PKEY pkey;

BASED ON BLOB_TEST.DATA blob_data;

EXEC SQL END DECLARE SECTION;

 

EXEC SQL CONNECT DB1 USER 'SYSDBA' PASSWORD 'masterkey';

printf("Database is open\n");

 

EXEC SQL SET TRANSACTION;

printf("Starting a transaction\n");

 

/* BLOB_load Example */

 

STORE A IN BLOB_TEST USING A.PKEY = 1;

BLOB_load (&A.DATA, DB1, gds__trans, "a.txt");

END_STORE; 

 

/* BLOB_dump Example */

 

FOR A in BLOB_TEST WITH A.PKEY = 1

BLOB_dump (&A.DATA, DB1, gds__trans, "b.txt");

END_FOR; 

 

/* BLOB_display Example */

 

FOR A in BLOB_TEST WITH A.PKEY = 1

BLOB_display(&A.DATA, DB1, gds__trans, "");

END_FOR; 

 

/* BLOB_edit Insert Example */

 

STORE A IN BLOB_TEST USING A.PKEY = 1;

BLOB_edit(&A.DATA, DB1, gds__trans, "edit");

END_STORE;

 

/* BLOB_Edit Update Example */

 

FOR A IN BLOB_TEST WITH A.PKEY = 1

MODIFY A USING

BLOB_edit(&A.DATA, DB1, gds__trans, "edit");

END_MODIFY;

END_FOR;

 

printf("\n");

 

EXEC SQL COMMIT;

printf("Commit the transaction\n");

 

EXEC SQL DISCONNECT DB1;

printf("Close the database\n");

 

ExitError:

if (SQLCODE)

{

isc_print_sqlerror(SQLCODE, isc_status);

EXEC SQL ROLLBACK;

EXEC SQL DISCONNECT DEFAULT;

exit(1);

}

 

}