Copyright: Copyright © 1996 Borland International, Inc. All rights reserved.


API Function Reference

This is an alphabetical reference for InterBase API function calls. It includes the following topics:

Function Categories: provides tables that categorize calls by the tasks they perform.

Alphabetical Function List: provides an alphabetical list of all included functions.

Using Function Definitions: explains the elements of a function definition.


Function Categories

The InterBase API function calls can be divided into nine classes:

Array functions for handling arrays of data.

BLOB functions for handling the InterBase BLOB data type.

Conversion functions for translating dates between InterBase format and Unix tm format, and for reversing the byte order of integers.

Database functions for handling database requests.

DSQL functions for handling SQL statements entered by users at run time.

Error-handling functions for handling database error conditions.

Event functions for registering interest in events posted by triggers and stored procedures in applications and for processing the event queue.

Information functions for retrieving information about databases, transactions, BLOB data, and events.

Transaction functions for handling transactions in an application.

Some functions, such as information calls, occur in more than one class.


Alphabetical Function List

isc_array_get_slice()	isc_dsql_exec_immed2()
isc_array_lookup_bounds()	isc_dsql_fetch()
isc_array_lookup_desc()	isc_dsql_free_statement()
isc_array_put_slice()	isc_dsql_prepare()
isc_array_set_desc()	isc_dsql_set_cursor_name()
isc_attach_database()	isc_dsql_sql_info()
isc_blob_default_desc()	isc_encode_date()
isc_blob_gen_bpb()	isc_event_block()
isc_blob_info()	isc_event_counts()
isc_blob_lookup_desc()	isc_expand_dpb()
isc_blob_set_desc()	isc_get_segment()
isc_cancel_blob()	isc_interprete()
isc_cancel_events()	isc_open_blob2()
isc_close_blob()	isc_prepare_transaction()
isc_commit_retaining()	isc_prepare_transaction2()
isc_commit_transaction()	isc_print_sqlerror()
isc_create_blob2()	isc_print_status()
isc_database_info()	isc_put_segment()
isc_decode_date()	isc_que_events()
isc_detach_database()	isc_rollback_transaction()
isc_drop_database()	isc_sqlcode()
isc_dsql_allocate_statement()	isc_sql_interprete()
isc_dsql_alloc_statement2()	isc_start_multiple()
isc_dsql_describe()	isc_start_transaction()
isc_dsql_describe_bind()	isc_transaction_info()
isc_dsql_execute()	isc_vax_integer()
isc_dsql_execute2()	isc_version()
isc_dsql_execute_immediate()	isc_wait_for_event()


Using Function Definitions

Each function definition includes the elements in the following table:

Element	Description
Title	Function name
Definition	Main purpose of function
Syntax	Diagram of the function and parameters
Parameters	Table describing each parameter
Description	Detailed information about using the function
Example	Example of using the function in a program
Return Value	Description of possible values returned in the status vector, if any
See Also	Cross references to other related functions


Array Functions

The following table summarizes the InterBase API functions available for handling array data in an application:

Function Name	Purpose
isc_array_get_slice()	Retrieve a specified part of an array field.
isc_array_lookup_bounds()	Determine the dimensions of an array field.
isc_array_lookup_desc()	Retrieve an array description.
isc_array_put_slice()	Write a specified part of an array field.
isc_array_set_desc()	Set an array description.


BLOB Functions

The following table summarizes the InterBase API functions available for handling BLOB data in an application:

Function Name	Purpose
isc_blob_default_desc()	Set a default BLOB description for dynamic access.
isc_blob_gen_bpb()	Generate a BLOB parameter buffer (BPB) for dynamic access.
isc_blob_info()	Request information about a BLOB field.
isc_blob_lookup_desc()	Retrieve a BLOB description from the user-defined BLOB parameter buffer.
isc_blob_set_desc()	Define BLOB filter attributes in a user-defined BLOB parameter buffer.
isc_cancel_blob()	Perform garbage collection on BLOB data.
isc_close_blob()	Close a BLOB.
isc_create_blob2()	Create a new BLOB.
isc_get_segment()	Retrieve a segment of BLOB data.
isc_open_blob2()	Open a BLOB for access.
isc_put_segment()	Write a segment of BLOB data.


Conversion Functions

The following table summarizes the InterBase API functions available for translating between InterBase DATE format and the Unix tm date format, and for reversing the byte order of an integer:

Function Name	Purpose
isc_decode_date()	Translate a date from InterBase format to C tm format.
isc_encode_date()	Translate a date from C tm format to InterBase format.
isc_vax_integer()	Reverse the byte order of an integer.


Database Functions

The following table summarizes the InterBase API functions available for handling database requests in an application:

Function Name	Purpose
isc_attach_database()	Connect to an existing database.
isc_database_info()	Request information about an attached database.
isc_detach_database()	Disconnect from a database.
isc_drop_database()	Delete an attached database and its associated files.
isc_expand_dpb()	Build a database parameter buffer dynamically.
isc_version()	Retrieve database implementation number and on-disk structure (ODS) major and minor version numbers.


DSQL Functions

The following table summarizes the InterBase API functions available for handling DSQL statements built or entered by users at run time:

Function Name	Purpose
isc_dsql_allocate_statement()	Allocate a statement handle.
isc_dsql_alloc_statement2()	Allocate a statement handle that is automatically freed on database detachment.
isc_dsql_describe()	Fill in an XSQLDA with information about values returned by a statement.
isc_dsql_describe_bind()	Fill in an XSQLDA with information about a statement’s input parameters.
isc_dsql_execute()	Execute a prepared statement.
isc_dsql_execute2()	Execute a prepared statement returning a single set of values.
isc_dsql_execute_immediate()	Prepare and execute a statement without return values for one-time use.
isc_dsql_exec_immed2()	Prepare and execute a statement with a single set of return values for one-time use.
isc_dsql_fetch()	Retrieve data returned by a previously prepared and executed statement.
isc_dsql_free_statement()	Free a statement handle, or close a cursor associated with a statement handle.
isc_dsql_prepare()	Prepare a statement for execution.
isc_dsql_set_cursor_name()	Define a cursor name and associate it with a statement handle.
isc_dsql_sql_info()	Request information about a prepared statement.


Error-handling Functions

The following table summarizes the InterBase API functions available for handling database error conditions in an application:

Function Name	Purpose
isc_interprete()	Capture InterBase error messages to a buffer.
isc_print_sqlerror()	Display an SQL error message.
isc_print_status()	Display InterBase error messages.
isc_sqlcode()	Set the value of SQLCODE.
isc_sql_interprete()	Capture an SQL error message to a buffer.


Event Functions

The following table summarizes the InterBase API functions available for handling events in an application:

Function Name	Purpose
isc_cancel_events()	Cancel interest in an event.
isc_event_block()	Allocate event parameter buffers.
isc_event_counts()	Get the change in values of event counters in the event array.
isc_que_events()	Wait asynchronously until an event is posted.
isc_wait_for_event()	Wait synchronously until an event is posted.


Information Functions

The following table summarizes the InterBase API functions available for retrieving information about databases, transactions, BLOB data, and events:

Function Name	Purpose
isc_blob_info()	Request information about a BLOB field.
isc_database_info()	Request information about an attached database.
isc_dsql_sql_info()	Request information about a prepared DSQL statement.
isc_transaction_info()	Request information about a specified transaction.
isc_version()	Retrieve database implementation number and on-disk structure (ODS) major and minor version numbers.


Transaction Control Functions

The following table summarizes the InterBase API functions available for controlling transactions in an application:

Function Name	Purpose
isc_commit_retaining()	Commit a transaction, and start a new one using the original transaction’s context.
isc_commit_transaction()	Save a transaction’s database changes and end the transaction.
isc_prepare_transaction()	Execute the first phase of a two-phase commit.
isc_prepare_transaction2()	Execute the first phase of a two-phase commit.
isc_rollback_transaction()	Undo a transaction’s database changes, and end the transaction.
isc_start_multiple()	Begin new transactions (used on systems that do not support a variable number of input arguments).
isc_start_transaction()	Begin new transactions.
isc_transaction_info()	Request information about a specified transaction.


isc_array_get_slice( )

Syntax	Example	See Also

Description

isc_array_get_slice() retrieves data from an array column of a table row using an array ID. You can either retrieve all the array elements in that column, or a subset of contiguous array elements, called a slice. The upper and lower boundaries in the desc structure specify which elements are to be retrieved.

InterBase copies the elements into the buffer, dest_array, whose size is specified by slice_length. This should be at least the expected length required for the elements retrieved. Before returning from isc_array_get_slice(), InterBase sets slice_length to the actual number of bytes copied.

Before calling isc_array_get_slice(), there are many operations you must do in order to fill in the array descriptor, desc, determine the appropriate internal array identifier, array_id, and fetch the rows whose array columns you want to access.

Return Value

isc_array_get_slice() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_trans_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_array_lookup_bounds()

isc_array_lookup_desc()

isc_array_put_slice()

isc_array_set_desc()

isc_dsql_fetch()

isc_dsql_prepare()


Operations Required to Retrieve Array Data

There are seven steps required for reading data from an array or slice of an array:

1.	Create a SELECT statement that specifies selection of the array column (and any other columns desired) in the rows of interest.

2.	Prepare an output XSQLDA structure to hold the column data for each row that is fetched.

3.	Prepare the SELECT statement for execution.

4.	Execute the statement.

5.	Populate an array descriptor with information describing the array or array slice to be retrieved.

6.	Fetch the selected rows one by one.

7.	Read and process the array data from each row.

Creating the SELECT Statement

Elicit a statement string from the user or create one that consists of the SQL query that will select rows containing the array data of interest. In your query, specify the array column name and the names of any other columns containing data you are interested in. For example, the following creates an SQL query statement string that selects an array column named QUART_HEAD_CNT and another column named DEPT_NO from the table, PROJ_DEPT_BUDGET:

char *sel_str =

"SELECT DEPT_NO, QUART_HEAD_CNT FROM PROJ_DEPT_BUDGET \

WHERE year = 1994 AND PROJ_ID = 'VBASE'";

Preparing the Output XSQLDA

Most queries return one or more rows of data, referred to as a select-list. An output XSQLDA must be created to store the column data for each row that is fetched. For an array column, the column data is an internal array identifier (array ID) that is needed to access the actual data. To prepare the XSQLDA, follow these steps:

1.	Declare a variable to hold the XSQLDA. For example, the following declaration creates an XSQLDA called out_sqlda:

XSQLDA *out_sqlda;

2.	Allocate memory for the XSQLDA using the XSQLDA_LENGTH macro. The XSQLDA must contain one XSQLVAR substructure for each column to be fetched. The following statement allocates storage for an output XSQLDA (out_sqlda) with two XSQLVAR substructures:

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

3.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field of the XSQLDA to indicate the number of XSQLVAR substructures allocated:

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 2;

Preparing the SELECT Statement for Execution

After an XSQLDA is created for holding the column data for each selected row, the query statement string can be prepared for execution. Follow these steps:

1.	Declare and initialize an SQL statement handle, then allocate it with isc_dsql_allocate_statement():

isc_stmt_handle stmt; 	/* Declare a statement handle. */

stmt = NULL; 	/* Set handle to NULL before allocation. */

isc_dsql_allocate_statement(status_vector, &db_handle, &stmt);

2.	Ready the statement string for execution with isc_dsql_prepare(). This checks the string (sel_str) for syntax errors, parses it into a format that can be efficiently executed, and sets the statement handle (stmt) to refer to this parsed format. The statement handle is used in a later call to isc_dsql_execute().

If isc_dsql_prepare() is passed a pointer to the output XSQLDA, as in the following example, it will fill in most fields of the XSQLDA and all its XSQLVAR substructures with information such as the data type, length, and name of the corresponding columns in the statement.

A sample call to isc_dsql_prepare() is:

isc_dsql_prepare(

status_vector,

&trans,	/* Set by previous isc_start_transaction() call. */

&stmt,	/* Statement handle set by this function call. */

0,	/* Specifies statement string is null-terminated. */

sel_str,	/* Statement string. */

1,	/* XSQLDA version number. */

out_sqlda	/* XSQLDA for storing column data. */

);

3.	Set up an XSQLVAR structure for each column. Setting up an XSQLVAR structure involves the following steps:

For columns whose types are known at compile time:

Specify the column's data type (if it was not set by isc_dsql_prepare(), as previously described).

Point the sqldata field of the XSQLVAR to an appropriate local variable.

For columns whose types are not known until run time:

Coerce the item's data type (optional); for example, from SQL_VARYING to SQL_TEXT.

Dynamically allocate local storage for the data pointed to by the sqldata field of the XSQLVAR.

For both:

Provide a NULL value indicator for the parameter.

Data retrieval for array (and BLOB) columns is different from other types of columns, so the XSQLVAR fields must be set differently. For non-array (and non-BLOB) columns, isc_dsql_prepare() sets each XSQLVAR sqltype field to the appropriate field type, and the data retrieved when a select list row's data is fetched is placed into the sqldata space allocated for the column. For array columns, the type is set to SQL_ARRAY (or SQL_ARRAY + 1 if the array column is allowed to be NULL). InterBase stores the internal array identifier (array ID), not the array data, in the sqldata space when a row's data is fetched, so you must point sqldata to an area the size of an array ID.

The following code example illustrates the assignments for array and non-array columns whose types are known at compile time:

ISC_QUAD array_id = 0L;

char dept_no[6];

short flag0, flag1;

out_sqlda->sqlvar[0].sqldata = (char *) dept_no;

out_sqlda->sqlvar[0].sqltype = SQL_TEXT + 1;

out_sqlda->sqlvar[0].sqlind = &flag0;

out_sqlda->sqlvar[1].sqldata = (char *) &array_id;

out_sqlda->sqlvar[1].sqltype = SQL_ARRAY + 1;

out_sqlda->sqlvar[1].sqlind = &flag1;

Executing the Statement

Once the query statement string is prepared, it can be executed:

isc_dsql_execute(

status_vector,

&trans,	/* set by previous isc_start_transaction() call */

&stmt,	/* set above by isc_dsql_prepare() */

1,	/* XSQLDA version number */

NULL	/* NULL since stmt doesn't have input values */

);

This statement creates a select-list, the rows returned by execution of the statement.

Populating the Array Descriptor

To prepare an array descriptor that describes the array or array slice to be read, follow these steps:

1.	Create the array descriptor:

ISC_ARRAY_DESC desc;

2.	Fill in the descriptor with information regarding the array column from which data will be read. Do this either by calling one of the functions isc_array_lookup_bounds(), isc_array_lookup_desc(), or isc_array_set_desc(), or by directly filling in the descriptor.

Ensure the descriptor boundaries are set to those of the slice to be read.

If you want to retrieve all the array data (that is, not just a smaller slice), set the boundaries to the full boundaries as initially declared for the array column. This is guaranteed to be the case if you fill in the descriptor by calling isc_array_lookup_bounds(), as in:

ISC_ARRAY_DESC desc;

isc_array_lookup_bounds(

status_vector,

&db_handle,

&trans,

"PROJ_DEPT_BUDGET",	/* table name */

"QUART_HEAD_CNT",	/* array column name */

&desc);

Suppose the array column, QUART_HEAD_CNT, is a one-dimensional array consisting of four elements, and it was declared to have a lower subscript bound of 1 and an upper bound of 4 when it was created. Then after the above call to isc_array_lookup_bounds(), the array descriptor fields for the boundaries contain the following information:

desc.array_desc_bounds[0].array_bound_lower == 1

desc.array_desc_bounds[0].array_bound_upper == 4

If you want to read just a slice of the array, then modify the upper and/or lower bounds appropriately. For example, if you just want to read the first two elements of the array, then modify the upper bound to the value 2, as in:

desc.array_desc_bounds[0].array_bound_upper = 2

Fetching Selected Rows

A looping construct is used to fetch (into the output XSQLDA) the column data for a single row at a time from the select-list and to process each row before the next row is fetched. Each execution of isc_dsql_fetch() fetches the column data for the next row into the corresponding XSQLVAR structures of out_sqlda. For the array column, the array ID, not the actual array data, is fetched.

ISC_STATUS fetch_stat;

long SQLCODE;

. . .

while ((fetch_stat =

isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda))

== 0)

{

/* Read and process the array data */

}

if (fetch_stat != 100L)

{

/* isc_dsql_fetch returns 100 if no more rows remain to be

retrieved */

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

return(1);

}

Reading and Processing the Array Data

To read and process the array or array slice data:

1.	Create a buffer for holding the array data to be read. Make it large enough to hold all the elements in the slice to be read (which could be the entire array). For example, the following declares an array buffer large enough to hold 4 long elements:

long hcnt[4];

2.	Declare a short variable for specifying the size of the array buffer:

short len;

3.	Set the variable to the buffer length:

len = sizeof(hcnt);

4.	Read the array or array slice data into the buffer by calling isc_array_get_slice(). Process the data read. In the following example, the array is read into the hcnt array buffer, and "processing" consists of printing the data:

isc_array_get_slice(

status_vector,

&db_handle,	/* set by isc_attach_database()*/

&trans,	/* set by isc_start_transaction() */

&array_id, 	/* array ID put into out_sqlda by isc_dsql_fetch()*/

&desc,	/* array descriptor specifying slice to be read */

(void *) hcnt,	/* buffer into which data will be read */

(long *) &len	/* length of buffer */

);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}

/* Make dept_no a null-terminated string */

dept_no[out_sqlda->sqlvar[0].sqllen] = '\0';

printf("Department #: %s\n\n", dept_no);

printf("\tCurrent head counts: %ld %ld %ld %ld\n",

hcnt[0], hcnt[1], hcnt[2], hcnt[3]);


isc_array_get_slice( ) Syntax

Example	Definition

ISC_STATUS isc_array_get_slice(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_tr_handle *trans_handle,
ISC_QUAD *array_id,
ISC_ARRAY_DESC *desc,
void *dest_array,
ISC_LONG *slice_length);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database containing the array column. db_handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
array_id	Internal identifier for the array. The array ID must be previously retrieved through API DSQL functions.
desc	Descriptor defining the array slice (entire array or subset) to be retrieved.
dest_array	Pointer to a buffer of length slice_length into which the array slice will be copied by this function.
slice_length	Length, in bytes, of the dest_array buffer.


isc_array_get_slice( ) Example

Syntax	Definition

The following program operates on a table named PROJ_DEPT_BUDGET. This table contains the quarterly head counts allocated for each project in each department of an organization. Each row of the table applies to a particular department and project. The quarterly head counts are contained in an array column named QUARTERLY_HEAD_CNT. Each row has four elements in this column, one per quarter. Each element of the array is a number of type long.

The program selects the rows containing 1994 information for the project named VBASE. For each such row, it retrieves and prints the department number and the data in the array column (i.e., the quarterly head counts).

In addition to illustrating the usage of isc_array_lookup_bounds() and isc_array_get_slice(), the program shows data structure initializations and calls to the DSQL functions required to prepare and execute the SELECT statement, to obtain the array_id needed by isc_array_get_slice(), and to fetch the selected rows one by one.

#include <ibase.h>

#define Return_if_Error(stat) if (stat[0] == 1 && stat[1]) \

{ \

isc_print_status(stat); \

return(1); \

}

char *sel_str =

"SELECT dept_no, quarterly_head_cnt FROM proj_dept_budget \

WHERE year = 1994 AND proj_id = 'VBASE'";

char dept_no[6];

long hcnt[4], tr_handle, database_handle, SQLCODE;

short len, i, flag0, flag1;

ISC_QUAD array_id;

ISC_ARRAY_DESC desc;

ISC_STATUS status_vector[20], fetch_stat;

isc_stmt_handle stmt = NULL;

XSQLDA *osqlda;

tr_handle = database_handle = 0L;

/* Attach to a database here--this code omitted for brevity */

/* Start a transaction here--this code omitted for brevity */

/* Set up the SELECT statement. */

/* Allocate the output XSQLDA for holding the array data. */

osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

osqlda->sqln = 2;

osqlda->version = 1;

/* Allocate a statement handle. */

isc_dsql_allocate_statement(

status_vector,

&database_handle,

&stmt);

Return_if_Error(status_vector);

/* Prepare the query for execution. */

isc_dsql_prepare(

status_vector,

&tr_handle,

&stmt,

0,

sel_str,

1,

osqlda);

Return_if_Error(status_vector);

/* Set up an XSQLVAR structure to allocate space for each item

to be retrieved. */

osqlda->sqlvar[0].sqldata = (char *) dept_no;

osqlda->sqlvar[0].sqltype = SQL_TEXT + 1;

osqlda->sqlvar[0].sqlind = &flag0;

osqlda->sqlvar[1].sqldata = (char *) &array_id;

osqlda->sqlvar[1].sqltype = SQL_ARRAY + 1;

osqlda->sqlvar[1].sqlind = &flag1;

/* Execute the SELECT statement. */

isc_dsql_execute(

status_vector,

&tr_handle,

&stmt,

1,

NULL);

Return_if_Error(status_vector);

/* Set up the array descriptor. */

isc_array_lookup_bounds(

status_vector,

&database_handle, /* Set by previous isc_attach_database() call. */

&tr_handle, /* Set by previous isc_start_transaction() call. */

"PROJ_DEPT_BUDGET", /* Table name. */

"QUARTERLY_HEAD_CNT", /* Array column name. */

&desc);

Return_if_Error(status_vector);

/* Fetch the head count for each department's four quarters. */

while ((fetch_stat = isc_dsql_fetch(

status_vector,

&stmt,

1,

osqlda)) == 0)

{

if (!flag1)

{

/* There is array data; get the current values. */

len = sizeof(hcnt);

/* Fetch the data from the array column into hcnt array. */

isc_array_get_slice(

status_vector,

&database_handle,

&tr_handle,

&array_id,

&desc,

hcnt,

&len);

Return_if_Error(status_vector);

/* Print department number and head counts. */

dept_no[osqlda->sqlvar[0].sqllen] = '\0';

printf("Department #: %s\n\n", dept_no);

printf("\tCurrent counts: %d %d %d %d\n",

hcnt[0], hcnt[1], hcnt[2], hcnt[3]);

};

}

if (fetch_stat != 100L)

{

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

return(1);

}


isc_array_lookup_bounds( )

Syntax	Example	See Also

Description

isc_array_lookup_bounds() determines the data type, length, scale, dimensions, and array boundaries for the elements in an array column, column_name in the table, table_name. It stores this information in the array descriptor, desc.

isc_array_lookup_bounds() also sets to 0 a flag in the descriptor. This specifies that the array should be accessed in future function calls in row-major order, the default. If an application requires column-major access, reset this flag to 1.

The array descriptor is used in subsequent calls to isc_array_get_slice() or isc_array_put_slice().

Note:	There are ways to fill in an array descriptor other than by calling isc_array_lookup_bounds(). You can also:

Call isc_array_lookup_desc(). This is exactly the same as isc_array_lookup_bounds(), except that it does not fill in information about the upper and lower bounds of each dimension.

Call isc_array_set_desc() to initialize the descriptor from parameters you call it with, rather than accessing the database metadata.

Set the descriptor fields directly.

Return Value

isc_array_lookup_bounds() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_trans_handle, isc_fld_not_def, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_array_get_slice()

isc_array_lookup_desc()

isc_array_put_slice()

isc_array_set_desc()


isc_array_lookup_bounds( ) Syntax

Example	Definition

ISC_STATUS isc_array_lookup_bounds(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_tr_handle *trans_handle,
char *table_name,
char *column_name,
ISC_ARRAY_DESC *desc);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database containing the array column. db_handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
table_name	Name of the table containing the array column, column_name. This may be either null-terminated or blank-terminated.
column_name	Name of the array column. This may be either null-terminated or blank-terminated.
desc	Pointer to an array descriptor that will be filled in by this function.


isc_array_lookup_bounds( ) Example

Syntax	Definition

The following illustrates a sample call to isc_array_lookup_bounds(). More complete examples of accessing arrays are found in the sample programs for isc_array_get_slice() and isc_array_put_slice().

#include <ibase.h>

ISC_STATUS status_vector[20];

ISC_ARRAY_DESC desc;

char str1 = "PROJ_DEPT_BUDGET";

char str2 = "QUARTERLY_HEAD_CNT";

if (isc_array_lookup_bounds(

status_vector,

&database_handle, /* Set in previous isc_attach_database() call. */

&tr_handle, /* Set in previous isc_start_transaction() call. */

str1,

str2,

&desc))

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}


isc_array_lookup_desc( )

Syntax	Example	See Also

Description

isc_array_lookup_desc() determines the data type, length, scale, and dimensions for the array column, column_name, in the table, table_name. It stores this information in the array descriptor, desc.

It also sets to 0 a flag in the descriptor. This specifies that the array is accessed in future function calls in row-major order, the default. If an application requires column-major access, reset this flag to 1.

The array descriptor is used in subsequent calls to isc_array_get_slice() or isc_array_put_slice().

Note:	There are ways to fill in an array descriptor other than by calling isc_array_lookup_desc(). You can also:

Call isc_array_lookup_bounds(). This is like isc_array_lookup_desc(), except that isc_array_lookup_bounds() also fills in information about the upper and lower bounds of each dimension.

Call isc_array_set_desc(), to initialize the descriptor from parameters you call it with, rather than accessing the database metadata.

Set the descriptor fields directly.

Return Value

isc_array_lookup_desc() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_trans_handle, isc_fld_not_def, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_array_get_slice()

isc_array_lookup_bounds()

isc_array_put_slice()

isc_array_set_desc()


isc_array_lookup_desc( ) Syntax

Example	Definition

ISC_STATUS isc_array_lookup_desc(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_tr_handle *trans_handle,
char *table_name,
char *column_name,
ISC_ARRAY_DESC *desc);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database containing the array column. db_handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
table_name	Name of the table containing the array column column_name. This may be either null-terminated or blank-terminated.
column_name	Name of the array column. This may be either null-terminated or blank-terminated.
desc	Pointer to an array descriptor that will be filled in by this function.


isc_array_lookup_desc( ) Example

Syntax	Definition

The following illustrates a sample call to isc_array_lookup_desc(). More complete examples of accessing arrays are found in the sample programs for isc_array_get_slice() and isc_array_put_slice().

#include <ibase.h>

ISC_STATUS status_vector[20];

ISC_ARRAY_DESC desc;

char str1 = "PROJ_DEPT_BUDGET";

char str2 = "QUARTERLY_HEAD_CNT";

if (isc_array_lookup_desc(

status_vector,

&database_handle, /* Set in previous isc_attach_database() call. */

&tr_handle, /* Set in previous isc_start_transaction() call. */

str1,

str2,

&desc))

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

};


isc_array_put_slice( )

Syntax	Example	See Also

Description

isc_array_put_slice() writes data into an array column. You can either store into all the array elements in that column, or into an array slice, a subset of contiguous array elements. The boundaries passed to the function in the array descriptor, desc, specify which elements are to be stored into.

InterBase copies the elements from the buffer, source_array, whose size is specified by slice_length.

The array identifier (array ID), array_id, should be passed as NULL if you are calling isc_array_put_slice() to create a new array. If you are calling it to modify an existing array, then array_id should be the identifier of the array to be modified. This must have been determined by previous calls to DSQL functions.

Before calling isc_array_put_slice(), there are many operations you must do in order to fill in the array descriptor, desc, determine the appropriate internal array identifier, array_id, and fetch the rows whose array columns you want to access.

Return Value

isc_array_put_slice() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_trans_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_array_get_slice()

isc_array_lookup_bounds()

isc_array_lookup_desc()

isc_array_set_desc()

isc_dsql_allocate_statement()

isc_dsql_describe_bind()

isc_dsql_execute()

isc_dsql_fetch()

isc_dsql_prepare()

isc_dsql_set_cursor_name()


Operations Required to Write Array Data

isc_array_put_slice() is called to write data to an array or array slice. Use it to:

Include a new array in a row to be inserted into a table.

Replace the current contents of an array column of a row with a new array.

Update the array referenced by an array column of a row by modifying the array data or a slice of the data.

The entry in an array column of a row does not actually contain array data. Rather, it has an array ID referring to the data, which is stored elsewhere. So, to set or modify an array column, you need to set or change the array ID stored in it. If an array column contains an array ID, and you modify the column to refer to a different array (or to contain NULL), the array referenced by the previously stored array ID will be deleted during the next garbage collection.

The following steps are required to insert, replace, or update array data:

1.	Prepare an array descriptor with information describing the array (or slice) to be written to.

2.	Prepare an array buffer with the data to be written.

3.	Prepare an appropriate DSQL statement. This will be an INSERT statement if you are inserting a new row into a table, or an UPDATE statement for modifying an existing row.

4.	Call isc_array_put_slice() to create a new array (possibly copying an existing one), and to write the data from the array buffer into the array or array slice.

5.	Associate the new array with an array column of the table row being modified or inserted by executing the UPDATE or INSERT statement. This sets the array column to contain the array ID of the new array.

Preparing the Array Descriptor

To prepare an array descriptor that specifies the array or array slice to be written to, follow these steps:

1.	Create the array descriptor:

ISC_ARRAY_DESC desc;

2.	Fill in the descriptor with information regarding the array column to which data will be written. Do this either by calling one of the functions isc_array_lookup_bounds(), isc_array_lookup_desc(), or isc_array_set_desc(), or by directly filling in the descriptor.

Ensure the descriptor boundaries are set to those of the slice to be written to.

If you want to write to the entire array (i.e., not just a smaller slice), set the boundaries to the full boundaries as initially declared for the array column. This is guaranteed to be the case if you fill in the descriptor by calling isc_array_lookup_bounds(), as in:

isc_array_lookup_bounds(

status_vector,

db_handle,

&trans,

"PROJ_DEPT_BUDGET",	/* table name */

"QUART_HEAD_CNT",	/* array column name */

&desc);

Suppose the array column, QUART_HEAD_CNT, is a one-dimensional array consisting of four elements, and it was declared to have a lower subscript bound of 1 and an upper bound of 4 when it was created. Then after a call to isc_array_lookup_bounds(), the array descriptor fields for the boundaries contain the following information:

desc.array_desc_bounds[0].array_bound_lower == 1

desc.array_desc_bounds[0].array_bound_upper == 4

If you just want to write to (or modify) a slice of the array, then change the upper and lower bound appropriately. For example, if you just want to write to the first two elements of the array, then modify the upper bound to the value 2, as in:

desc.array_desc_bounds[0].array_bound_upper == 2

Preparing the Array Buffer With Data

1.	Create an array buffer to hold the data to be written to the array. Make it large enough to hold all the elements in the slice to be written (which could be the entire array). For example, the following declares an array buffer large enough to hold 4 long elements:

long hcnt[4];

2.	Create a variable specifying the length of the array buffer:

short len;

len = sizeof(hcnt);

3.	Fill the array buffer with the data to be written.

If you are creating a new array, then fill the buffer with data. For example,

hcnt[0] = 4;

hcnt[1] = 5;

hcnt[2] = 6;

hcnt[3] = 6;

To modify existing array data instead of creating a new one, then perform all the steps listed above under "Reading Data from an Array" to read the existing array data into the array buffer. Modify the data in the buffer.

Preparing the UPDATE or INSERT Statement

To prepare an UPDATE or INSERT statement for execution, follow these steps:

1.	Elicit an UPDATE or INSERT statement string from the user or create one for inserting a new row or updating the row(s) containing the array column(s) of interest. For example, the following statement is for updating the array column named QUART_HEAD_CNT in the specified row of the table, PROJ_DEPT_BUDGET. The department number and quarterly head counts are assumed to be supplied at run time:

char *upd_str =

"UPDATE PROJ_DEPT_BUDGET SET QUART_HEAD_CNT = ? WHERE \

YEAR = 1994 AND PROJ_ID = "MKTPR" AND DEPT_NO = ?";

As an example of an INSERT statement, the following is for inserting a new row into the PROJ_DEPT_BUDGET table, with column data supplied at run time:

char *upd_str =

"INSERT INTO PROJ_DEPT_BUDGET (YEAR, PROJ_ID, DEPT_NO, \

QUART_HEAD_CNT) VALUES (?, ?, ?, ?)";

The remaining steps refer only to UPDATE statements, but the actions apply to INSERT statements as well.

2.	Declare a variable to hold the input XSQLDA needed to supply parameter values to the UPDATE statement at run time. For example, the following declaration creates an XSQLDA called in_sqlda:

XSQLDA *in_sqlda;

3.	Allocate memory for the input XSQLDA using the XSQLDA_LENGTH macro. The XSQLDA must contain one XSQLVAR substructure for each parameter to be passed to the UPDATE statement. The following statement allocates storage for an input XSQLDA (in_sqlda) with two XSQLVAR substructures:

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

4.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field to indicate the number of XSQLVAR structures allocated:

in_sqlda->version = SQLDA_VERSION1;

in_sqlda->sqln = 2;

5.	Set up the XSQLVAR structure in the XSQLDA for each parameter to be passed. Setting up an XSQLVAR structure involves the following steps:

Specifying the item's data type.

For parameters whose types are known at compile time: Point the sqldata field of the XSQLVAR to an appropriate local variable that will contain the data to be passed.

For parameters whose types are not known until run time: Allocate local storage for the data pointed to by the sqldata field of the XSQLVAR.

Specifying the number of bytes of data.

Data storage for array (and BLOB) columns is different from other types of columns, so the XSQLVAR fields must be set differently. For non-array (and non-BLOB) columns, input parameter data comes from the space pointed to by sqldata. For array columns, set the type to SQL_ARRAY (or SQL_ARRAY + 1 if the array column is allowed to be NULL). The application must store space for the internal array identifier, not the array data, in the sqldata space. See the following sections to create or modify an array, store its array ID in the sqldata space, and then associate the actual array data with the column.

The following code example illustrates the assignments for one TEXT column and one array column, where the column types are known at compile time.

#define NUMLEN 4

char dept_no[NUMLEN + 1];

ISC_QUAD array_id;

in_sqlda->sqlvar[0].sqldata = &array_id;

in_sqlda->sqlvar[0].sqltype = SQL_ARRAY + 1;

in_sqlda->sqlvar[0].sqllen = sizeof(ISC_QUAD);

in_sqlda->sqlvar[1].sqldata = dept_no;

in_sqlda->sqlvar[1].sqltype = SQL_TEXT;

in_sqlda->sqlvar[1].sqllen = 4;

The dept_no variable should be assigned a value at run time (unless the value is known at compile time). The array_id variable should be set to refer to the newly created array, as described in the following sections.

Calling isc_array_put_slice()

The following steps are required to store the data into an array or array slice:

1.	Declare an array ID:

ISC_QUAD array_id; 	/* Declare an array ID. */

2.	Initialize the array ID. If you are creating a new array to be inserted into a new row, or to replace an existing array, then simply initialize the array ID to NULL:

array_id = NULL;	/* Set handle to NULL before using it */

If you are modifying an existing array, then follow the steps listed under "Reading Data from an Array" to read the existing array ID into array_id.

3.	Call isc_array_put_slice(). In your call you pass the array ID (either the array ID of an existing array, or NULL for a new array) in the array_id variable. You also pass the buffer of data to be written and a descriptor specifying the array slice to which the data belongs.

When isc_array_put_slice() is called with an array ID of an existing array, it creates a new array with the same characteristics as the specified array, and copies the existing array data to the new array. Then isc_array_put_slice() writes the data from the array buffer to the new array (or slice of the array), per the bounds specified in the array descriptor, and returns in the same array_id variable the array ID of the new array.

When isc_array_put_slice() is called with a NULL array ID, it creates a new empty array with characteristics as declared for the array column whose name and table name are specified in the array descriptor passed to isc_array_put_slice(). It then writes the data from the array buffer to the new array (or slice of the array), and returns in the array_id variable the array ID of the new array.

Note that in both cases, a new array is created, and its array ID is returned in the array_id variable. The array is temporary until an UPDATE or INSERT statement is executed to associate the array with a particular column of a particular row.

You can make a single call to isc_array_put_slice() to write all the data to the array. Or, you may call isc_array_put_slice() multiple times to store data into various slices of the array. In this case, each call to isc_array_put_slice() after the first call should pass the array ID of the temporary array. When isc_array_put_slice() is called with the array ID of a temporary array, it copies the specified data to the specified slice of the temporary array, but does not create a new array.

The following is a sample call to isc_array_put_slice():

isc_array_put_slice(

status_vector,

&db_handle,

&trans,

&array_id,	/* array ID (NULL, or existing array's array ID) */

&desc,	/* array descriptor describing where to write data */

hcnt, 	/* array buffer containing data to write to array */

&len 	/* length of array buffer */

);

This call creates a new array, copies the data in hcnt to the new array (or slice of the array), assigns the array an array ID, and sets array_id to point to the array ID.

Important:	array_id should be the variable pointed to by the sqldata field of the UPDATE (or INSERT) statement input parameter that specifies the array column to be updated. Thus, when the INSERT or UPDATE statement is executed, this new array's array ID will be used to set or update the array column to refer to the new array.

Associating the New Array With the Array Column

Execute the UPDATE statement to associate the new array with the array column in the row selected by the statement:

isc_dsql_execute_immediate(

status_vector,

&db_handle,

&trans,

0,	/* indicates string to execute is null-terminated */

upd_str,	/* UPDATE statement string to be executed */

1,	/* XSQLDA version number */

in_sqlda	/* XSQLDA supplying parameters to UPDATE statement */

);

This sets the array column in the row specified in the UPDATE statement to contain the array ID of the new array. The array ID comes from the array_id variable pointed to by the in_sqlda parameter corresponding to the array column.

If the array column in the specified row contains the array ID of a different array before the UPDATE statement is executed, then the column is modified to contain the new array ID, and the array referenced by the previously stored array ID will be deleted during the next garbage collection.


isc_array_put_slice( ) Syntax

Example	Definition

ISC_STATUS isc_array_put_slice(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_tr_handle *trans_handle,
ISC_QUAD *array_id,
ISC_ARRAY_DESC *desc,
void *source_array,
ISC_LONG *slice_length);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database containing the array column. db_handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
array_id	On input, NULL (if you are creating a new array), or the internal identifier for an array to be modified, as assigned by the InterBase engine. This internal identifier must have been determined by previous calls to DSQL functions. This function changes array_id to be the identifier for the array it creates or modifies.
desc	Descriptor defining the array slice (entire array or subset) to be written to.
source_array	Pointer to a buffer of length slice_length which contains the slice of data that will be copied to the array by this function.
slice_length	Length, in bytes, of the source_array buffer.


isc_array_put_slice( ) Example

Syntax	Definition

The following program operates on a table named PROJ_DEPT_BUDGET. This table contains the quarterly head counts allocated for each project in each department of an organization. Each row of the table applies to a particular department and project. The quarterly head counts are contained in an array column named QUARTERLY_HEAD_CNT. Each row has four elements in this column, one per quarter. Each element is a number of type long.

This program selects the rows containing 1994 information for the project named VBASE. For each such row, it calls isc_array_get_slice() to retrieve a slice of the array, the quarterly head counts for the last two quarters. It then increments each, and calls isc_array_put_slice() to store the updated values.

In addition to illustrating the usage of isc_array_lookup_desc(), isc_array_get_slice(), and isc_array_put_slice(), the program shows data structure initializations and calls to the DSQL functions required to prepare and execute the SELECT and UPDATE statements, to obtain the array_id needed by isc_array_get_slice() and isc_array_put_slice(), to fetch the selected rows one by one, and to update the array ID.

#include <ibase.h>

#define Return_if_Error(stat) if (stat[0] == 1 && stat[1]) \

{ \

isc_print_status(stat); \

return(1); \

}

char *sel_str =

"SELECT dept_no, quarterly_head_cnt FROM proj_dept_budget \

WHERE year = 1994 AND proj_id = 'VBASE'";

char *upd_str =

"UPDATE proj_dept_budget SET quarterly_head_count = ? \

WHERE CURRENT OF S";

char dept_no[6];

long fetch_stat, hcnt[2];

short len, i, flag0, flag1, flag2;

ISC_QUAD array_id;

ISC_ARRAY_DESC desc;

ISC_STATUS status_vector[20];

isc_stmt_handle stmt = NULL;

isc_stmt_handle ustmt = NULL;

char *cursor = "S";

XSQLDA *osqlda, *isqlda;

/* Set up the SELECT statement. */

/* Allocate the output XSQLDA for holding the array data. */

osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

osqlda->sqln = 2;

osqlda->version = SQLDA_VERSION1;

/* Allocate a statement handle for the SELECT statement. */

if (isc_dsql_allocate_statement(

status_vector, &database_handle, &stmt));

{Return_if_Error(status_vector)};

/* Prepare the query for execution. */

if (isc_dsql_prepare(

status_vector,

&tr_handle,

&stmt,

0,

sel_str,

1,

osqlda))

{Return_if_Error(status_vector)};

/* Set up an XSQLVAR structure to allocate space for each item to be retrieved. */

osqlda->sqlvar[0].sqldata = (char *) dept_no;

osqlda->sqlvar[0].sqltype = SQL_TEXT + 1;

osqlda->sqlvar[0].sqlind = &flag0;

osqlda->sqlvar[1].sqldata = (char *) &array_id;

osqlda->sqlvar[1].sqltype = SQL_ARRAY + 1;

osqlda->sqlvar[1].sqlind = &flag1;

/* Execute the SELECT statement. */

if (isc_dsql_execute(

status_vector,

&tr_handle,

&stmt,

1,

NULL))

{Return_if_Error(status_vector)};

/* Declare a cursor. */

if (isc_dsql_set_cursor_name(

status_vector, &stmt, cursor, 0))

{Return_if_Error(status_vector)};

/* Set up the UPDATE statement */

/* Allocate a statement handle for the UPDATE statement. */

if (isc_dsql_allocate_statement(

status_vector, &database_handle, &ustmt));

{Return_if_Error(status_vector)};

/* Allocate the input XSQLDA. */

isqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

isqlda->sqln = 1;

1sqlda->version = SQLDA_VERSION1;

/* Prepare the UPDATE statement for execution. */

if (isc_dsql_prepare(

status_vector,

&tr_handle,

&ustmt,

0,

upd_str,

1,

NULL))

{Return_if_Error(status_vector)};

/* Initialize the input XSQLDA. */

if (isc_dsql_describe_bind(

status_vector, &ustmt, 1, isqlda))

{Return_if_Error(status_vector)};

/* Set up the input sqldata and sqlind fields. */

isqlda->sqlvar[0].sqldata = (char *) &array_id;

isqlda->sqlvar[0].sqlind = &flag2;

/* Set up the array descriptor. */

if (isc_array_lookup_desc(

status_vector,

&database_handle, /* Set by previous isc_attach_database() call. */

&tr_handle, /* Set by previous isc_start_transaction() call. */

"PROJ_DEPT_BUDGET", /* Table name. */

"QUARTERLY_HEAD_CNT", /* Array column name. */

&desc))

{Return_if_Error(status_vector)};

/* Set the descriptor bounds to those of the slice to be updated, i.e., to those of the last two elements. Assuming the array column was defined to contain 4 elements, with a lower bound (subscript) of 1 and an upper bound of 4, the last two elements are at subscripts 3 and 4. */

desc->array_desc_bounds[0].array_bound_lower = 3;

desc->array_desc_bounds[0].array_bound_upper = 4;

/* Fetch and process the rows of interest. */

while ((fetch_stat = isc_dsql_fetch(

status_vector, &stmt, 1, osqlda)) == 0)

{

if (!flag1)

{

/* There is array data; get values for last two quarters. */

len = sizeof(hcnt);

/* Fetch the data from the array slice into hcnt array. */

if (isc_array_get_slice(

status_vector,

&database_handle,

&tr_handle,

&array_id,

&desc,

hcnt,

&len))

{Return_if_Error(status_vector)};

/* Add 1 to each count. */

for (i = 0; i < 2; i++)

hcnt[i] = hcnt[i] + 1;

/* Save new values. */

if (isc_array_put_slice(

status_vector,

&database_handle,

&tr_handle,

&array_id,

&desc,

hcnt,

&len))

{Return_if_Error(status_vector)};

/* Update the array id. */

if (isc_dsql_execute(

status_vector, &tr_handle, &ustmt, 1, isqlda))

{Return_if_Error(status_vector)};

};

};


isc_array_set_desc( )

Syntax	Example	See Also

Description

isc_array_set_desc() initializes the array descriptor, desc, from the function parameters, table_name, column_name, sql_dtype, sql_length, and dimensions.

isc_array_set_desc() also sets to 0 a flag in the descriptor. This specifies that the array is accessed in future function calls in row-major order, the default. If an application requires column-major access, reset this flag to 1.

table_name and column_name can be either null-terminated or blank-terminated. The names stored in the descriptor will be null-terminated.

sql_dtype must be given as an SQL macro constant.

The array descriptor is used in subsequent calls to isc_array_get_slice() or isc_array_put_slice().

Note:	There are ways to fill in an array descriptor other than by calling isc_array_set_desc(). You can also:

Call isc_array_lookup_bounds(). This function is similar to isc_array_lookup_desc(), except that it also fills in information about the upper and lower bounds of each dimension.

Call isc_array_lookup_desc(). This function is similar to isc_array_lookup_bounds(), except that it does not fill in information about the upper and lower bounds of each dimension.

Set the descriptor fields directly. array_desc_dtype must be expressed as a BLR data type, and array_desc_field_name and array_desc_relation_name must be null-terminated.

Return Value

isc_array_set_desc() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_array_get_slice()

isc_array_lookup_bounds()

isc_array_lookup_desc()

isc_array_put_slice()


isc_array_set_desc( ) Syntax

Example	Definition

ISC_STATUS isc_array_get_slice(
ISC_STATUS *status_vector,
char *table_name,
char *column_name,
short *sql_dtype,
short *sql_length,
short *dimensions,
ISC_ARRAY_DESC *desc);

Parameter	Description
status_vector	Pointer to the error status vector.
table_name	Name of the table containing the array column, column_name. This may be either null-terminated or blank-terminated.
column_name	Name of the array column. This may be either null-terminated or blank-terminated.
sql_dtype	Pointer to SQL data type of the array elements.
sql_length	Pointer to length of each array element.
dimensions	Pointer to number of array dimensions.
desc	Array descriptor to be filled in by this function.


isc_array_set_desc( ) Example

Syntax	Definition

The following illustrates a sample call to isc_array_set_desc(). More complete examples of accessing arrays are found in the sample programs for isc_array_get_slice() and isc_array_put_slice().

#include <ibase.h>

ISC_STATUS status_vector[20];

ISC_ARRAY_DESC desc;

if (isc_array_set_desc(

status_vector,

"TABLE1",

"CHAR_ARRAY",

SQL_CHAR,

8,

2,

&desc))

{

/* Process error. */

isc_print_status(status_vector);

return 1;

};


isc_attach_database( )

Syntax	Example	See Also

Description

The isc_attach_database() function connects to an existing database to enable subsequent program access. It also optionally specifies various operational characteristics, such as a user name and password combination for access to a database on a remote server, or the number of database cache buffers to use. These optional characteristics are passed in a database parameter buffer (DPB) supplied and populated by the calling program, either through direct program construction, or by calling isc_expand_dpb() to build the DPB.

A program passes the name of the database file to which to attach in db_name. For programs not written in C, the program must also pass the length, in bytes, of db_name in the db_name_length parameter. C programs should pass a 0 length in this parameter.

If successful, isc_attach_database() assigns a unique ID to db_handle. Subsequent API calls use this handle to identify the database against which they operate.

When finished accessing a database, disconnect from it with isc_detach_database().

Return Value

isc_attach_database() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_detach_database()

isc_expand_dpb()


Database Parameter Buffer

A database parameter buffer (DPB) is a char array consisting of the following:

1.	A byte specifying the format version of the parameter buffer (use the compile-time constant isc_dpb_version1).

2.	A contiguous series of one or more clusters of bytes, each describing a single parameter.

Each cluster consists of the following parts:

1.	A one-byte parameter type. There are compile-time constants defined for all the parameter types (e.g., isc_dpb_num_buffers).

2.	A one-byte number specifying the number of bytes that follow in the remainder of the cluster.

3.	A variable number of bytes, whose interpretation (e.g., as a number or as a string of characters) depends on the parameter type.

Important:	All numbers in the DPB must be represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers should have the sign in the last byte.

DPB parameters have a variety of purposes:

User Validation

Environmental Control

System Management

Shadow Control

Replay Logging System Control

Character Set and Message File Specification

The following table lists the DPB parameters in alphabetical order.

Parameter	Purpose
isc_dpb_activate_shadow	Directive to activate the database shadow, which is an optional, duplicate, in-sync copy of the database.
isc_dpb_begin_log	Activate a replay logging system to keep track of all database calls.
isc_dpb_damaged	Number signifying whether or not the database should be marked as damaged. 1 = mark as damaged 0 = do not mark as damaged
isc_dpb_dbkey_scope	Scope of dbkey context. 0 limits scope to the current transaction, 1 extends scope to the database session.
isc_dpb_delete_shadow	Directive to delete a database shadow connection when it is no longer needed.
isc_dpb_encrypt_key	String encryption key, up to 255 characters.
isc_dpb_force_write	Specifies whether database writes are synchronous or asynchronous. 0 = asynchronous 1 = synchronous
isc_dpb_lc_ctype	String specifying the character set to be utilized.
isc_dpb_lc_messages	String specifying a language-specific message file.
isc_dpb_license	Check an explicit list of license options.
isc_dpb_no_reserve	Reserves a small amount of space on each database page to hold backup versions of records when modifications are made. Reserving space optimizes update activity. 0 (default) = reserve space. 1= do not reserve space.
isc_dpb_num_buffers	Number of database cache buffers to allocate for use with the database. Default: 75.
isc_dpb_password	String password, up to 255 characters.
isc_dpb_password_enc	String encrypted password, up to 255 characters.
isc_dpb_quit_log	Deactivate the replay logging system.
isc_dpb_sys_user_name	String system DBA name, up to 255 characters.
isc_dpb_sys_user_name_enc	Encrypted system DBA user’s name.
isc_dpb_user_name	String user name, up to 255 characters.
isc_dpb_verify	Perform consistency checking of internal structures.
Note:	Even if a parameter is just a directive, and the value is ignored, you must still provide a length and a value part following the parameter type in the buffer. You can simply use zeros for both of these.


isc_attach_database( ) Syntax

Example	Definition

ISC_STATUS isc_attach_database(
ISC_STATUS *status_vector,
short db_name_length,
char *db_name,
isc_db_handle *db_handle,
short parm_buffer_length,
char *parm_buffer);

Parameter	Description
status_vector	Pointer to the error status vector.
db_name_length	Number of bytes in db_name string. If 0, the string is assumed to be null-terminated.
db_name	Database name.
db_handle	Pointer to a database handle. This function sets the pointer value for use by subsequent API calls that require a database handle. If db_handle is not NULL when this function is called, an error is returned in status_vector.
parm_buffer_length	Number of bytes in the database parameter buffer (DPB).
parm_buffer	Address of the DPB.


isc_attach_database( ) Example

Syntax	Definition

The following program fragment attaches to a database named employee.db. In the parameter buffer, it specifies a user name and password. These come from the contents of char * variables named user_name and user_password, respectively.

char dpb_buffer[256], *dpb, *p;

ISC_STATUS status_vector[20];

isc_db_handle handle = NULL;

short dpb_length;

/* Construct the database parameter buffer. */

dpb = dpb_buffer;

*dpb++ = isc_dpb_version1;

*dpb++ = isc_dpb_user_name;

*dpb++ = strlen(user_name);

for (p = user_name; *p;)

*dpb++ = *p++;

*dpb++ = isc_dpb_password;

*dpb++ = strlen(user_password);

for (p = user_password; *p;)

*dpb++ = *p++;

/* An alternate choice for the above construction is to call: isc_expand_dpb(). */

dpb_length = dpb - dpb_buffer;

if (isc_attach_database(

status_vector,

0,

"employee.db",

&handle,

dpb_length,

dpb_buffer))

{

/* An error occurred. */

isc_print_status (status_vector);

exit(1);

}


isc_blob_default_desc( )

Syntax	Example	See Also

Description

isc_blob_default_desc() loads a BLOB descriptor, desc, with the following default information about a BLOB prior to calling isc_blob_gen_bpb() to generate a BLOB parameter buffer (BPB) for the BLOB column being accessed:

Subtype is set to TEXT.

Character set is set to the default character set for the process or database.

Segment size is set to 80 bytes.

isc_blob_default_desc() and three related functions, isc_blob_gen_bpb(), isc_blob_lookup_desc(), and isc_blob_set_desc(), provide dynamic access to BLOB information. In particular, these functions can define and access information about a BLOB for filtering purposes, such as character set information for text BLOB data, and subtype information for text and non-text BLOB data.

isc_blob_default_desc() fills the desc structure with information that is not included in the BPB.

Note:	Creating a BPB and filling it with isc_blob_default_desc() or isc_blob_set_desc() can also reduce an application’s operating overhead because these calls populate the BPB without a call to the InterBase engine.

Return Value

None.


See Also

isc_blob_gen_bpb()

isc_blob_lookup_desc()

isc_blob_set_desc()


isc_blob_default_desc( ) Syntax

Example	Definition

void isc_blob_default_desc(
ISC_BLOB_DESC *desc,
unsigned char *table_name,
unsigned char *column_name);

Parameter	Description
desc	Pointer to a BLOB descriptor.
table_name	Pointer to the source or destination table.
column_name	Pointer to the source or destination column of the BLOB.


isc_blob_default_desc( ) Example

Syntax	Definition

The following fragment loads the BLOB descriptor with default information:

typedef struct

{

short	blob_desc_subtype;

short	blob_desc_charset;

short	blob_desc_segment_size;

unsigned char	blob_desc_field_name[32];

unsigned char	blob_desc_relation_name[32];

ISC_BLOB_DESC;

isc_blob_default_desc(&desc, &relation, &field);


isc_blob_gen_bpb( )

Syntax	Example	See Also

Description

isc_blob_gen_bpb() generates a BLOB parameter buffer (BPB) from subtype and character set information stored in the source BLOB descriptor from_desc and the target (destination) BLOB descriptor to_desc.

A BPB is needed whenever a filter will be used when writing to or reading from a BLOB column. Two BLOB descriptors are needed for filtering: one (from_desc) to describe the filter source data, and the other (to_desc) to describe the destination. The descriptors must have been previously created either directly, or via a call to isc_blob_default_desc(), isc_blob_lookup_desc(), or isc_blob_set_desc().

The BPB generated by isc_blob_gen_bpb() is subsequently needed in calls to isc_open_blob2() or isc_create_blob2() if filtering will be utilized. isc_blob_gen_bpb() generates a BLOB parameter buffer (BPB) with the following format:

Type	Description
isc_bpb_version1	Version of the BPB.
isc_bpb_target_type	Subtype of the target BLOB descriptor.
isc_bpb_source_type	Subtype of the source BLOB descriptor.
isc_bpb_target_interp	Character set of the target BLOB descriptor.
isc_bpb_source_interp	Character set of the source BLOB descriptor.
isc_blob_gen_bpb() and three related functions, isc_blob_default_desc(), isc_blob_lookup_desc(), and isc_blob_set_desc(), provide dynamic access to BLOB information. In particular, you can use these functions to define and access information about BLOB data for filtering purposes, such as character set information for text BLOB data, and subtype information for text and non-text BLOB data.

To generate a BPB with isc_blob_gen_bpb(), follow these steps:

1.	Declare BLOB descriptors and populate them with calls to isc_blob_default_desc() or isc_blob_set_desc().

2.	Declare a BLOB parameter buffer, and pass it to isc_blob_gen_bpb() to generate the BPB.

After the BLOB descriptors and BPB are populated, you can reference the BPB in subsequent calls to isc_create_blob2() and isc_open_blob2().

Return Value

isc_blob_gen_bpb() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_blob_default_desc()

isc_blob_lookup_desc()

isc_blob_set_desc()

isc_create_blob2()

isc_open_blob2()


BLOB Parameter Buffer

The BLOB parameter buffer (BPB) is a variable-length byte vector, declared in an application to store control information for handling BLOB access. The BPB contains a number of constants to specify that control characteristics of a BLOB.

Parameter	Description
isc_bpb_version1	An InterBase version 1 BLOB.
isc_bpb_statement	Indicates the statement that specifies source and target subtypes.
isc_bpb_source_type	Specifies the source subtype.
isc_bpb_target_type	Specifies the target subtype.
isc_bpb_type	Specifies whether the BLOB is segmented.
isc_bpb_type_code	Code for segmented type.
isc_bpb_length	Length of the BPB.
The BPB does not contain character set information and cannot be accessed dynamically.


isc_blob_gen_bpb( ) Syntax

Example	Definition

ISC_STATUS isc_blob_gen_bpb(
ISC_STATUS *status_vector,
ISC_BLOB_DESC *to_desc,
ISC_BLOB_DESC *from_desc,
unsigned short bpb_buffer_length,
unsigned char *bpb_buffer,
unsigned short *bpb_length);

Parameter	Description
status_vector	Pointer to the error status vector.
to_desc	Pointer to the target BLOB descriptor buffer.
from_desc	Pointer to the source BLOB descriptor buffer.
bpb_buffer_length	Length of the BPB buffer.
bpb_buffer	Pointer to the BPB buffer.
bpb_length	Pointer to the length of the BPB.


isc_blob_gen_bpb( ) Example

Syntax	Definition

The following fragment generates the BLOB descriptor:

isc_blob_gen_bpb(status, &to_desc, &from_desc, bpb_length, &buffer, &buf_length);


isc_blob_info( )

Syntax	Example	See Also

Description

isc_blob_info() returns information about an existing BLOB specified by blob_handle. The item-list buffer is an unstructured byte vector. An application lists the items about which it wants information in the item-list buffer.

InterBase returns the requested information to the result buffer as a series of clusters of information, one per item requested. Each cluster consists of three parts:

1.	A one-byte item type. Each is the same as one of the item types in the item-list buffer.

2.	A 2-byte number specifying the number of bytes that follow in the remainder of the cluster.

3.	A value, stored in a variable number of bytes, whose interpretation depends on the item type.

A calling program is responsible for interpreting the contents of the result buffer and for deciphering each cluster as appropriate.

The following table lists items about which information can be requested and returned and of any values reported:

Item	Value
isc_info_blob_num_segments	Report the total number of segments.
isc_info_blob_max_segment	Report the length, in bytes, of the longest segment.
isc_info_blob_total_length	Report the total size, in bytes, of BLOB.
isc_info_blob_type	Report the type of BLOB. 0 (segmented) 1 (reserved)
Note:	All lengths are two bytes.

In addition to the information InterBase returns in response to a request, InterBase can also return one or more status messages to the result buffer.

Return Value

isc_blob_info() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_create_blob2()

isc_open_blob2()


isc_blob_info( ) Syntax

Example	Definition

ISC_STATUS isc_blob_info(
ISC_STATUS *status_vector,
isc_blob_handle *blob_handle,
short item_list_buffer_length,
char *item_list_buffer,
short result_buffer_length,
char *result_buffer);

Parameter	Description
status_vector	Pointer to the error status vector.
blob_handle	Pointer to the BLOB.
item_list_buffer_length	Length of the item-list buffer in which you specify the items for which you want information.
item_list_buffer	Pointer to the item-list buffer.
result_buffer_length	Length of the result buffer into which InterBase returns the requested information.
result_buffer	Pointer to the result buffer.


isc_blob_info( ) Example

Syntax	Definition

The following example retrieves information about the current open BLOB:

static char blob_items[] = {

isc_info_max_segment,

isc_info_number_segments,

isc_info_blob_type};

CHAR blob_info[32];

isc_open_blob2(status_vector, &db, &tr_handle, &blob_handle, blob_id, blength, baddr)

if (status_vector[0]==1 && status_vector[1])

{isc_print_status(status);

return (1);}

isc_blob_info(status, &blob_handle, sizeof(blob_items),blob_items, sizeof(blob_info), blob_info));


isc_blob_lookup_desc( )

Syntax	Example	See Also

Description

isc_blob_lookup_desc() uses the system tables of a database to determine the subtype, character set, and segment size of a BLOB given a table name and BLOB column name.

isc_blob_lookup_desc() and three related functions, isc_blob_default_desc(), isc_blob_gen_bpb(), and isc_blob_set_desc() provide dynamic access to BLOB information. In particular, you can use these functions to define and access information about BLOB data for filtering purposes, such as character set information for text BLOB data, and subtype information for text and non-text BLOB data.

isc_blob_lookup_desc() stores the requested information about the BLOB from the desc BLOB descriptor structure.

Return Value

isc_blob_lookup_desc() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code.To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_blob_default_desc()

isc_blob_gen_bpb()

isc_blob_set_desc()


isc_blob_lookup_desc( ) Syntax

Example	Definition

ISC_STATUS isc_blob_lookup_desc(
ISC_STATUS *status_vector,
isc_db_handle **db_handle,
isc_tr_handle **trans_handle,
unsigned char *table_name,
unsigned char *column_name,
ISC_BLOB_DESC *desc,
unsigned char *global);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database containing the BLOB column. db_handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
table_name	Pointer to the system table containing information about the BLOB.
column_name	Pointer to the column in the system table containing information about the BLOB.
desc	Pointer to the descriptor to which the function returns information.
global	Column name of the BLOB to look up.


isc_blob_lookup_desc( ) Example

Syntax	Definition

The following fragment retrieves information into a BLOB descriptor:

isc_blob_lookup_desc(status, &db_handle, &tr_handle, &relation_name, &field_name, desc, &global);


isc_blob_set_desc( )

Syntax	Example	See Also

Description

isc_blob_set_desc() sets the BLOB subtype, segment size, and character set for a BLOB column to values specified by the application. To set these values to InterBase defaults, use isc_blob_default_desc().

isc_blob_set_desc() and three related functions, isc_blob_default_desc(), isc_blob_gen_bpb(), and isc_blob_lookup_desc() provide dynamic access to BLOB data. In particular, you can use these functions to define and access information about BLOB data for filtering purposes, such as character set information for text BLOB data, and subtype information for text and non-text BLOB data.

You can manually set the subtype and character set information (for a TEXT subtype) in a BLOB descriptor, by way of a call to isc_blob_set_desc(). Pass the subtype, character set, and segment size to the BLOB descriptor in your application.

isc_blob_set_desc() is useful for setting the contents of the BLOB descriptor without querying the system tables for the information. Calls to this function also let an application specify character set and subtype for custom filtering operations.

Important:	Do not call this function while running against a V3.x database.

Return Value

isc_blob_set_desc() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_blob_default_desc()

isc_blob_gen_bpb()

isc_blob_lookup_desc()


isc_blob_set_desc( ) Syntax

Example	Definition

ISC_STATUS isc_blob_set_desc(
ISC_STATUS *status_vector,
unsigned char *table_name,
unsigned char *column_name,
short subtype,
short charset,
short segment_size,
ISC_BLOB_DESC *desc);

Parameter	Description
status_vector	Pointer to the error status vector.
table_name	Pointer to the table containing the BLOB.
column_name	Pointer to the BLOB column in the table.
subtype	Specifies the subtype of the BLOB. You can either pass the InterBase-defined subtype values, 0 or 1 (TEXT) or you can pass user-defined subtypes, -1 to -32768.
charset	Specifies the character set for the BLOB.
segment_size	Specifies the segment size for the BLOB.
desc	Pointer to a BLOB descriptor to populate.


isc_blob_set_desc( ) Example

Syntax	Definition

The following example sets the default values for a tour guide application, including subtype, character set, and segment size:

isc_blob_set_desc(status, "TOURISM", "GUIDEBOOK", 1, 2, 80, &desc);


isc_cancel_blob( )

Syntax	Example	See Also

Description

InterBase temporarily stores BLOB data in the database during create operations. If, for some reason, you do not, or cannot, close a BLOB, the storage space remains allocated in the database and InterBase does not set the handle to NULL. Call isc_cancel_blob() to release the temporary storage in the database, and to set blob_handle to NULL. If you close the BLOB in the normal course of your application processing logic, this step is unnecessary as InterBase releases system resources on a call to isc_close_blob().

Tip:	A call to this function does not produce an error when the handle is NULL. Therefore, it is good practice to call isc_cancel_blob() before creating or opening a BLOB to clean up existing BLOB operations.

Return Value

isc_cancel_blob() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_close_blob()


isc_cancel_blob( ) Syntax

Example	Definition

ISC_STATUS isc_cancel_blob(
ISC_STATUS *status_vector,
isc_blob_handle *blob_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
blob_handle	Pointer to the BLOB you want to cancel. This function sets the handle to zero and returns a successful result even if the handle is NULL.


isc_cancel_blob( ) Example

Syntax	Definition

The following fragment cancels any open BLOB before creating a new one:

isc_cancel_blob(status_vector, &blob_handle);

if (status_vector[0] == 1 && status_vector[1])

{

/* process error */

isc_print_status(status_vector);

return(1);

}

isc_create_blob(status_vector, &DB, &trans, &blob_handle, &blob_id)


isc_cancel_events( )

Syntax	Example	See Also

Description

isc_cancel_events() cancels an application program’s asynchronous wait for any of a specified list of events. The events are the ones that were associated with event_id as a result of a previous call to isc_que_events().

Return Value

isc_cancel_events() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_que_events()


isc_cancel_events( ) Syntax

Example	Definition

ISC_STATUS isc_cancel_events(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
ISC_LONG *event_id);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database for which the event watch is to be canceled. db_handle must not be NULL, or an error is returned in status_vector.
event_id	Pointer to the event or events to cancel. This must have been set by a previous call to isc_que_events().


isc_cancel_events( ) Example

Syntax	Definition

The following call cancels a program’s wait for events associated with event_id, where event_id was previously returned from a call to isc_que_events():

isc_cancel_events(status_vector, &database_handle, &event_id);

A more complete example is provided in the section on isc_que_events().


isc_close_blob( )

Syntax	Example	See Also

Description

isc_close_blob() is used to store a BLOB in the database and clean up after BLOB operations. Close any BLOB after reading from or writing to it. If, for some reason, your application does not close a BLOB, you can lose data. If your application might open a BLOB without closing it then you should call isc_cancel_blob() to make sure that the application does not try to open a BLOB that is already open.

blob_handle is set by a call to isc_create_blob2() or to isc_open_blob2().

Return Value

isc_close_blob() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_cancel_blob()

isc_create_blob2()

isc_open_blob2()


isc_close_blob( ) Syntax

Example	Definition

ISC_STATUS isc_close_blob(
ISC_STATUS *status_vector,
isc_blob_handle *blob_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
blob_handle	Pointer to the handle of the BLOB to close.


isc_close_blob( ) Example

Syntax	Definition

The following example closes a BLOB and frees system resources:

if (status[1] == isc_segstr_eof)

isc_close_blob(status, &blob_handle)


isc_commit_retaining( )

Syntax	Examples	See Also

Description

isc_commit_retaining() commits an active transaction and immediately clones itself. This means that the function retains the transaction name, system resources associated with the transaction, and the current state of any open cursors in the transaction. Although the function is actually initiating a new transaction, by assigning the new transaction the active transaction handle it is, in effect, keeping the transaction open across commits. This results in improved performance by allowing an application to minimize the overhead of initiating additional transactions. isc_commit_retaining() allows you to commit updates while keeping a cursor open.

You can initiate a rollback within the active transaction but, the rollback only affects uncommitted updates. In other words, a rollback is legal, even after the transaction context has been passed to the cloned transaction, but, in that case, the rollback will only affect the updates your application has made to the database since the last commit.

The transaction ends when you commit without using the retention feature, such as with a call to isc_commit_transaction(), or when you roll back with isc_rollback_transaction().

Return Value

isc_commit_retaining() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_commit_transaction()

isc_rollback_transaction()

isc_start_transaction()


isc_commit_retaining( ) Syntax

Examples	Definition

ISC_STATUS isc_commit_retaining(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to the transaction handle for the transaction to commit. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.


isc_commit_retaining( ) Examples

Syntax	Definition

The following call commits a transaction, prints a message, and starts a new transaction with the same handle within the same request:

if (isc_commit_retaining(status, &retained_trans))

{

fprintf("Committed and retained\\n");

isc_print_status(status);

}

The following call commits a transaction, prints a confirmation message, starts a new transaction with the same handle within the same request, or, if the commit fails, prints an error message and rolls back.

if (!(isc_commit_retaining(status, &retained_trans)))

{

fprintf("An error occurred during commit, rolling back.");

rb_status = isc_rollback_transaction(status, &retained_status);

}

else

{

fprintf("Commit successful.");

tr_count++; /*Increments the number of commits. */

}


isc_commit_transaction( )

Syntax	Example	See Also

Description

isc_commit_transaction() closes record streams, frees system resources, and sets the transaction handle to zero for the specified transaction.

When you call this function to execute a commit operation against multiple databases, InterBase first initiates a call to the isc_prepare_transaction() function. isc_prepare_transaction() executes the first phase of a two-phase commit. This puts the transaction into limbo and signals your intention to commit, so that InterBase can poll all target databases to verify that they are ready to accept the commit. In addition, isc_commit_transaction() writes a BLOB message to the RDB$TRANSACTION_DESCRIPTION column of the RDB$TRANSACTIONS system table, detailing information required by InterBase to perform a reconnect in case of system failure during the commit process.

The isc_commit_transaction() function also performs the second phase of a two-phase commit upon receiving verification that all databases are ready to accept the commit. Also, isc_commit_transaction() cleans up RDB$TRANSACTIONS.

Return Value

isc_commit_transaction() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_commit_retaining()

isc_prepare_transaction()


isc_commit_transaction( ) Syntax

Example	Definition

ISC_STATUS isc_commit_transaction(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to the transaction handle for the transaction to commit. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.


isc_commit_transaction( ) Example

Syntax	Definition

The following call commits a transaction and prints a message:

isc_commit_transaction(status, &trans)

if (status[0]==1 && status[1])

{

fprintf("Error on write\\n");

isc_print_status(status);

}


isc_create_blob2( )

Syntax	Example	See Also

Description

isc_create_blob2() creates a context for storing a BLOB, opens a BLOB for write access, and optionally filters the BLOB from one subtype to another. Subsequent calls to isc_put_segment() write data from an application buffer to the BLOB.

If a BLOB filter is used, it is called for each segment written to the BLOB. The BLOB filter must be specified as a subtype in a previously populated BLOB parameter buffer (BPB), pointed to by bpb_address.

Important:	BLOB filters are not supported on Netware.

If a BLOB filter is not needed or cannot be used, set the filter subtype to zero in the BPB.

The BPB contains BLOB information in the following format:

Type	Description
isc_bpb_version1	Version of the BPB.
isc_bpb_target_type	Subtype of the target BLOB descriptor.
isc_bpb_source_type	Subtype of the source BLOB descriptor.
isc_bpb_target_interp	Character set of the target BLOB descriptor.
isc_bpb_source_interp	Character set of the source BLOB descriptor.
If you are storing bitmaps or other binary files, you do not need to filter them to open them as BLOB data, unless you intend to change from one format to another, say from .GEM to .BMP. You can also store compressed bitmaps directly in a database, in formats such as .JPG (JPEG), .BMP (Windows native bitmaps), or .GIF (CompuServe Graphic Interchange Format). No filtering is required.

You can store bitmaps in a database in row-major or column-major order. You can generate the BPB with the isc_blob_gen_bpb() function, or create it manually.

blob_handle must be zero when isc_create_blob2() is called. To reuse blob_handle, close the BLOB with a call to isc_close_blob() to zero out the handle before calling isc_create_blob2().

blob_id is assigned by InterBase on creation of the BLOB context. This pointer can contain the address of the BLOB data or the address of a page containing pointers to fragments of the BLOB.

Note:	To read data in a newly created BLOB, close the BLOB, then re-open it with isc_open_blob2().

Return Value

isc_create_blob2() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_blob_gen_bpb()

isc_open_blob2()

isc_put_segment()


BLOB Filters

On all platforms except NetWare, you can use BLOB filters to convert from one BLOB subtype to another. You might also convert a BLOB from one subtype to the same subtype. For example, you might want to convert a text file with paragraph breaks at the end of each line to a text file with paragraph breaks at the end of each paragraph. In this case you would convert from subtype 1 to subtype 1, filtering through a program to convert the paragraphs.

Other uses for BLOB data and BLOB filtering might include storing images, sounds, and video in the database. Often you can access these various data types directly, but you might need to convert one image format to another, for instance, to display the same image on monitors with different resolution. Or you might convert a binary BLOB to ASCII text and back again to be able to move the file more easily from one system to another.

InterBase includes two system-defined filters: one is subtype 0 (unstructured data) and the other is subtype 1 (TEXT). If you define your own filters, you can assign them subtype identifiers from -32768 to -1.

You can write BLOB filters in C or Pascal (or any language that can be called from C). To use your own filters, follow these steps:

1.	Decide which filters you need to write.

2.	Write the filters in a host language.

3.	Define the filters to the database.

4.	Build a shared filter library.

5.	Make the filter library available.

6.	Write an application that requests filtering.

User-defined BLOB filters take control away from InterBase to perform specific functions and return actions to InterBase. To help InterBase understand the interaction with the filters and to help it make use of the filter return values, you must create a line of communication between the filter and the InterBase engine. Define this line of communication by including two things in your filters:

1.	A control structure that serves as a messenger between InterBase and the BLOB filter.

2.	Program constants called actions that regulate the action between the InterBase and the BLOB filter.

BLOB Control Structure

The BLOB control structure serves as a messenger between the BLOB filters you write and the InterBase engine.

Parameter	Type	Description
(*ctl_source)()	ISC_STATUS *	Pointer to the entry point of the next filter.
ctl_source_handle	isc_blob_ctl *	Argument to pass to the source filter.
ctl_to_sub_type	short	Target subtype.
ctl_from_sub_type	short	Source subtype.
ctl_buffer_length	unsigned short	Length of the buffer.
ctl_segment_length	unsigned short	Length of the current segment.
ctl_bpb_length	unsigned short	Length of the BLOB parameter.
ctl_bpb	char *	Pointer to the BPB.
ctl_buffer	unsigned char *	Pointer to the segment buffer.
ctl_max_segment_length	ISC_LONG	Length in bytes of the longest segment. You must update this statistic in your application.
ctl_number_segments	ISC_LONG	Total number of segments. You must update this statistic in your application.
ctl_total_length	ISC_LONG	Total length in bytes of the BLOB. You must update this statistic in your application.
ctl_status	ISC_STATUS *	Pointer to the status vector.
ctl_data [8]	long	Application-specific data.
The BLOB control structure is defined as a typedef, CTL, in ibase.h. It is defined as follows:

typedef struct ctl {

int(*ctl_source)(); /* Source filter. */

struct ctl *ctl_source_handle; /* Argument to pass to source filter. */

short ctl_to_sub_type; /* Target subtype. */

short ctl_from_sub_type;	 /* Source subtype. */

short ctl_buffer_length;	 /* Length of buffer. */

short ctl_segment_length;	/* Length of current segment. */

short ctl_bpb_length; /* Length of BLOB n

Actions

Include the following action constants in your filter as a case statement to intercept and respond to events that occur as a result of related function calls. InterBase invokes the filter by way of these actions. The actions are the result of events that occur when your application initiates a BLOB access operation.

Action	BLOB Access Operation
isc_blob_filter_open	Invoked when the application opens a BLOB READ cursor.
isc_blob_filter_get_segment	Invoked when the application executes a BLOB FETCH statement. This action invokes the filter without InterBase passing an action. Instead, the filter calls the *ctl_source function to get a segment.
isc_blob_filter_close	Invoked when the application closes a BLOB.
isc_blob_filter_create	Invoked when an application opens a BLOB INSERT cursor.
isc_blob_filter_put_segment	Invoked when an application executes a BLOB INSERT statement. This action invokes the filter without InterBase passing an action. Instead, the filter calls the *ctl_source function to put a segment.
isc_blob_filter_alloc	Invoked when InterBase initializes filter processing. Not a result of a particular application action.
isc_blob_filter_free	Invoked when InterBase ends filter processing. Not a result of a particular application action.
In an application’s action case statement, include the operations to be performed, as in the following example:

#include <ibase.h>

switch (action)

{

case isc_blob_filter_open:

if (!isc_get_segment(&status, &from_blob, seg_len, 80, &from_buffer)) write_status = isc_put_segment(&status, &to_blob, 80, &to_buffer);

else {

isc_print_status(status);

}

break;

case isc_blob_filter_close:

. . .

break;

}

return status;

}

Filter Function

You are required to declare a filter function that defines the filter’s entry point to the database. Declare the filter function in the database and also in the filter. In the filter, pass the action case statement as a parameter to the filter function.

To declare the filter to the database use the DECLARE FILTER statement, as follows:

DECLARE FILTER JPEG_FILTER

INPUT TYPE -233

OUTPUT TYPE -2330

ENTRY_POINT "jpeg_flt"

MODULE_NAME "IMG_LIB";

In the filter, declare the filter function as follows:

#include <ibase.h>

#include <stdio.h>

#define isc_blob_filter_open 0

#define isc_blob_filter_get_segment 1

#define isc_blob_filter_close 2

#define isc_blob_filter_create 3

#define isc_blob_filter_put_segment 4

#define isc_blob_filter_alloc 5

#define isc_blob_filter_free 6

#define SUCCESS 0

#define FAILURE 1

isc_status FilterFunction(short action, isc_blob_ctl control)

{

isc_status status = SUCCESS;

switch (action)

{

case isc_blob_filter_open:

if (!isc_get_segment(&status, &from_blob, seg_len, 80, &from_buffer))
write_status = isc_put_segment(&status, &to_blob, 80, &to_buffer);
else {
isc_print_status(status);
}

break;

case_isc_blob_filter_get_segment:

. . .

break;

case isc_blob_filter_close:

. . .

break;

case isc_blob_filter_create:

. . .

break;

case isc_blob_filter_put_segment:

. . .

break;

case isc_blob_filter_alloc:

. . .

break;

case isc_blob_filter_free:

. . .

break;

}

return status;

}


isc_create_blob2( ) Syntax

Example	Definition

ISC_STATUS isc_create_blob2(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_tr_handle *trans_handle,
isc_blob_handle *blob_handle,
ISC_QUAD *blob_id,
short bpb_length,
char *bpb_address);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). Pointer to the handle of the database where you want to create the BLOB. db_handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to the handle of the transaction in which you want the BLOB to be created.
blob_handle	Pointer to the BLOB.
blob_id	Pointer to the 64-bit system-defined BLOB ID. The BLOB ID is stored in a field in the table and points to the first segment of the BLOB or to a page of pointers to BLOB fragments.
bpb_length	Length of the BLOB parameter buffer (BPB).
bpb_address	Pointer to the BPB.


isc_create_blob2( ) Example

Syntax	Definition

The following fragment declares a BPB, populates it with filter information, then creates a BLOB and passes the BPB:

isc_blob_handle blob_handle; /* declare at beginning */

ISC_QUAD blob_id; /* declare at beginning */

char bpb[] = {

isc_bpb_version1,

isc_bpb_target_type,

1,	/* # bytes that follow which specify target subtype */

1,	/* target subtype (TEXT) */

isc_bpb_source_type,

1,	/* # bytes that follow which specify source subtype */

-4,	/* source subtype*/

};

. . .

isc_create_blob2(

status_vector,

&db_handle,

&tr_handle,

&blob_handle, 	/* to be filled in by this function */

&blob_id, 	/* to be filled in by this function */

actual_bpb_length, 	/* length of BPB data */

&bpb 	/* BLOB parameter buffer */

)


isc_database_info( )

Syntax	Example	See Also

Description

isc_database_info() returns information about an attached database. Typically, isc_database_info() is called to:

Determine how much space is used for page caches. The space is the product of the number of buffers and the page size, which are determined by calling isc_database_info() with the isc_info_num_buffers and isc_info_page_size item-list options.

Monitor performance. For example, to compare the efficiency of two update strategies, such as updating a sorted or unsorted stream.

The calling program passes its request for information through the item-list buffer supplied by the program, and InterBase returns the information to a program-supplied result buffer.

Item-list Buffer Items and Result Buffer Values

The item-list buffer is a char array into which are placed a sequence of byte values, one per requested item of information. Each byte is an item type, specifying the kind of information desired.

The result buffer returns a series of clusters of information, one per item requested. Each cluster consists of three parts:

1.	A one-byte item return type. There are compile-time constants defined for all the item return types in ibase.h.

2.	A two-byte number specifying the number of bytes that follow in the remainder of the cluster.

3.	A value, stored in a variable number of bytes, whose interpretation (e.g., as a number or as a string of characters) depends on the item return type.

A calling program is responsible for interpreting the contents of the result buffer and for deciphering each cluster as appropriate. In many cases, the value simply contains a number or a string (sequence of characters). But in other cases, the value is a number of bytes whose interpretation depends on the item return type.

The clusters returned to the result buffer are not aligned. Furthermore, all numbers are represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers have the sign in the last byte. Convert the numbers to a data type native to your system before interpreting them.

The following types of information may be requested:

Database characteristics

Environmental characteristics

Performance statistics

Database operation counts

Write-ahead log information

Return Value

isc_database_info() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_attach_database()

isc_detach_database()


Database Characteristics

Several items are provided for determining database characteristics, such as its size and major and minor ODS version numbers. The following table shows what is returned in the value part of the result buffer for each information item request:

Item	Result Buffer Contents
isc_info_allocation	Number of database pages allocated (size of the database).
isc_info_base_level	Database version (level) number: 1 byte containing the number 1 1 byte containing the version number
isc_info_db_id	Database file name and site name: 1 byte containing the number 2 1 byte containing the length, m, of the database file name m bytes containing the database file name 1 byte containing the length, n, of the site name n bytes containing the site name
isc_info_implementation	Database implementation number: 1 byte containing a 1 1 byte containing the implementation number 1 byte containing a "class" number, either 1 or 12
isc_info_no_reserve	0 or 1.0, the default, indicates space is reserved on each database page for holding backup versions of modified records.1 indicates no space is reserved for such records.
isc_info_ods_minor_version	On-disk structure (ODS) minor version number. An increase in a minor version number indicates a non-structural change, one that still allows the database to be accessed by database engines with the same major version number but possibly different minor version numbers.
isc_info_ods_version	ODS major version number. Databases with different major version numbers have different physical layouts. A database engine can only access databases with a particular ODS major version number. Trying to attach to a database with a different ODS number results in an error.
isc_info_page_size	Number of bytes per page of attached database.
isc_info_version	Version identification string of the database implementation. 1 byte containing the number 1 1 byte specifying the length, n, of the following string n bytes containing the version identification string


Environmental Characteristics

Several items are provided for determining environmental characteristics, such as the amount of memory currently in use, or the number of database cache buffers currently allocated. These items are described in the following table:

Item	Result Buffer Contents
isc_info_current_memory	Amount of dynamically allocated server memory (in bytes) currently in use.
isc_info_forced_writes	Number specifying the mode in which writes are performed to a database (0 for asynchronous, 1 for synchronous).
isc_info_max_memory	Maximum amount of memory (in bytes) used at one time by the server since the database was initially attached.
isc_info_num_buffers	Number of memory buffers currently allocated.
isc_info_sweep_interval	Number of transactions that are committed or rolled back before a “sweep” is forced to clean up old record versions.
isc_info_user_names	Names of all the users currently attached to the database. For each such user, the result buffer will contain an isc_info_user_names byte followed by a 1-byte length specifying the number of bytes in the user name, followed by the user name.
Note:	Not all environmental information items are available on all platforms.


Performance Statistics

There are four items providing performance statistics for a database. These statistics accumulate for a database from the moment it is first attached by any process until the last remaining process detaches from the database. A program requesting this information, therefore, sees information pertaining to its own attachment and all other attachments.

For example, the value returned for isc_info_reads is the number of reads since the current database was first attached, i.e., an aggregate of all reads done by all attached processes, rather than the number of reads done for the calling program since it attached to the database.

The items providing performance statistics are summarized in the following table:

Item	Result Buffer Contents
isc_info_fetches	Number of reads from the memory buffer cache.
isc_info_marks	Number of writes to the memory buffer cache.
isc_info_reads	Number of page reads.
isc_info_writes	Number of page writes.


Database Operation Counts

Several information items are provided for determining the number of various database operations performed by the currently attached calling program. These values are calculated on a per-table basis.

When any of these information items is requested, InterBase returns to the result buffer:

1.	1 byte specifying the item type (e.g., isc_info_insert_count)

2.	2 bytes telling how many bytes compose the subsequent value pairs

3.	A pair of values for each table in the database on which the requested type of operation has occurred since the database was last attached

Each pair consists of:

1.	2 bytes specifying the table ID.

2.	4 bytes listing the number of operations (e.g., inserts) done on that table.

The following table describes the items which return count values for operations on the database:

Item	Result Buffer Contents
isc_info_backout_count	Number of removals of a version of a record.
isc_info_delete_count	Number of database deletes.
isc_info_expunge_count	Number of removals of a record and all of its ancestors for records whose deletions have been committed.
isc_info_insert_count	Number of inserts into the database since the database was last attached.
isc_info_purge_count	Number of removals of old versions of fully mature records (records committed, resulting in older—ancestor—versions no longer being needed).
isc_info_read_idx_count	Number of reads done via an index since the database was last attached.
isc_info_read_seq_count	Number of sequential database reads, i.e., the number of sequential table scans (row reads) done on each table since the database was last attached.
isc_info_update_count	Number of database updates since the database was last attached.


Write-ahead Log (WAL) Information

On NetWare servers, InterBase supports a write-ahead log (WAL) protocol that records all database changes before they are actually stored in the database itself. The WAL consists of:

WAL buffers, areas of memory set aside to store database changes.

WAL files, disk files used to record changes flushed from the write-ahead buffers.

A WAL writer, the shared process that administers the WAL buffers, log file rollover, and checkpoints for a database.

A physical log file can be partitioned into equal-sized logical log files.

WAL-related information that can be requested is summarized in the following table:

Item	Result Buffer Contents
isc_info_logfile	Number specifying whether or not a WAL protocol is active. A 1 returned means “yes,” 0 means “no.”
isc_info_cur_logfile_name	Name of the current physical WAL file.
isc_info_cur_log_part_offset	Partition offset, in bytes, of the beginning of the current logical log file in the current physical WAL file. If the current log file is not partitioned, then the isc_info_cur_log_part_offset result value will be 0.
isc_info_num_wal_buffers	Number of memory write-ahead log buffers used to store database changes.
isc_info_wal_buffer_size	Number of bytes in each WAL buffer.
isc_info_wal_ckpt_length	Number of bytes of data that determine a “checkpoint” interval; whenever that number of bytes have been recorded in the WAL file(s), the database on disk is brought up to date with respect to the previous checkpoint.
isc_info_wal_cur_ckpt_nterval	Number specifying how many bytes have been written so far in the log file(s) towards the next checkpoint.
isc_info_wal_prv_ckpt_fname	Name of the physical log file in which the previous checkpoint occurred.
isc_info_wal_prv_ckpt_poffset	Partition offset (in bytes) of the beginning of the logical log file in which the previous checkpoint occurred. This offset is relative to the beginning of the physical log file specified in isc_info_wal_prv_ckpt_fname. It will be 0 if that physical log file is not partitioned.
isc_info_wal_grpc_wait_usecs	Number of microseconds that specify a “group commit” interval; after the specified number of microseconds have passed, all changes stored in WAL buffers for transactions committed since the beginning of the interval are flushed to the WAL file.
isc_info_wal_num_io	Total number of log file writes performed for the database since the first attachment in this session.
isc_info_wal_avg_io_size	Average number of bytes written to the log file(s) since the first attachment in this session.
isc_info_wal_num_commits	Number of transactions committed since the first attachment in this session.
isc_info_wal_avg_grpc_size	Average number of transactions committed per “group commit” interval.


isc_database_info( ) Syntax

Example	Definition

ISC_STATUS isc_database_info(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
short item_list_buffer_length,
char *item_list_buffer,
short result_buffer_length,
char *result_buffer);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). db_handle must not be NULL, or an error is returned in status_vector.
item_list_buffer_length	Number of bytes in the item-list buffer.
item_list_buffer	Address of the item-list buffer.
result_buffer_length	Number of bytes in the result buffer.
result_buffer	Address of the result buffer.


isc_database_info( ) Example

Syntax	Definition

The following code requests the page size and the number of buffers for the currently attached database, then examines the result buffer:

char db_items[] = {

isc_info_page_size, isc_info_num_buffers,

isc_info_end};

char res_buffer[40], *p, item;

int length;

SLONG page_size = 0L, num_buffers = 0L;

ISC_STATUS status_vector[20];

isc_database_info(

status_vector,

&handle,	/* Set in previous isc_attach_database() call. */

sizeof(db_items),

db_items,

sizeof(res_buffer),

res_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

/* An error occurred. */

isc_print_status(status_vector);

return(1);

};

/* Extract the values returned in the result buffer. */

for (p = res_buffer; *p != isc_info_end ;)

{

item = *p++

length = isc_vax_integer(p, 2);

p += 2;

switch (item)

{

case isc_info_page_size:

page_size = isc_vax_integer(p, length);

break;

case isc_info_num_buffers:

num_buffers = isc_vax_integer(p, length);

break;

default:

break;

}

p += length;

};


isc_decode_date( )

Syntax	Example	See Also

Description

isc_decode_date() translates a date retrieved from a table and stored in an ISC_QUAD variable, ib_date, into a C time structure for program manipulation. Both ib_date and tm_date must be declared and initialized before use.

Use the isc_dsql family of API calls to retrieve InterBase DATE data from a table into the ISC_QUAD structure prior to translation.

Return Value

None.


See Also

isc_encode_date()


isc_decode_date( ) Syntax

Example	Definition

void isc_decode_date(
ISC_QUAD *ib_date,
void *tm_date);

Parameter	Description
ib_date	Pointer to an eight-byte ISC_QUAD structure containing a date in InterBase format.
tm_date	Pointer to a C tm structure.


isc_decode_date( ) Example

Syntax	Definition

The following code fragment illustrates declaring time structures and calling isc_decode_date() to translate an InterBase date format into a C time format:

#include <time.h>

#include <ibase.h>

. . .

struct tm hire_time;

ISC_QUAD hire_date;

. . .

/* Retrieve DATE data from a table here. */

. . .

isc_decode_date(&hire_date, &hire_time);


isc_detach_database( )

Syntax	Example	See Also

Description

isc_detach_database() detaches an attached database. Call this function to release system resources when you are done using a database or before re-attaching the database with different attach parameters. isc_detach_database() also releases the buffers and structures that control the remote interface on the client and the remote server where the database is stored.

Before calling isc_detach_database() commit or roll back transactions affecting the database from which you want to detach.

Return Value

isc_detach_database() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_attach_database()


isc_detach_database( ) Syntax

Example	Definition

ISC_STATUS isc_detach_database(
ISC_STATUS *status_vector,
isc_db_handle *db_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). db_handle must not be NULL, or an error is returned in status_vector.


isc_detach_database( ) Example

Syntax	Definition

The following conditional statement detaches a database:

if (handle)

isc_detach_database(status_vector, &handle);

Assuming that handle is valid and identifies an attached database, the specified database is detached when this statement executes.


isc_drop_database( )

Syntax	Example	See Also

Description

isc_drop_database() deletes an attached database and all of its supporting files. Call this routine when you no longer have a use for the database, for example, if you moved all the data into another database, or if the database was just temporary and is no longer needed. To succeed, isc_drop_database() must be issued when no other processes are attached to the database.

Return Value

isc_drop_database() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_attach_database()


isc_drop_database( ) Syntax

Example	Definition

ISC_STATUS isc_drop_database(
ISC_STATUS *status_vector,
isc_db_handle *db_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). db_handle must not be NULL, or an error is returned in status_vector.


isc_drop_database( ) Example

Syntax	Definition

The following conditional statement drops a database:

if (handle)

isc_drop_database(status_vector, &handle);

Assuming that handle is valid and identifies an attached database, the specified database is dropped when this statement executes.


isc_dsql_allocate_statement( )

Syntax	Example	See Also

Description

isc_dsql_allocate_statement() allocates a statement handle and returns a pointer to it in stmt_handle. This pointer is passed to isc_dsql_prepare() to associate the statement handle with a particular DSQL statement for processing.

If a DSQL statement is to be executed multiple times, or if it returns output (other than the results from a stored procedure), isc_dsql_allocate_statement() or isc_dsql_alloc_statement2() should be called to allocate a statement handle prior to preparing and executing the statement with isc_dsql_prepare() and isc_dsql_execute().

Note:	isc_dsql_allocate_statement() is very similar to isc_dsql_alloc_statement2() except that statement handles allocated using isc_dsql_allocate_statement() are not automatically reset to NULL when the database under which they are allocated is detached. To reset statement handles automatically, use isc_dsql_alloc_statement2().

When you are done processing a statement, the statement handle can be freed with isc_dsql_free_statement() or by calling isc_detach_database().

Return Value

isc_dsql_allocate_statement() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_db_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_alloc_statement2()

isc_dsql_execute()

isc_dsql_free_statement()

isc_dsql_prepare()


isc_dsql_allocate_statement( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_allocate_statement(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_stmt_handle *stmt_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database against which a DSQL statement is to be executed. The handle must not be NULL, or an error is returned in status_vector.
stmt_handle	Pointer to the statement handle to be allocated by this function. The handle must be NULL when this function is called, or an error is returned in status_vector.


isc_dsql_allocate_statement( ) Example

Syntax	Definition

The following program fragment allocates a statement handle for an SQL statement that will access the database referenced by the database handle, database_handle:

ISC_STATUS status_vector[20];

isc_stmt_handle statement_handle;

statement_handle = NULL; /* Set handle to NULL before allocating it. */

isc_dsql_allocate_statement(

status_vector,

&database_handle, /* Set in previous isc_attach_database() call. */

&statement_handle);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);	 /* Return now. */

}

/* Call other functions to associate a particular SQL statement with the statement handle, and to do other operations necessary to prepare and execute the DSQL statement. Free the statement handle when it is no longer needed. */

;


isc_dsql_alloc_statement2( )

Syntax	Example	See Also

Description

isc_dsql_alloc_statement2() allocates a statement handle and returns a pointer to it in stmt_handle. This pointer is passed to isc_dsql_prepare() to associate the statement handle with a particular DSQL statement for processing.

If a DSQL statement is to be executed multiple times, or if it returns output (other than the results from a stored procedure), isc_dsql_alloc_statement2() or isc_dsql_allocate_statement() should be called to allocate a statement handle prior to preparing and executing the statement with isc_dsql_prepare() and isc_dsql_execute().

Return Value

isc_dsql_alloc_statement2() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_db_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_alloc_statement()

isc_dsql_execute()

isc_dsql_free_statement()

isc_dsql_prepare()


isc_dsql_alloc_statement2( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_alloc_statement2(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_stmt_handle *stmt_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database against which a DSQL statement is to be executed. The handle must not be NULL, or an error is returned in status_vector.
stmt_handle	Pointer to the statement handle to be allocated by this function. The handle must be NULL when this function is called, or an error is returned in status_vector.


isc_dsql_alloc_statement2( ) Example

Syntax	Definition

The following program fragment allocates a statement handle for an SQL statement that will access the database referenced by the database handle, database_handle:

ISC_STATUS status_vector[20];

isc_stmt_handle statement_handle;

isc_dsql_alloc_statement2(

status_vector,

&database_handle, /* Set in previous isc_attach_database() call. */

&statement_handle);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display an error message. */

return(1); /* Return now. */

}

/*	Call other functions to associate a particular SQL statement with the statement handle, and to do other operations necessary to prepare and execute the DSQL statement. */

;


isc_dsql_describe( )

Syntax	Example	See Also

Description

isc_dsql_describe() stores into xsqlda a description of the columns that make up the rows returned for a SELECT statement, or a description of the result values returned by an EXECUTE PROCEDURE statement. These statements must have been previously prepared for execution with isc_dsql_prepare(), before isc_dsql_describe() can be called.

Important:	Using isc_dsql_describe() is not necessary unless a previously issued isc_dsql_prepare() function indicates that there is insufficient room in the output XSQLDA for the return values of the DSQL statement to be executed.

Preparing a DSQL Statement with Return Values

A DSQL statement is prepared by calling isc_dsql_prepare(). Before calling isc_dsql_prepare(), allocate the XSQLDA as follows:

#include <ibase.h>

xsqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));

where XSQLDA_LENGTH is a pre-defined macro in ibase.h that calculates the number of bytes for storage required when n result columns will be returned by the statement.

After allocating the XSQLDA xsqlda, set xsqlda->version to SQLDA_VERSION1, and set xsqlda_sqln to indicate the number of XSQLVAR structures allocated (n in the above example).

Then call isc_dsql_prepare() to fill in the other fields of the XSQLDA and all the XSQLVARs with information such as the data type, length, and name of the corresponding select-list items in the prepared statement. It fills in xsqlda->sqld with the actual number of select-list items. If xsqlda->sqld is greater than xsqlda->sqln, then not enough room is allocated for return values. In this case:

1.	Record the current value in xsqlda->sqld.

2.	Free the storage previously allocated for xsqlda.

3.	Reallocate storage for xsqlda, this time specifying the correct number (from step 1) in the argument to XSQLDA_LENGTH.

4.	Reset xsqlda->sqld and xsqlda->version.

5.	Execute isc_dsql_describe() to have other xsqlda fields filled in appropriately.

Return Value

isc_dsql_describe() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_describe_bind()

isc_dsql_execute()

isc_dsql_execute2()

isc_dsql_prepare()


isc_dsql_describe( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_describe(
ISC_STATUS *status_vector,
isc_stmt_handle *stmt_handle,
unsigned short dialect,
XSQLDA *xsqlda);

Parameter	Description
status_vector	Pointer to the error status vector.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). The handle must not be NULL, or an error is returned in status_vector.
dialect	Indicates the version of the SQL descriptor area passed to the function. Set this value to 1.
xsqlda	Pointer to a previously allocated XSQLDA used for output.


isc_dsql_describe( ) Example

Syntax	Definition

The following program fragment illustrates a sequence of calls which allocates an XSQLDA, prepares a statement, checks whether or not the appropriate number of XSQLVARs was allocated, and corrects the situation if needed.

#include <ibase.h>

ISC_STATUS status_vector[20];

XSQLDA *osqlda;

int n;

char *query =

"SELECT * FROM CITIES WHERE STATE = "NY" ORDER BY CITY DESCENDING";

osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(3);

osqlda->version = SQLDA_VERSION1;

osqlda->sqln = 3;

isc_dsql_prepare(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

 /* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

0,

query,

1,

osqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

if (osqlda->sqld > osqlda->sqln) /* Need more XSQLVARS. */

{

n = osqlda->sqld;

free(osqlda);

osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n);

osqlda->sqln = n;

osqlda->version = SQLDA_VERSION1;

isc_dsql_describe(

status_vector,

&stmt_handle,

1,

osqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

}


isc_dsql_describe_bind( )

Syntax	Example	See Also

Description

isc_dsql_describe_bind() stores into the input XSQLDA xsqlda information about the dynamic input parameters required by a DSQL statement previously prepared with isc_dsql_prepare().

Before an application can execute a statement with input parameters, it must supply values for them in an input XSQLDA structure. If you know exactly how many parameters are required, and their data types, you can set up the XSQLDA directly without calling isc_dsql_describe_bind(). But if you need InterBase to analyze the statement and provide information such as the number of parameters and their data types, you must call isc_dsql_describe_bind() to supply the information.

Preparing a DSQL Statement with Input Parameters

A DSQL statement is prepared by calling isc_dsql_prepare(). After preparing the statement, set up an input XSQLDA as follows:

#include <ibase.h>

in_xsqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));

where XSQLDA_LENGTH is a pre-defined macro in ibase.h that calculates the number of bytes required for n input parameters.

After allocating the XSQLDA in_xsqlda, set in_xsqlda->version to SQLDA_VERSION1, and set in_xsqlda_sqln to indicate the number of XSQLVAR structures allocated.

Then call isc_dsql_describe_bind(), to set the type, length, scale, and subtype fields of the XSQLVAR for each input parameter. The function estimates the data type and length that should be passed for each parameter, based on where it applies in the prepared statement. isc_dsql_describe_bind() also fills in in_xsqlda->sqld with the actual number of parameters. If in_xsqlda->sqld is greater than in_xsqlda->sqln, then not enough memory is allocated to hold all the input parameter information required. In this case:

1.	Record the current value of in_xsqlda->sqld.

2.	Free the storage previously allocated for in_xsqlda.

3.	Reallocate storage for in_xsqlda, this time specifying the correct number (from step 1) in the argument to XSQLDA_LENGTH.

4.	Reset in_xsqlda->sqld and in_xsqlda->version.

5.	Execute isc_dsql_describe_bind() again to have in_xsqlda fields filled in appropriately.

Once a DSQL statement has been prepared with isc_dsql_prepare(), an output XSQLDA (if required) has been properly allocated with isc_dsql_prepare() and possibly isc_dsql_describe(), and an input XSQLDA (if required) has been properly allocated and filled in with values, the statement is can be executed with either isc_dsql_execute() or isc_dsql_execute2().

Return Value

isc_dsql_describe_bind() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_describe()

isc_dsql_execute()

isc_dsql_execute2()

isc_dsql_prepare()


isc_dsql_describe_bind( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_describe_bind(
ISC_STATUS *status_vector,
isc_stmt_handle *stmt_handle,
unsigned short dialect,
XSQLDA *xsqlda);

Parameter	Description
status_vector	Pointer to the error status vector.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). The handle must not be NULL, or an error is returned in status_vector.
dialect	Indicates the version of the SQL descriptor area passed to the function. Set this value to 1.
xsqlda	Pointer to a previously allocated XSQLDA used for input.


isc_dsql_describe_bind( ) Example

Syntax	Definition

The following program fragment illustrates a sequence of calls that allocates an input XSQLDA, prepares a DSQL UPDATE statement, calls the function isc_dsql_describe_bind(), checks whether or not the appropriate number of XSQLVARs was allocated, and corrects the situation if necessary.

#include <ibase.h>

ISC_STATUS status_vector[20];

XSQLDA *isqlda

int n;

char *str = "UPDATE DEPARTMENT SET BUDGET = ?, LOCATION = ?";

isc_dsql_prepare(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

0,

str,

1,

NULL);

if (status_vector[0] == 1 && status_vector[1])

{

 /* Process error. */

isc_print_status(status_vector);

return(1);

}

/* Allocate an input XSQLDA. */

isqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(1);

isqlda->version = SQLDA_VERSION1;

isqlda->sqln = 1;

isc_dsql_describe_bind(

status_vector,

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

1,

isqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

if (isqlda->sqld > isqlda->sqln) /* Need more XSQLVARs. */

{

n = isqlda->sqld;

free(isqlda);

isqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n);

isqlda->sqln = n;

isqlda->version = SQLDA_VERSION1;

isc_dsql_describe_bind(

status_vector,

&stmt_handle,

1,

isqlda);

if (status_vector[0] == 1 && status_vector[1])

{

 /* Process error. */

isc_print_status(status_vector);

return(1);

}

}


isc_dsql_execute( )

Syntax	Example	See Also

Description

isc_dsql_execute() executes a DSQL statement previously prepared with isc_dsql_prepare(). isc_dsql_execute() can be used to execute two types of statements:

Statements that may return more than one row of data.

Statements that need to be executed more than once.

If a statement to execute has input parameters, then isc_dsql_execute() requires an input XSQLDA to describe those parameters. It does not provide for an output XSQLDA. A call to isc_dsql_execute() that executes a SELECT statement results in the creation of a list containing all the rows of data that are the result of execution of the statement. To access these rows, call isc_dsql_fetch() in a loop. Each call to isc_dsql_fetch() fetches the next row from the select list.

If the statement to be executed requires input parameter values (i.e., if it contains parameter markers), these values must be supplied in the input XSQLDA xsqlda before calling isc_dsql_execute().

Tip:	To execute a statement repeatedly when it both has input parameters and return values, such as EXECUTE PROCEDURE, use isc_dsql_execute2() which requires both an input and an output XSQLDA.

If you only need to execute a statement once, and it does not return any data, call isc_dsql_execute_immediate() instead of isc_dsql_prepare() and isc_dsql_execute(). To execute a statement with both input and output parameters a single time, use isc_dsql_exec_immed2().

Important:	CREATE DATABASE and SET TRANSACTION cannot be executed with isc_dsql_execute() or isc_dsql_execute2(). To execute these statements, use isc_dsql_execute_immediate().

Return Value

isc_dsql_execute() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_trans_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_describe_bind()

isc_dsql_exec_immed2()

isc_dsql_execute_immediate()

isc_dsql_execute2()

isc_dsql_fetch()

isc_dsql_prepare()


isc_dsql_execute( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_execute(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle,
isc_stmt_handle *stmt_handle,
unsigned short dialect,
XSQLDA *xsqlda);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). The handle must not be NULL, or an error is returned in status_vector.
dialect	Indicates the version of the extended SQL descriptor area (XSQLDA) passed to the function. Set this value to 1.
xsqlda	Pointer to a previously allocated XSQLDA used for input.


isc_dsql_execute( ) Example

Syntax	Definition

The following program fragment illustrates calls to isc_dsql_execute() and isc_dsql_fetch(). It allocates input and output XSQLDAs, prepares a SELECT statement, executes it, and fetches and processes each row one-by-one.

#include <ibase.h>

ISC_STATUS status_vector[20], fetch_stat;

XSQLDA *isqlda, *osqlda;

XSQLVAR *ivar, *ovar;

char *str = "SELECT CITY, POPULATION FROM CITIES WHERE STATE = ?";

char *state = "CA";

/* Allocate an output XSQLDA osqlda. */

osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2);

osqlda->version = SQLDA_VERSION1;

osqlda->sqln = 2;

/* Prepare the statement, including filling in osqlda with information about the select-list items to be returned by the statement. */

isc_dsql_prepare(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

0,

str,

1,

osqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

/* Check to see whether or not the output XSQLDA had enough XSQLVARS allocated. If not, correct it -- see isc_dsql_describe(). */

/* Allocate and fill in the input XSQLDA. This example assumes you know how many input parameters there are (1), and all other information necessary to supply a value. If this is not true, you will need to call isc_dsql_describe_bind(). */

isqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(1));

isqlda->version = SQLDA_VERSION1;

isqlda->sqln = 1;

isqlda->sqld = 1;

ivar = isqlda->sqlvar[0];

ivar->sqltype = SQL_TEXT;

ivar->sqllen = sizeof(state);

ivar->sqldata = state;

/* Execute the statement. */

isc_dsql_execute(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

1,

isqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

/* Set up an output XSQLVAR structure to allocate space for each item to be returned. */

for (i=0, ovar = osqlda->sqlvar; i < osqlda->sqld; i++, ovar++)

{

dtype = (ovar->sqltype & ~1) /* Drop NULL bit for now. */

switch(dtype)

{

case SQL_TEXT:

ovar->sqldata = (char *)malloc(sizeof(char) * ovar->sqllen);

break;

case SQL_LONG:

ovar->sqldata = (char *)malloc(sizeof(long));

/* Process remaining types. */

. . .

}

if (ovar->sqltype & 1)

{

/* Assign a variable to hold NULL status. */

ovar->sqlind = (short *)malloc(sizeof(short));

}

} /* end of for loop */

/* Fetch and process the rows in the select list one by one. */

while ((fetch_stat = isc_dsql_fetch(

status_vector,

&stmt_handle,

1,

osqlda)) == 0)

{

for (i=0; i < osqlda->sqld; i++)

{

/* Call a function you've written to process each returned select-list item. */

process_column(osqlda->sqlvar[i]);

}

}


isc_dsql_execute2( )

Syntax	Example	See Also

Description

isc_dsql_execute2() executes a previously prepared DSQL statement that has input parameters and returns results, such as EXECUTE PROCEDURE and SELECT.

If the statement to execute requires input parameter values (i.e., if it contains parameter markers), these values must be supplied in the input XSQLDA, in_xsqlda before calling isc_dsql_execute2().

If the statement to execute returns values, they are placed in the specified output XSQLDA, out_xsqlda. If a NULL value is supplied for the output XSQLDA and the statement returns values, they are stored in an result set. To access the returned data, use isc_dsql_fetch() in a loop.

Tip:	If you just want to execute once a statement returning just one group of data, call isc_dsql_exec_immed2() instead of isc_dsql_prepare() and isc_dsql_execute2().

To execute a statement that does not return any data a single time, call isc_dsql_execute_immediate() instead of isc_dsql_prepare() and isc_dsql_execute2().

Return Value

isc_dsql_execute2() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_trans_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_exec_immed2()

isc_dsql_execute_immediate()

isc_dsql_execute()

isc_dsql_fetch()

isc_dsql_prepare()


isc_dsql_execute2( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_execute2(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle,
isc_stmt_handle *stmt_handle,
unsigned short dialect,
XSQLDA *in_xsqlda,
XSQLDA *out_xsqlda);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). The handle must not be NULL, or an error is returned in status_vector.
dialect	Indicates the version of the extended SQL descriptor area (XSQLDA) passed to the function. Set this value to 1.
in_xsqlda	Pointer to an optional, previously allocated XSQLDA used for input. If input parameters are not supplied, set this value to NULL.
out_xsqlda	Pointer to an optional, previously allocated XSQLDA used for results of statement execution. If not required, set this value to NULL.


isc_dsql_execute2( ) Example

Syntax	Definition

The following program fragment illustrates a sequence of calls that allocates an input XSQLDA and loads values into it, allocates an output XSQLDA, prepares an EXECUTE PROCEDURE statement, allocates space in the output XSQLDA for each column returned for each row retrieved by the call, and executes the prepared statement, placing return values in the output XSQLDA.

#include <ibase.h>

ISC_STATUS status_vector[20];

XSQLDA *isqlda, *osqlda;

XSQLVAR *ivar, *ovar;

short null_flag;

char *str = "EXECUTE PROCEDURE P1";

char *state = "CA";

/* Allocate an output XSQLDA osqlda. This example assumes you know that P1 will return one value. */

osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(1);

osqlda->version = SQLDA_VERSION1;

osqlda->sqln = 1;

/* Prepare the statement, including filling in osqlda with information about the item to be returned by the statement (procedure). */

isc_dsql_prepare(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

0,

str,

1,

osqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

/* Set up the output XSQLVAR structure to allocate space for the return value. Again, this example assumes you know that P1 returns just one value. For an example of what to do if you're not sure, see isc_dsql_describe(). For an example of setting up an output XSQLVAR structure to allocate space for multiple return items, see the isc_dsql_execute() example program. */

ovar = osqlda->sqlvar[0];

dtype = (ovar->sqltype & ~1); /* Drop NULL bit for now. */

switch(dtype)

{

case SQL_TEXT:

ovar->sqldata = (char *)malloc(sizeof(char) * ovar->sqllen);

break;

case SQL_LONG:

ovar->sqldata = (char *)malloc(sizeof(long));

/* Process remaining types. */

. . .

}

if (ovar->sqltype & 1)

{

/* Assign a variable to hold NULL status. */

ovar->sqlind = &null_flag;

}

/* Allocate and fill in the input XSQLDA. This example assumes you know how many input parameters there are (1), and all other information necessary to supply a value. If this is not true, you will need to call isc_dsql_describe_bind(). */

isqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(1);

isqlda->version = SQLDA_VERSION1;

isqlda->sqln = 1;

isqlda->sqld = 1;

ivar = isqlda->sqlvar[0];

ivar->sqltype = SQL_TEXT;

ivar->sqllen = sizeof(state);

ivar->sqldata = state;

/* Execute the statement. */

isc_dsql_execute2(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

1,

isqlda,

osqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

/* Now process the value returned in osqlda->sqlvar[0]. */

. . .


isc_dsql_execute_immediate( )

Syntax	Example	See Also

Description

isc_dsql_execute_immediate() prepares the DSQL statement specified in statement, executes it once, and discards it. The statement must not be one that returns data (i.e., it must not be a SELECT or EXECUTE PROCEDURE statement).

If statement requires input parameter values (i.e., if it contains parameter markers), these values must be supplied in the input XSQLDA, xsqlda.

Tip:	If statement returns data, or if it needs to be executed more than once, use isc_dsql_prepare() and isc_dsql_execute() (or isc_dsql_execute2()) instead of isc_dsql_execute_immediate().

Important:	You must call isc_dsql_execute_immediate() for CREATE DATABASE and SET TRANSACTION; you cannot prepare and execute such statements by calling isc_dsql_prepare() and isc_dsql_execute().

Return Value

isc_dsql_execute_immediate() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_db_handle, isc_bad_trans_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_exec_immed2()

isc_dsql_execute()

isc_dsql_prepare()


isc_dsql_execute_immediate( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_execute_immediate(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_tr_handle *trans_handle,
unsigned short length,
char *statement,
unsigned short dialect,
XSQLDA *xsqlda);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database against which a DSQL statement is to be executed. The handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
length	Length of the DSQL statement in bytes. Set to 0 in C programs to indicate a null-terminated string.
statement	DSQL string to be executed.
dialect	Indicates the version of the extended SQL descriptor area (XSQLDA) passed to the function. Set this value to 1.
xsqlda	Pointer to an optional, previously allocated XSQLDA used for input. If input parameters are not supplied, set this value to NULL.


isc_dsql_execute_immediate( ) Example

Syntax	Definition

The following program fragment calls isc_dsql_execute_immediate():

char *insert_stmt =

"INSERT INTO CUSTOMER(CUSTNAME, BAL, CUSTNO)

VALUES("John Smith", 299.0, 5050)";

isc_dsql_execute_immediate(

status_vector,

&database_handle,	 /* Set in previous isc_attach_database() call. */

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

0,

insert_stmt,

1,

NULL);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}


isc_dsql_exec_immed2( )

Syntax	Example	See Also

Description

isc_dsql_exec_immed2() prepares the DSQL statement specified in statement, executes it once, and discards it. statement can return a single set of values (i.e, it can be an EXECUTE PROCEDURE or singleton SELECT) in the output XSQLDA.

If statement requires input parameter values (i.e., if it contains parameter markers), these values must be supplied in the input XSQLDA, in_xsqlda.

For statements that return multiple rows of data, use isc_dsql_prepare(), isc_dsql_execute2(), and isc_dsql_fetch().

Return Value

isc_dsql_exec_immed2() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_db_handle, isc_bad_trans_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_execute2()

isc_dsql_prepare()


isc_dsql_exec_immed2( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_exec_immed2(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_tr_handle *trans_handle,
unsigned short length,
char *statement,
unsigned short dialect,
XSQLDA *in_xsqlda,
XSQLDA *out_xsqlda);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database against which a DSQL statement is to be executed. db_handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
length	Length of the DSQL statement, in bytes. Set to 0 in C programs to indicate a null-terminated string.
statement	DSQL string to be executed.
dialect	Indicates the version of the extended SQL descriptor area (XSQLDA) passed to the function. Set this value to 1.
in_xsqlda	Pointer to an optional, previously allocated XSQLDA used for input. If input parameters are not supplied, set this value to NULL.
out_xsqlda	Pointer to an optional, previously allocated XSQLDA used for results of statement execution. If not required, set this value to NULL.


isc_dsql_exec_immed2( ) Example

Syntax	Definition

The following program fragment calls isc_dsql_exec_immed2():

ISC_STATUS status_vector[20];

XSQLDA *in_xsqlda, *out_xsqlda;

char *execute_p1 = "EXECUTE PROCEDURE P1 ?";

/* Set up input and output XSQLDA structures here. */

. . .

isc_dsql_exec_immed2(

status_vector,

&database_handle,	 /* Set in previous isc_attach_database() call. */

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

0,

execute_p1,

1,

in_xsqlda,

out_xsqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}


isc_dsql_fetch( )

Syntax	Example	See Also

Description

isc_dsql_fetch() retrieves one row of data into xsqlda each time it is called. It is used in a loop to retrieve and process each row of data for statements that return multiple rows. It is up to the application to provide the loop construct for fetching the data.

Before calling isc_dsql_fetch(), a statement must be prepared with isc_dsql_prepare(), and executed with isc_dsql_execute() (or isc_dsql_execute2() with a NULL output XSQLDA argument). Statement execution produces a result set containing the data returned. Each call to isc_dsql_fetch() retrieves the next available row of data from the result set into xsqlda.

Return Value

isc_dsql_fetch() returns the second element of the status vector. Zero indicates success. The value 100 indicates that no more rows remain to be retrieved. Any other nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_execute()

isc_dsql_execute2()

isc_dsql_prepare()


isc_dsql_fetch( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_fetch(
ISC_STATUS *status_vector,
isc_stmt_handle *stmt_handle,
unsigned short dialect,
XSQLDA *xsqlda);

Parameter	Description
status_vector	Pointer to the error status vector.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). The handle must not be NULL, or an error is returned in status_vector.
dialect	Indicates the version of the extended SQL descriptor area (XSQLDA) passed to the function. Set this value to 1.
xsqlda	Pointer to an optional, previously allocated XSQLDA used for results of statement execution.


isc_dsql_fetch( ) Example

Syntax	Definition

The following program fragment illustrates a sequence of calls that allocates an output XSQLDA, prepares a statement for execution, allocates an XSQLVAR structure in the XSQLDA for each column of data to be retrieved, executes the statement, producing a select list of returned data, then fetches and processes each row in a loop:

#include <ibase.h>

#define LASTLEN 20

#define FIRSTLEN 15

#define EXTLEN 4

typedef struct vary {

short vary_length;

char vary_string[1];

} VARY;

ISC_STATUS status_vector[20], retcode;

long SQLCODE;

XSQLDA *osqlda;

XSQLVAR *ovar;

short flag0, flag1, flag2;

char *str =

"SELECT last_name, first_name, phone_ext FROM phone_list

WHERE location = "Monterey" ORDER BY last_name, first_name";

char last_name[LASTLEN + 2];

char first_name[FIRSTLEN + 2];

char phone_ext[EXTLEN + 2];

VARY *vary;

/* Allocate an output XSQLDA osqlda. */

osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(3);

osqlda->version = SQLDA_VERSION1;

osqlda->sqln = 3;

/* Prepare the statement. */

isc_dsql_prepare(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

0,

str,

1,

osqlda);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

/* Set up an output XSQLVAR structure to allocate space for each item to be returned. */

osqlda->sqlvar[0].sqldata = last_name;

osqlda->sqlvar[0].sqltype = SQL_VARYING + 1;

osqlda->sqlvar[0].sqlind = &flag0;

osqlda->sqlvar[1].sqldata = first_name;

osqlda->sqlvar[1].sqltype = SQL_VARYING + 1;

osqlda->sqlvar[1].sqlind = &flag1;

osqlda->sqlvar[2].sqldata = phone_ext;

osqlda->sqlvar[2].sqltype = SQL_VARYING + 1;

osqlda->sqlvar[2].sqlind = &flag2;

/* Execute the statement. */

isc_dsql_execute(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

1,

NULL);

if (status_vector[0] == 1 && status_vector[1])

{

/* Process error. */

isc_print_status(status_vector);

return(1);

}

printf("\n%-20s %-15s %-10s\n\n", "LAST NAME", "FIRST NAME", "EXTENSION");

/* Fetch and print the records in the select list one by one. */

while ((retcode = isc_dsql_fetch(

status_vector,

&stmt_handle,

1,

osqlda)) == 0)

{

vary = (VARY *)last_name;

printf("%-20.*s ", vary->vary_length, vary->vary_string);

vary = (VARY *)first_name;

printf("%-15.*s ", vary->vary_length, vary->vary_string);

vary = (VARY *)phone_ext;

printf("%-4.*s ", vary->vary_length, vary->vary_string);

}

if (retcode != 100L)

{

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

return(1);

}


isc_dsql_free_statement( )

Syntax	Example	See Also

Description

isc_dsql_free_statement() either frees a statement handle and all resources allocated for it (option = DSQL_drop), or closes a cursor associated with the statement (option = DSQL_close).

Note:	If isc_dsql_free_statement() is called with any option value other than DSQL_drop or DSQL_close, the function does nothing.

DSQL_close

Call isc_dsql_free_statement() with the DSQL_close option to close a cursor after it is no longer needed, i.e., after fetching and processing all the rows resulting from the execution of a query. A cursor need only be closed in this manner if it was previously opened and associated with stmt_handle by isc_dsql_set_cursor_name().

DSQL_close closes a cursor, but the statement it was associated with remains available for further execution.

If you have used a cursor to perform updates or deletes on all the rows returned from the execution of a query, and you want to perform other update or delete operations on rows resulting from execution of the same statement again (possibly with different input parameters), follow these steps:

1.	Close the cursor with isc_dsql_free_statement().

2.	Re-open it with isc_dsql_set_cursor_name().

3.	If desired, change the input parameters to be passed to the statement.

4.	Re-execute the statement to retrieve a new select list.

5.	Retrieve rows in a loop with isc_dsql_fetch() and process them again with isc_dsql_execute_immediate().

DSQL_drop

Statement handles allocated with isc_dsql_allocate_statement() must be released when no longer needed by calling isc_dsql_free_statement() with the DSQL_drop option. This option frees all resources associated with the statement handle, and closes any open cursors associated with the statement handle.

Return Value

isc_dsql_free_statement() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_allocate_statement()

isc_dsql_alloc_statement2()

isc_dsql_set_cursor_name()


isc_dsql_free_statement( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_free_statement(
ISC_STATUS *status_vector,
isc_stmt_handle *stmt_handle,
unsigned short option);

Parameter	Description
status_vector	Pointer to the error status vector.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). stmt_handle must not be NULL, or an error is returned in status_vector.
option	Either DSQL_close or DSQL_drop.


isc_dsql_free_statement( ) Example

Syntax	Definition

The following program fragment shows examples of the two types of isc_dsql_free_statement() calls. It assumes that stmt_handle1 and stmt_handle2 are statement handles, each of which was previously allocated with either isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). A cursor is also assumed to have been associated with the statement referenced by stmt_handle1.

#include <ibase.h>

ISC_STATUS status_vector[20];

. . .

/* Free the cursor associated with stmt_handle1. */

isc_dsql_free_statement(

status_vector,

&stmt_handle1,

DSQL_close);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}

/* Free stmt_handle2. */

isc_dsql_free_statement(

status_vector,

&stmt_handle2,

DSQL_drop);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}


isc_dsql_prepare( )

Syntax	Example	See Also

Description

isc_dsql_prepare() readies the DSQL statement specified in statement for repeated execution by checking it for syntax errors and parsing it into a format that can be efficiently executed. All SELECT statements must be prepared with isc_dsql_prepare().

After a statement is prepared, it is available for execution as many times as necessary during the current session. Preparing a statement for repeated execution is more efficient than using isc_dsql_execute_immediate() or isc_dsql_exec_immed2() over and over again to prepare and execute a statement.

If a statement to be prepared does not return data, set the output XSQLDA to NULL. Otherwise, the output XSQLDA must be allocated prior to calling isc_dsql_prepare(). Allocate the XSQLDA using the macro, XSQLDA_LENGTH, defined in ibase.h, as follows:

xsqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));

XSQLDA_LENGTH calculates the number of bytes required when n result columns will be returned by the statement, and allocates the appropriate amount of storage.

After allocating the XSQLDA xsqlda, set xsqlda->version to SQLDA_VERSION1, and set xsqlda_sqln to indicate the number of XSQLVAR structures allocated.

When isc_dsql_prepare() is called, it fills in the other fields of the XSQLDA and all the XSQLVARs with information such as the data type, length, and name of the corresponding select-list items in the statement. It fills in xsqlda->sqld with the actual number of select-list items returned. If xsqlda->sqld is greater than xsqlda->sqln, then enough room is not allocated, and the XSQLDA must be resized by following these steps:

1.	Record the current value of the xsqlda->sqld.

2.	Free the storage previously allocated for xsqlda.

3.	Reallocate storage for xsqlda, this time specifying the correct number (from step 1) in the argument to XSQLDA_LENGTH.

4.	Reset xsqlda->sqld and xsqlda->version.

5.	Execute isc_dsql_describe() to fill in the xsqlda fields.

Note:	If the prepared statement requires input parameter values, then an input XSQLDA will need to be allocated and filled in with appropriate values prior to calling isc_dsql_execute() or isc_dsql_execute2(). You can either allocate and directly fill in all the fields of the input XSQLDA, or you can allocate it, call isc_dsql_describe_bind() to get information regarding the number and types of parameters required, then fill in appropriate values.

Return Value

isc_dsql_prepare() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle, isc_bad_trans_handle, or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_describe()

isc_dsql_describe_bind()

isc_dsql_execute()

isc_dsql_execute2()

isc_dsql_fetch()


isc_dsql_prepare( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_prepare(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle,
isc_stmt_handle *stmt_handle,
unsigned short length,
char *statement,
unsigned short dialect,
XSQLDA *xsqlda);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). The handle must not be NULL, or an error is returned in status_vector.
length	Length of the DSQL statement, in bytes. Set to 0 in C programs to indicate a null-terminated string.
statement	DSQL string to be executed.
dialect	Indicates the version of the extended SQL descriptor area (XSQLDA) passed to the function. Set this value to 1.
xsqlda	Pointer to an optional, previously allocated XSQLDA used for results of statement execution.


isc_dsql_prepare( ) Example

Syntax	Definition

The following program fragment illustrates the allocation of the output XSQLDA, and a call to isc_dsql_prepare().

#include <ibase.h>

ISC_STATUS status_vector[20];

XSQLDA *osqlda;

char *query =

"SELECT CITY, STATE, POPULATION FROM CITIES \

WHERE STATE = "NY" ORDER BY CITY DESCENDING";

osqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(3);

osqlda->version = SQLDA_VERSION1;

osqlda->sqln = 3;

isc_dsql_prepare(

status_vector,

&tr_handle,	 /* Set in previous isc_start_transaction() call. */

&stmt_handle,

/* Allocated previously by isc_dsql_allocate_statement()

or isc_dsql_alloc_statement2() call. */

0,

query,

1,

osqlda);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}

More complete examples showing the subsequent execution and fetching of result data are provided in the example programs for isc_dsql_execute(), isc_dsql_execute2(), and isc_dsql_fetch().


isc_dsql_set_cursor_name( )

Syntax	Example	See Also

Description

isc_dsql_set_cursor_name() defines a cursor name, and associates it with a DSQL statement handle for a statement that returns multiple rows of data (e.g., SELECT), effectively opening the cursor for access.

A cursor is a one-way pointer into the ordered set of rows retrieved by a statement. A cursor is only needed to process positioned UPDATE and DELETE statements made against the rows retrieved by isc_dsql_fetch() for SELECT statements that specify an optional FOR UPDATE OF clause.

Note:	In UPDATE or DELETE statements, the cursor name may not be supplied as a parameter marker (?).

When a cursor is no longer needed, close it with the DSQL_close option of isc_dsql_free_statement().

Return Value

isc_dsql_set_cursor_name() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to isc_bad_stmt_handle or another InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_fetch()

isc_dsql_free_statement()


isc_dsql_set_cursor_name( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_set_cursor_name(
ISC_STATUS *status_vector,
isc_stmt_handle *stmt_handle,
char *cursor_name,
unsigned short type);

Parameter	Description
status_vector	Pointer to the error status vector.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). The handle must not be NULL, or an error is returned in status_vector.
cursor_name	String name of a cursor.
type	Reserved for future use. Set to NULL.


isc_dsql_set_cursor_name( ) Example

Syntax	Definition

The following pseudo-code illustrates the calling sequence necessary to execute an UPDATE or DELETE with the WHERE CURRENT OF clause using a cursor name established and opened with isc_dsql_set_cursor_name().

#include <ibase.h>

ISC_STATUS status_vector[20], fetch_stat;

isc_stmt_handle st_handle = NULL;

char *cursor = "S";

/* Allocate the statement handle st_handle. */

isc_dsql_allocate_statement(

status_vector,

&db, /* Database handle set by isc_attach_database() call. /*

&st_handle);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}

/* Set up an output XSQLDA osqlda here. */

/* Call isc_dsql_prepare() to prepare the SELECT statement. */

/* Set up an input XSQLDA, if needed, for the SELECT statement. */

/* Call isc_dsql_execute() to execute the SELECT statement. */

/* Set up an input XSQLDA (if needed) for the UPDATE or DELETE statement. */

/* Declare the cursor name, and associate it with st_handle. */

isc_dsql_set_cursor_name(

status_vector,

&st_handle,

cursor, 0);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}

/* Fetch rows one by one, with the cursor pointing to each row as it is fetched, and execute an UPDATE or DELETE statement to update or delete the row pointed to by the cursor. */

while ((fetch_stat = isc_dsql_fetch(

status_vector, &st_handle, 1, osqlda)) == 0)

{

. . .

/* Update or delete the current row by executing an "UPDATE ...

WHERE CURRENT OF S" or "DELETE ... WHERE CURRENT OF S"

statement, where "S" is the name of the cursor declared in

isc_dsql_set_cursor_name(). */

}


isc_dsql_sql_info( )

Syntax	Example	See Also

Description

isc_dsql_sql_info() returns requested information about a statement prepared with a call to isc_dsql_prepare(). The main application need for this function is to determine the statement type of an unknown prepared statement, for example, a statement entered by the user at run time.

Requested information can include the:

Statement type.

Number of input parameters required by the statement.

Number of output values returned by the statement.

Detailed information regarding each input parameter or output value, including its data type, scale, and length.

To use isc_dsql_sql_info(), allocate an item-list buffer that describes the type of information requested, and allocate a result buffer, where the function can return the desired information. For example, to determine the statement type of an unknown, but prepared statement, you would allocate a one-element item-list buffer, and fill it with the macro constant, isc_info_sql_stmt_type, defined in ibase.h:

char type_item[];

type_item[] = {isc_info_sql_stmt_type};

Note:	Additional information item macros for requested items can be found in ibase.h under the comment, “SQL information items.”

The result buffer must be large enough to contain any data returned by the call. The proper size for this buffer depends on the information requested. If not enough space is allocated, then isc_dsql_sql_info() puts the predefined value, isc_info_truncated, in the last byte of the result buffer. Generally, when requesting statement type information, 8 bytes is a sufficient buffer size. Declaring a larger than necessary buffer is also safe. A request to identify a statement type returns the following information in the result buffer:

1.	One byte containing isc_info_sql_stmt_type.

2.	Two bytes containing a number, n, telling how many bytes compose the subsequent value.

3.	One or two bytes specifying the statement type.

4.	A final byte containing the value isc_info_end (0).

The values placed in the result buffer are not aligned. Furthermore, all numbers are represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers have the sign in the last byte. Convert the numbers to a data type native to your system before interpreting them.

Note:	All information about a statement except its type can be more easily determined by calling functions other than isc_dsql_sql_info(). For example, to determine the information to fill in an input XSQLDA, call isc_dsql_describe_bind(). To fill in an output XSQLDA, call isc_dsql_prepare() or isc_dsql_describe().

Return Value

isc_dsql_sql_info() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_dsql_describe_bind()

isc_dsql_describe()

isc_vax_integer()


isc_dsql_sql_info( ) Syntax

Example	Definition

ISC_STATUS isc_dsql_sql_info(
ISC_STATUS *status_vector,
isc_stmt_handle *stmt_handle,
unsigned short item_length,
char *items,
unsigned short buffer_length,
char *buffer);

Parameter	Description
status_vector	Pointer to the error status vector.
stmt_handle	Pointer to a statement handle previously allocated with isc_dsql_allocate_statement() or isc_dsql_alloc_statement2(). The handle must not be NULL, or an error is returned in status_vector.
item_length	Number of bytes in the string of information items in items.
items	String of requested information items.
buffer_length	Number of bytes in the result buffer, buffer.
buffer	User-provided buffer for holding returned data. It must be large enough to hold the information requested.


isc_dsql_sql_info( ) Example

Syntax	Definition

The following illustrates a call to isc_dsql_sql_info() to determine the statement type of the statement whose handle is referenced by stmt:

int statement_type;

int length;

char type_item[] = {isc_info_sql_stmt_type};

char	 res_buffer[8];

isc_dsql_sql_info(

status_vector,

&stmt,

/* Allocated previously by isc_dsql_allocate_statement() or isc_dsql_alloc_statement2() call. */

sizeof(type_item),

type_item,

sizeof(res_buffer),

res_buffer);

if (res_buffer[0] == isc_info_sql_stmt_type)

{

length = isc_vax_integer(buffer[1], 2);

statement_type = isc_vax_integer(buffer[3], length);

}


isc_encode_date( )

Syntax	Example	See Also

Description

isc_encode_date() translates a date in a C time structure into an ISC_QUAD format internal to InterBase. This call is used prior to writing DATE data to a table to guarantee that the date is in a format recognized by InterBase.

Use the isc_dsql family of API calls to insert or update DATE data from the ISC_QUAD structure in a table.

Return Value

None.


See Also

isc_decode_date()


isc_encode_date( ) Syntax

Example	Definition

void isc_encode_date(
void *tm_date,
ISC_QUAD *ib_date);

Parameter	Description
tm_date	Pointer to a C tm structure.
ib_date	Pointer to an eight-byte ISC_QUAD structure containing a date in InterBase format.


isc_encode_date( ) Example

Syntax	Definition

The following code fragment illustrates declaring time structures and calling isc_encode_date() to translate a C time format into an InterBase date format prior to inserting or updating a table:

#include <time.h>

#include <ibase.h>

. . .

struct tm hire_time;

ISC_QUAD hire_date;

. . .

/* Store date info into the tm struct here. */

. . .

isc_encode_date(&hire_time, &hire_date);

/* Now use a DSQL INSERT or UPDATE statement to move the date into a DATE column. */


isc_event_block( )

Syntax	Example	See Also

Description

isc_event_block() must be called before any other event functions. It:

Allocates two event parameter buffers of the same size, and stores their addresses into the character pointers addressed by event_buffer and result_buffer.

Stores into the buffer referenced by event_buffer the names and event counts for each of the specified events. The names are the ones that appear as the final arguments to isc_event_block(). The event counts are initialized to zero and are used to specify how many times each event has been posted prior to each wait for events to occur.

Returns the length, in bytes, of the buffers.

The buffers, and their lengths, are used in subsequent calls to isc_wait_for_event(), isc_que_events(), and isc_event_counts(). event_buffer is used to indicate the events of interest, and to hold the counts in effect before a wait for one of the events. After an event is posted, result_buffer is filled in exactly as event_buffer, except that the event counts are updated. isc_event_counts() is then called to determine which events were posted between the time the counts were set in event_buffer, and the time the counts are set in result_buffer.

Return Value

isc_event_block() returns a number that is the size, in bytes, of each event parameter buffer it allocates.


See Also

isc_event_counts()

isc_que_events()

isc_wait_for_event()


isc_event_block( ) Syntax

Example	Definition

ISC_STATUS isc_event_block(
char **event_buffer,
char **result_buffer,
unsigned short id_count,
. . .);

Parameter	Description
event_buffer	Address of a character pointer. This function allocates and initializes an event parameter buffer, and stores its address into the character pointer.
result_buffer	Address of a character pointer. This function allocates an event parameter buffer, and stores its address into the character pointer.
id_count	Number of event identifier strings that follow.
. . .	Up to 15 null-terminated and comma-separated strings that each name an event.


isc_event_block( ) Example

Syntax	Definition

The following program fragment illustrates a call to isc_event_block():

#define number_of_stocks 3;

char *event_buffer, *result_buffer;

long length;

length = isc_event_block(

&event_buffer,

&result_buffer,

number_of_stocks,

"DEC", "HP", "SUN");


isc_event_counts( )

Syntax	Example	See Also

Description

isc_event_counts() compares the event counts in the event parameter buffers, event_buffer and result_buffer, and sets count_array to contain the differences. It then modifies event_buffer to contain the same event counts as result_buffer in preparation for the next call to either isc_wait_for_event() or isc_que_events().

The counts in event_buffer specify how many times each event had been posted since the previous call to isc_event_wait() or isc_que_events(). The counts in result_buffer equal the values in event_buffer plus the number of additional times an event is posted after the current call to isc_event_wait() or isc_que_events(). If an event is posted after a call to either of these functions, its count is greater in result_buffer than in event_buffer. Other event counts may also be greater because an event may have been posted between calls to either of these functions. The values in status_vector are the differences in values between event_buffer and result_buffer. This mechanism of comparing all the counts ensures that no event postings are missed.

Return Value

None.


See Also

isc_que_events()

isc_wait_for_event()


isc_event_counts( ) Syntax

Example	Definition

void isc_event_counts(
ISC_STATUS *status_vector
short buffer_length,
char *event_buffer,
char *result_buffer);

Parameter	Description
status_vector	Pointer to the status vector, which is used to store the difference in event counts for each corresponding event in event_buffer and result_buffer.
buffer_length	Length of the event parameter buffers, returned by the isc_event_block() call that allocated them.
event_buffer	Pointer to the event parameter buffer that specifies the event counts prior to the previous call to isc_wait_for_event() or isc_que_events().
result_buffer	Pointer to the event parameter buffer filled in as a result of posting an event.


isc_event_counts( ) Example

Syntax	Definition

The following program fragment illustrates the set-up and waiting on any of the events named “DEC”, “HP”, or “SUN”, then calling isc_event_counts() to determine which events have been posted:

#include <ibase.h>

#define number_of_stocks 3;

char *event_buffer, *result_buffer;

ISC_STATUS status_vector[20];

char *event_names[] = {"DEC", "HP", "SUN"};

long length;

int i;

length = isc_event_block(

&event_buffer,

&result_buffer,

number_of_stocks,

"DEC", "HP", "SUN");

isc_wait_for_event(

status_vector,

&database_handle, /* Set by previous isc_attach_database(). */

length,	/* Returned from isc_event_block(). */

event_buffer,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

}

isc_event_counts(

status_vector,

(short) length,

event_buffer,

result_buffer);

for (i=0; i<number_of_stocks; i++)

if (status_vector[i])

{

/* The event has been posted. Do whatever is appropriate, for example,

 initiating a buy or sell order. */

;

}


isc_expand_dpb( )

Syntax	Example	See Also

Description

isc_expand_dpb() builds or expands a database parameter buffer dynamically. Its main use is to simplify the building of the DPB prior to a call to isc_attach_database(), or to allow an end user to supply a user name and password combination at run time. In many cases, the DPB must be constructed programmatically, but isc_expand_dpb() enables an application to pass user names, password, message file, and character set parameters to the function, which then adds them to an existing DPB.

A pointer to a previously allocated and initialized DPB must be passed to isc_expand_dpb() along with a pointer to a variable containing the current size of the DPB when this function is called. If the space allocated for the DPB is not large enough for the parameters passed to isc_expand_dpb(), then the function reallocates a larger DPB, preserving its current contents, and adds the new parameters.

To ensure proper memory management, applications that call isc_expand_dpb() should always allocate DPBs large enough to hold all anticipated parameters.

Return Value

None.


See Also

isc_attach_database()


isc_expand_dpb( ) Syntax

Example	Definition

void isc_expand_dpb(
char **dpb,
unsigned short *dpb_size,
. . .);

Parameter	Description
dpb	Pointer to an existing DPB.
dpb_size	Pointer to the current size, in bytes, of the DPB.
. . .	Pointers to items to insert into the expanded DPB.


isc_expand_dpb( ) Example

Syntax	Definition

The following code calls isc_expand_dpb() to create a DPB, then attaches to a database using the newly created DPB:

#include <ibase.h>

char *dpb;

ISC_STATUS status_vector[20];

isc_db_handle handle = NULL;

short dpb_length;

/* Build the database parameter buffer. */

dpb = (char *) malloc(50);

dpb_length = 0;

isc_expand_dpb(&dpb, &dpb_length, isc_dpb_user_name, user_name, isc_dpb_password, user_password, NULL);

isc_attach_database(

status_vector,

0,

"employee.db",

&handle,

dpb_length,

dpb_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

/* An error occurred. */

isc_print_status(status_vector);

return(1);

}


isc_get_segment( )

Syntax	Example	See Also

Description

isc_get_segment() reads a BLOB segment from a previously opened BLOB. You can set the seg_buffer_length parameter to a size that is efficient for a particular type of BLOB data. For example, if you are reading BLOB data from a text file, you might set the segment buffer length to 80, to take advantage of the 72 to 80 character line lengths that are common in text files. By periodically checking the value of the actual segment length in your loop, you can determine an end-of-line or end-of-file condition.

Before reading any part of a BLOB, you must open the BLOB with a call to isc_open_blob2(). isc_get_segment() behaves differently depending on which call precedes it. If the most recent call is to isc_open_blob2(), then a call to isc_get_segment() reads the first segment in the BLOB. If the most recent call is to isc_get_segment(), then it reads the next segment.

If BLOB filters are specified when a BLOB is opened, then each segment retrieved by isc_get_segment() is filtered on read.

Important:	BLOB filters are not supported on NetWare.

You can read bitmaps and other binary files directly, without filtering, unless you intend to change from one format to another, say from .GEM to .BMP. You can also store compressed bitmaps directly in a database, in formats such as .JPG (JPEG), .BMP (Windows native bitmaps), or .GIF (CompuServe Graphic Interchange Format). No filtering is required.

You can store bitmaps in a database in row-major or column-major order.

Return Value

isc_get_segment() returns the second element of the status vector. Zero indicates success. isc_segment indicates the buffer is not large enough to hold the entire current segment; the next call to isc_get_segment() gets the next chunk of the oversized segment rather than getting the next segment. isc_segstr_eof indicates that the last segment of the BLOB has been read. Any other nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_create_blob2()

isc_open_blob2()

isc_put_segment()


isc_get_segment( ) Syntax

Example	Definition

ISC_STATUS isc_get_segment(
ISC_STATUS *status_vector,
isc_blob_handle *blob_handle,
unsigned short *actual_seg_length,
unsigned short seg_buffer_length,
char *seg_buffer);

Parameter	Description
status_vector	Pointer to the error status vector.
blob_handle	Pointer to the handle of the BLOB you want to read.
actual_seg_length	Pointer to the actual segment length that InterBase reads into the buffer. This is useful if the segment length is shorter than the buffer length.
seg_buffer_length	Length of the segment buffer.
seg_buffer	Pointer to the segment buffer.


isc_get_segment( ) Example

Syntax	Definition

The following call gets a segment from one BLOB and writes it to another:

get_status = isc_get_segment(status, &from_blob, &seg_len, 80, buffer);

if (status[0] == 1 && status[1])

{

isc_print_status(status);

return(1);

}

if (get_status != isc_segstr_eof)

write_status = isc_put_segment(status, &to_blob, seg_len, buffer);

if (status[0] == 1 && status[1])

{

isc_print_status(status);

return(1);

}


isc_interprete( )

Syntax	Example	See Also

Description

Given both the location of a storage buffer allocated in a program, and the address of the status vector, isc_interprete() builds an error message string from the information in the status vector, puts the formatted string in the buffer where the program can manipulate it, and advances the status vector pointer to the start of the next cluster of error message information. For example, you might declare an error string buffer, call isc_interprete() to retrieve the first error message and insert the message into the buffer, write the buffer to a log file, then peek at the next cluster to see if it contains more error information.

isc_interprete() retrieves and formats a single message each time it is called. When an error occurs, however, the status vector usually contains more than one error message. To retrieve all relevant error messages, you must make repeated calls to isc_interprete() until no more messages are returned.

Important:	Do not pass the address of the status vector directly, because each time isc_interprete() is called, it modifies the pointer to the status vector to point to the start of the next available message.

To display all error messages on the screen instead of to a buffer, use isc_print_status().

Return Value

If successful, isc_interprete() returns the length of the error message string it stores in buffer. It also advances the status vector pointer to the start of the next cluster of error message information.

If there are no more messages in the status vector, or if isc_interprete() cannot interpret the next message, it returns 0.


See Also

isc_print_sqlerror()

isc_print_status()

isc_sqlcode()

isc_sql_interprete()


isc_interprete( ) Syntax

Example	Definition

ISC_STATUS isc_interprete(
char *buffer,
ISC_STATUS **status vector);

Parameter	Description
buffer	Application buffer into which to store an InterBase error message.
status_vector	Pointer to a pointer to the error status vector.


isc_interprete( ) Example

Syntax	Definition

The following code declares a message buffer, a status vector, and a pointer to the vector, then illustrates how repeated calls are made to isc_interprete() to store all messages to the buffer:

#include <ibase.h>

char msg[512];

ISC_STATUS status_vector[20];

long *pvector; /* Pointer to pointer to status vector. */

FILE *efile; /* Code fragment assumes this points to an open file. */

. . .

pvector = status_vector; /* (Re)set to start of status vector. */

isc_interprete(msg, &pvector); /* Retrieve first message. */

fprintf(efile, "%s\n", msg); /* Write buffer to log file. */

msg[0] = '-'; /* Append leading hyphen to secondary messages. */

while(isc_interprete(msg + 1,&pvector)) /* More messages? */

{

fprintf(efile, "%s\n", msg); /* If so, write them, too. */

}

fclose(efile);

. . .


isc_open_blob2( )

Syntax	Example	See Also

Description

isc_open_blob2() opens an existing BLOB for retrieval and optional filtering from one BLOB subtype to another.

Important:	Using BLOB filters is not supported on NetWare.

Input and output BLOB filter types are passed to isc_open_blob2() as subtype information in a previously populated BLOB parameter buffer, pointed to by bpb_address. If BLOB filters are not needed or cannot be used, input and output filter types in the BPB must be set to zero.

Return Value

isc_open_blob2() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_close_blob()


isc_open_blob2( ) Syntax

Example	Definition

ISC_STATUS isc_open_blob2(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
isc_tr_handle *trans_handle,
isc_blob_handle *blob_handle,
ISC_QUAD *blob_id,
short bpb_length,
char *bpb_address);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). db_handle must not be NULL, or an error is returned in status_vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
blob_handle	Pointer to the BLOB handle. The handle must be NULL when you make this call.
blob_id	Pointer to the 64-bit system-defined BLOB ID. The BLOB ID is stored in a field in the table and points to the first segment of the BLOB or to a page of pointers to BLOB fragments.
bpb_length	Length of the BLOB parameter buffer (BPB).
bpb_address	Pointer to the address of the BPB.


isc_open_blob2( ) Example

Syntax	Definition

The following fragment is excerpted from the example file, api9.c. The example program displays job descriptions that are passed through a filter.

while ((fetch_stat = isc_dsql_fetch(status, &stmt, 1, sqlda)) == 0)

{

printf("\nJOB CODE: %5s GRADE: %d", job_code, job_grade);

printf(" COUNTRY: %-20s\n\n", job_country);

/* Open the blob with the fetched blob_id. */

isc_open_blob2(status, &DB, &trans, &blob_handle, &blob_id, 9, bpb);

if (status[0] == 1 && status[1])

{

isc_print_status(status);

return(1);

}

}


isc_prepare_transaction( )

Syntax	Example	See Also

Description

isc_prepare_transaction() initiates the first phase of a two-phase commit under program direction. It alerts InterBase, which polls all database participants and waits for replies. The isc_prepare_transaction() function puts the transaction in limbo.

Because a call to this function indicates that you intend to control all phases of the commit, you must complete the second phase of the commit by explicitly calling the isc_commit_transaction() function.

If a call to isc_prepare_transaction() fails, the application should roll back the transaction with a call to the isc_rollback_transaction() function.

Tip:	If you want InterBase to automatically perform the two-phase commit, call isc_commit_transaction() without calling isc_prepare_transaction().

Return Value

isc_prepare_transaction() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_commit_transaction()

isc_prepare_transaction2()

isc_rollback_transaction()


isc_prepare_transaction( ) Syntax

Example	Definition

ISC_STATUS isc_prepare_transaction(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.


isc_prepare_transaction( ) Example

Syntax	Definition

The following example executes the first phase of a two-phase commit and includes a rollback in case of failure:

isc_prepare_transaction(status_vector, &trans);

if (status_vector[0] == 1 && status_vector[1])

rb_status = isc_rollback_transaction(status_vector, &trans)

else

{

isc_commit_transaction(status_vector, &trans);

if (!(status_vector[0] == 1 && status_vector[1]))

fprintf("Commit successful.");

}


isc_prepare_transaction2( )

Syntax	Example	See Also

Description

isc_prepare_transaction2() performs the first phase of a two-phase commit, just as isc_prepare_transaction() does, but isc_prepare_transaction2() expects you to provide two additional arguments:

An information message to write to the RDB$TRANSACTION_DESCRIPTION column in the RDB$TRANSACTIONS system table that describes the transaction to commit, so that recovery is possible in the event a system crash occurs during the completion of the commit.

The length, in bytes, of the information message.

By electing to use isc_prepare_transaction2(), you are, in effect, disabling the automatic recovery functions inherent in the two-phase commit. It is your responsibility to deal with recovery issues that might occur during failure of the two-phase commit. Normally, InterBase automatically writes to the RDB$TRANSACTION_DESCRIPTION column in the RDB$TRANSACTIONS system table information that makes it possible to reconnect following a system crash during the commit. You can manually write a message string into RDB$TRANSACTIONS, by using the message parameter in this function.

You might choose to avoid this action entirely if you determine that there is too much overhead associated with writing this BLOB data to the system table every time your application commits.

Return Value

isc_prepare_transaction2() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_commit_transaction()

isc_prepare_transaction()

isc_rollback_transaction()


isc_prepare_transaction2( ) Syntax

Example	Definition

ISC_STATUS isc_prepare_transaction2(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle,
unsigned short message_length,
char *message);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
message_length	Length of message, in bytes.
message	Transaction description buffer.


isc_prepare_transaction2( ) Example

Syntax	Definition

The following example executes the first phase of a two-phase commit and includes a rollback in case of failure:

isc_prepare_transaction2(status_vector, &trans, msg_len, msg);

if (status_vector[0] == 1 && status_vector[1])

rb_status = isc_rollback_transaction(status_vector, &trans);


isc_print_sqlerror( )

Syntax	Example	See Also

Description

During the processing of DSQL API calls, SQL errors can occur. SQL errors are generally reported in a variable called SQLCODE. DSQL calls return error information to a user-defined error status vector like any other API call, but isc_print_sqlerror() can be used to interpret the primary error condition as an SQL error message for direct display on the screen.

To use isc_print_sqlerror(), an application must declare both an SQLCODE variable for holding the SQL error number, and an error status vector for holding InterBase error information.

isc_print_sqlerror() displays the SQLCODE value, a related SQL error message, and any additional InterBase error messages in the status array.

Note:	Some windowing systems do not encourage or permit direct screen writes. Do not use isc_print_sqlerror() when developing applications for these environments. Instead, use isc_sql_interprete() and isc_interprete() to capture messages to a buffer for display.

Return Value

None.


See Also

isc_interprete()

isc_print_status()

isc_sql_interprete()

isc_sqlcode()


isc_print_sqlerror( ) Syntax

Example	Definition

void isc_print_sqlerror(
short SQLCODE,
ISC_STATUS *status_vector);

Parameter	Description
SQLCODE	Variable containing an SQLCODE value.
status_vector	Pointer to the error status vector.


isc_print_sqlerror( ) Example

Syntax	Definition

The following code calls isc_print_sqlerror() when an error occurs:

#include <ibase.h>

long SQLCODE;

ISC_STATUS status_vector[20];

. . .

if (status_vector[0] == 1 && status_vector[1])

{

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

}


isc_print_status( )

Syntax	Example	See Also

Description

isc_print_status() builds all error messages based on the contents of the error status vector, and displays them on the screen. status_vector must be declared in the program as an array of twenty elements.

Return Value

isc_print_status() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_interprete()

isc_print_sqlerror()

isc_sqlcode()

isc_sql_interprete()


isc_print_status( ) Syntax

Example	Definition

ISC_STATUS isc_print_status(ISC_STATUS *status_vector);

Parameter	Description
status_vector	Pointer to the error status vector.


isc_print_status( ) Example

Syntax	Definition

The following code displays error messages when an error occurs during processing:

#include <ibase.h>

ISC_STATUS status_vector[20];

. . .

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}


isc_put_segment( )

Syntax	Example	See Also

Description

isc_put_segment() writes a BLOB segment in seg_buffer_address to a BLOB previously created and opened with isc_create_blob2().

If a BLOB filter was specified when the BLOB was created, then each segment is filtered before storing the result into the BLOB.

isc_put_segment() behaves slightly differently depending on which call precedes it. If the most recent call was to isc_create_blob() or isc_create_blob2(), then a call to isc_put_segment() writes the first segment of the BLOB. If the most recent call was to isc_put_segment(), then it writes the next segment.

You can write bitmaps and other binary files directly, without filtering, unless you intend to change from one format to another, say from .GEM to .BMP. You can also store compressed bitmaps directly in a database, in formats such as .JPG (JPEG), .BMP (Windows native bitmaps), or .GIF (CompuServe Graphic Interchange Format).

You can store bitmaps in your database in row-major or column-major order.

Note:	You cannot read a segment written with a call to isc_put_segment() until you have closed the BLOB with isc_close_blob(), then re-opened it with isc_open_blob2().

You cannot update a BLOB directly. If you want to modify BLOB data, you must:

Create a new BLOB.

Read the old BLOB data into a buffer where you can edit or modify it.

Write the modified data to the new BLOB.

Prepare and execute an UPDATE statement that will modify the BLOB column to contain the BLOB ID of the new BLOB, replacing the old BLOB's BLOB ID.

Return Value

isc_put_segment() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_close_blob()

isc_get_segment()

isc_open_blob2()


isc_put_segment( ) Syntax

Example	Definition

ISC_STATUS isc_put_segment(
ISC_STATUS *status_vector,
isc_blob_handle *blob_handle,
unsigned short seg_buffer_length,
char *seg_buffer);

Parameter	Description
status_vector	Pointer to the error status vector.
blob_handle	Pointer to the handle of the BLOB to which you want to write. A call to isc_create_blob2() sets a value for this handle.
seg_buffer_length	Length of the BLOB segment buffer.
seg_buffer	Pointer to the BLOB segment buffer that contains data for writing.


isc_put_segment( ) Example

Syntax	Definition

The following example reads a segment of one BLOB and writes it to another BLOB:

get_status = isc_get_segment(status, &from_blob, &seg_len, 80, buffer);

if (status[0] == 1 && status[1])

{

isc_print_status(status);

return(1);

}

if (get_status != isc_segstr_eof)

write_status = isc_put_segment(status, &to_blob, seg_len, buffer);

if (status[0] == 1 && status[1])

{

isc_print_status(status);

return(1);

}


isc_que_events( )

Syntax	Example	See Also

Description

isc_que_events() is called to request asynchronous notification of any of the events listed in event_buffer. Upon completion of the call, but before events are posted, control is returned to the calling application, which can continue other processing. When a requested event is posted, InterBase calls the event function specified in event_function to process event occurrence.

After event_function is called, you must call isc_que_events() again if you want to start another asynchronous wait on the specified events.

Important:	isc_que_events() cannot be called from within event_function.

If you want to cancel your isc_que_events() request for asynchronous event notification, call isc_cancel_events().

Note:	To request synchronous notification, call isc_wait_for_event().

Return Value

isc_que_events() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_cancel_events()

isc_event_block()

isc_event_counts()

isc_wait_for_event()


The Event Function

The event function, event_function, should be written to take three arguments:

1.	The event_function_arg specified in the call to isc_que_events(). This is usually a pointer to the event parameter buffer that should be filled in with updated event counts.

2.	The length of the following events_list buffer.

3.	A pointer to the events_list buffer, a temporary event parameter buffer just like that passed to isc_que_events(), except for having updated event counts.

Your result buffer is not automatically updated by the event occurrence; it is up to the event_function to copy the temporary events_list buffer to the more permanent buffer that your application utilizes.

event_function also needs to let the application know that it has been called, e.g., by setting a global flag.

A sample event_function appears below:

isc_callback event_function

(char *result, short length, char *updated)

{

/* Set the global event flag. */

event_flag++

/* Copy the temporary updated buffer to the result buffer. */

while (length--)

*result++ = *updated++;

return(0);

};


isc_que_events( ) Syntax

Example	Definition

ISC_STATUS isc_que_events(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
ISC_LONG *event_id,
short length,
char *event_buffer,
isc_callback event_function,
void *event_function_arg);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database against which the events are expected to be posted. db_handle must not be NULL, or an error is returned in status_vector.
event_id	Pointer to an event identifier to set.
length	Length of the event parameter buffers, returned by the isc_event_block() call which allocated them.
event_buffer	Pointer to the event parameter buffer that specifies the current counts of the events to be waited on. This buffer should have been initially allocated and filled in by a call to isc_event_block().
event_function	Pointer to the address of the function to receive event notification.
event_function_arg	First argument to be passed to event_function, usually a pointer to the event parameter buffer you want filled in with updated event counts.


isc_que_events( ) Example

Syntax	Definition

The following program fragment illustrates calling isc_que_events() to wait asynchronously for event occurrences. Within a loop, it performs other processing, and checks the event flag (presumably set by the specified event function) to determine when an event has been posted. If one has, the program resets the event flag, calls isc_event_counts() to determine which events have been posted since the last call to isc_que_events(), and calls isc_que_events() to initiate another asynchronous wait.

#include <ibase.h>

#define number_of_stocks 3;

#define MAX_LOOP 10

char *event_names[] = {"DEC", "HP", "SUN"};

char *event_buffer, *result_buffer;

ISC_STATUS count_array[number_of_stocks];

short length;

ISC_LONG event_id;

int i, counter;

int event_flag = 0;

length = (short)isc_event_block(

&event_buffer,

&result_buffer,

number_of_stocks,

"DEC", "HP", "SUN");

isc_que_events(

status_vector,

&database_handle, /* Set in previous isc_attach_database(). */

&event_id,

length, /* Returned from isc_event_block(). */

event_buffer,

(isc_callback)event_function,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

};

counter = 0;

while (counter < MAX_LOOP)

{

counter++;

if (!event_flag)

{

/* Do whatever other processing you want. */

;

}

else

{	event_flag = 0;

isc_event_counts(

count_array,

length,

event_buffer,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

}

for (i=0; i<number_of_stocks; i++)

if (count_array[i])

{

/* The event has been posted. Do whatever is appropriate,

for example, initiating a buy or sell order.

Note: event_names[i] tells the name of the event

corresponding to count_array[i]. */

;

}

isc_que_events(

status_vector,

&database_handle,

&event_id,

length,

event_buffer,

(isc_callback)event_function,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

}

}	/* End of else. */

}	/* End of while. */

/* Let InterBase know you no longer want to wait asynchronously. */

isc_cancel_events(

status_vector,

&database_handle,

&event_id);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

}


isc_rollback_transaction( )

Syntax	Example	See Also

Description

isc_rollback_transaction() rolls back a specified transaction, closes record streams, frees system resources, and sets the transaction handle to zero. It is typically used to undo all database changes made by a transaction when an error occurs.

A call to this function can fail only if:

You pass a NULL or invalid transaction handle.

The transaction dealt with more than one database and a communications link fails during the rollback operation. If that happens, subtransactions on the remote node will end up in limbo. You must use Server Manager or command-line gfix to manually roll back those transactions.

Return Value

isc_rollback_transaction() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_commit_transaction()


isc_rollback_transaction( ) Syntax

Example	Definition

ISC_STATUS isc_rollback_transaction(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle of the transaction to roll back. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.


isc_rollback_transaction( ) Example

Syntax	Definition

The following call rolls back a transaction:

isc_rollback_transaction(status_vector, &trans);


isc_sqlcode( )

Syntax	Example	See Also

Description

isc_sqlcode() searches status_vector for a reported SQL error, and if it finds it, translates the InterBase error code number into an appropriate SQL error code. Typically, this call is used to populate a program variable (usually called SQLCODE for portability among SQL implementations) with an SQL error number for use in an SQL error-handling routine.

Return Value

If successful, isc_sqlcode() returns the first valid SQL error code decoded from the InterBase status vector.

If no valid SQL error code is found, isc_sqlcode() returns -999.


See Also

isc_interprete()

isc_print_sqlerror()

isc_print_status()

isc_sql_interprete()


isc_sqlcode( ) Syntax

Example	Definition

ISC_LONG isc_sqlcode (ISC_STATUS *status_vector);

Parameter	Description
status_vector	Pointer to the error status vector.


isc_sqlcode( ) Example

Syntax	Definition

The following code illustrates how isc_sqlcode() might be called in a DSQL application:

#include <ibase.h>

long SQLCODE;

ISC_STATUS status_vector[20];

. . .

if (status_vector[0]==1 && status_vector[1])

{

SQLCODE = isc_sqlcode(&status_vector);

isc_print_sqlerror(SQLCODE, &status_vector);

}


isc_sql_interprete( )

Syntax	Example	See Also

Description

Given an SQLCODE value less than zero, isc_sql_interprete() builds a corresponding SQL error message string, and stores it in a user-defined buffer. The size of the buffer, in bytes, must also be passed to this function.

To display an SQL error message corresponding to an SQLCODE value, use isc_print_sqlerror() instead of this call.

Return Value

None.


See Also

isc_interprete()

isc_print_sqlerror()

isc_print_status()

isc_sqlcode()


isc_sql_interprete( ) Syntax

Example	Definition

void isc_sql_interprete(
short SQLCODE,
char *buffer,
short buffer_length);

Parameter	Description
SQLCODE	Variable containing an SQLCODE value.
buffer	Application buffer into which to store an SQL error message.
buffer_length	Length, in bytes, of buffer.


isc_sql_interprete( ) Example

Syntax	Definition

The following code fragment illustrates a call to isc_sql_interprete():

#include <ibase.h>

long SQLCODE;

char err_buf[256];

. . .

if (status_vector[0] == 1 && status_vector[1])

{

SQLCODE = isc_sqlcode(status_vector);

isc_sql_interprete(SQLCODE, err_buf, sizeof(err_buff));

}


isc_start_multiple( )

Syntax	Example	See Also

Description

Call isc_start_multiple() if you:

Are using a language that does not support a variable number of arguments in a function call.

Do not know how many databases you want to attach to when coding the start transaction function.

isc_start_multiple() passes information about each target database to InterBase. That information is stored in the transaction parameter buffer (TPB), which is pointed to by the teb_vector. The TPB contains details regarding transaction parameters, such as access mode and lock resolution.

teb_vector is a byte vector of database handles and TPBs, and is equivalent to the list of database handles and TPBs included in the isc_start_transaction() function call.

Note:	C programmers should use isc_start_transaction() instead of isc_start_multiple() whenever possible because it does not require setting up transaction existence buffers.

Return Value

isc_start_multiple() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_commit_transaction()

isc_prepare_transaction()

isc_prepare_transaction2()

isc_rollback_transaction()

isc_start_transaction()


Transaction Parameter Buffer

The transaction parameter buffer (TPB) is a byte vector containing a one-byte version number followed by zero or more compile-time constants you can use to define the attributes of a transaction. Declare the TPB in a program as a char array, listing the appropriate parameters in a parameter list. The following table lists valid parameters that can be passed in the TPB:

Parameter	Description
isc_tpb_version3	InterBase version 3 transaction.
isc_tpb_consistency	Table-locking transaction model.
isc_tpb_concurrency	High throughput, high concurrency transaction with acceptable consistency. Use of this parameter takes full advantage of the InterBase multi-generational transaction model. (Default.)
isc_tpb_shared	Concurrent, shared access of a specified table among all transactions. Use in conjunction with isc_tpb_lock_read and isc_tpb_lock_write to establish the lock option. (Default.)
isc_tpb_protected	Concurrent, restricted access of a specified table. Use in conjunction with isc_tpb_lock_read and isc_tpb_lock_write to establish the lock option.
isc_tpb_exclusive	See isc_tpb_consistency.
isc_tpb_wait	Lock resolution that the transaction is to wait until locked resources are released before retrying an operation. (Default.)
isc_tpb_nowait	Lock resolution that the transaction is not to wait for locks to be released, but instead, a lock conflict error should be returned immediately.
isc_tpb_read	Access mode of read-only that allows a transaction only to select data from tables.
isc_tpb_write	Access mode of read-write that allows a transaction to select, insert, update, and delete table data. (Default.)
isc_tpb_lock_read	Read-only access of a specified table by the transaction. Use in conjunction with isc_tpb_shared, isc_tpb_protected, and isc_tpb_exclusive to establish the lock option.
isc_tpb_lock_write	Read-write access of a specified table by the transaction. Use in conjunction with isc_tpb_shared, isc_tpb_protected, and isc_tpb_exclusive to establish the lock option. (Default.)
isc_tpb_ignore_limbo	Treats limbo transactions as if they are active.
isc_tpb_read_committed	High throughput, high concurrency transaction that can read changes committed by other concurrent transactions. Use of this parameter takes full advantage of the InterBase multi-generational transaction model.
isc_tpb_rec_version	Enables an isc_tpb_read_committed transaction to read the most recently committed version of a record even if other, uncommitted versions are pending.
isc_tpb_no_rec_version	Enables an isc_tpb_read_committed transaction to read only the latest committed version of a record. If an uncommitted version of a record is pending and isc_tpb_wait is also specified, then the transaction waits for the pending record to be committed or rolled back before proceeding. Otherwise, a lock conflict error is reported at once.


isc_start_multiple( ) Syntax

Example	Definition

ISC_STATUS isc_start_multiple(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle,
short db_handle_count,
void *teb_vector_address);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle returned by this function. It must be NULL when the function is called, or an error is returned in status_vector.
db_handle_count	Number of database handles passed in this call via transaction existence buffers (TEBs).
teb_vector_address	Pointer to the TEB.


isc_start_multiple( ) Example

Syntax	Definition

The following program starts a multiple-database transaction:

#include <ibase.h>

typedef struct { /* Define the ISC_TEB structure. */

int	*dbb_ptr;

long	tpb_len;

char	*tpb_ptr;

} ISC_TEB;

ISC_TEB	 teb_vec[2]; /* Declare the TEB vector. */

ISC_STATUS isc_status[20]; /* Status vector. */

long *db0, *db1, /* Database handle. */

long *trans; /* Transaction handle. */

static char

isc_tpb_0[] = { /* Declare the first transaction parameter

buffer. */

isc_tpb_version3, 	/* InterBase version. */

isc_tpb_write,	/* Read-write access. */

isc_tpb_consistency, 	/* Serializable. */

isc_tpb_wait,	/* Wait on lock. */

isc_tpb_lock_write, 3, /* Reserving IDS for update. */

'I','D','S',

isc_tpb_protected},	/* Don't allow other transactions to

write to the table. */

isc_tpb_1[] = {	/* Declare the second transaction.*/

/* Parameter buffer. */

isc_tpb_version3, 	/* InterBase version. */

isc_tpb_write,	/* Read-write access. */

isc_tpb_consistency, 	/* Serializable. */

isc_tpb_wait,	/* Wait on lock. */

isc_tpb_lock_write, 3, /* Reserving table OZS for update. */

'O','Z','S',

isc_tpb_protected};	/* Don't allow other transactions to

write to the table. */

main()

{

db0 = db1 = 0;

trans = 0;

/* If you can't attach to test_0 database, attach to test_1. */

isc_attach_database(isc_status, 0, "test_0.gdb", &db0, 0,0);

if (isc_status[0] == 1 && isc_status[1])

isc_attach_database(isc_status, 0, "test_1.gdb", &db1, 0,0);

if (db0 && db1)

{	/* Assign database handles, tpb length, and

tbp handle to the teb vectors. */

teb_vec[0].dbb_ptr = &db0;

teb_vec[0].tpb_len = sizeof (isc_tpb_0);

teb_vec[0].tpb_ptr = isc_tpb_0;

teb_vec[1].dbb_ptr = &db1;

teb_vec[1].tpb_len = sizeof (isc_tpb_1);

teb_vec[1].tpb_ptr = isc_tpb_1;

if (isc_start_multiple(isc_status, &trans, 2, teb_vec))

isc_print_status(isc_status);

}

if (trans)

isc_commit_transaction(isc_status, &trans);

if (db0 && !trans)

isc_detach_database(isc_status, &db0);

if (db1 && !(trans && db0))

isc_detach_database(isc_status, &db1);

if (isc_status[0] == 1 && isc_status[1])

isc_print_status(isc_status);

}


isc_start_transaction( )

Syntax	Example	See Also

Description

isc_start_transaction() starts a new transaction against one or more databases specified as database handles.

Important:	If you have a variable number of databases to update, or are using a language that does not support a variable number of arguments in a function call, use isc_start_multiple() instead of isc_start_transaction().

A single transaction can access multiple databases. This function passes information about each database it accesses and the conditions of access for that database in a transaction parameter buffer (TPB). The TPB is a variably-sized vector of bytes declared and populated by the program. It contains information describing intended transaction behavior such as its access and lock modes.

isc_start_transaction() can start a transaction against up to 16 databases. You must pass a database handle and a TPB for each referenced database. If you want to use defaults for the transaction, set tpb_length to zero. In this case, tpb_vector is a NULL pointer.

Return Value

isc_start_transaction() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_commit_transaction()

isc_prepare_transaction()

isc_prepare_transaction2()

isc_rollback_transaction()

isc_start_multiple()


isc_start_transaction( ) Syntax

Example	Definition

ISC_STATUS isc_start_transaction(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle,
short db_handle_count,
isc_db_handle *db_handle,
unsigned short tpb_length,
char *tpb_address,
[isc_db_handle *db_handle,
unsigned short tpb_length,
char *tpb_address ...]);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle returned by this function. It must be NULL when the function is called, or an error is returned in status_vector.
db_handle_count	Number of database handles passed in this call.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database against which a transaction takes place. db_handle must not be NULL, or an error is returned in status_vector.
tpb_length	Length of the transaction parameter buffer (TPB).
tpb_address	Pointer to the TPB.


isc_start_transaction( ) Example

Syntax	Definition

The following program includes a call to the start transaction function:

#include <ibase.h>

long

isc_status[20], /* Status vector. */

*db, /* Database handle. */

*trans, /* Transaction handle. */

static char

isc_tpb_0[] = {

isc_tpb_version3, 	/* InterBase version. */

isc_tpb_write,	/* Read-write access. */

isc_tpb_consistency, 	/* Consistency-mode transaction. */

isc_tpb_wait,	/* Wait on lock. */

isc_tpb_lock_write, 3, /* Reserving IDS table for update. */

"I","D","S",

isc_tpb_protected};	/* Don't allow other transactions to

write against this table. */

main()

{

db = trans = 0;

isc_attach_database(isc_status, 0, "test.gdb", &db, 0,0);

if (db)

{

isc_start_transaction(

isc_status, &trans, 1, &db,

sizeof(isc_tpb_0), isc_tpb_0);

if (isc_status[0] == 1 && isc_status[1])

isc_print_status(isc_status);

}

if (trans)

isc_commit_transaction(isc_status, &trans);

if (db && !trans)

isc_detach_database(isc_status, &db);

if (status_vector[0] == 1 && status_vector[1])

isc_print_status(isc_status);

}


isc_transaction_info( )

Syntax	Example	See Also

Description

isc_transaction_info() returns information necessary for keeping track of transaction IDs. This call is used internally by isc_prepare_transaction(). You should not need to use it in your own applications.

You can explicitly retrieve information about the transaction ID by including the following constant in the item-list buffer, where the transaction items about which you want information are listed:

Item	Value
isc_info_tra_id	Determine the transaction ID
Note:	The size of the next value is two bytes.

isc_transaction_info() uses two buffers defined in the calling program: the item-list buffer, which lists transaction items about which you want information, and a result buffer, where the information requested is reported.

To define the item-list buffer, include the parameters item_list_buffer_length and item_list_buffer_address. The item-list buffer is a regular byte vector with no structure.

To define the result buffer, include the parameters result_buffer_length and result_buffer_address. These parameters specify the length and address of a buffer where the InterBase engine will place the return values from the function call.

The values returned to the result buffer are unaligned clusters of generic binary numbers. Furthermore, all numbers are represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers have the sign in the last byte. Convert the numbers to a data type native to your system before interpreting them.

In your call, include the item specifying the transaction ID, isc_info_tra_id. InterBase returns the transaction ID in the result buffer. In addition to the information InterBase returns in response to a request, InterBase can also return one or more status messages to the result buffer.

The function return value indicates only that InterBase accepted the request for information. It does not mean that it understood the request or that it supplied all of the requested information. Your application must interpret the contents of the result buffer for details about the transaction.

Return Value

isc_transaction_info() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_start_transaction()


isc_transaction_info( ) Syntax

Example	Definition

ISC_STATUS isc_transaction_info(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle,
short item_list_buffer_length,
char *item_list_buffer,
short result_buffer_length,
char *result_buffer);

Parameter	Description
status_vector	Pointer to the error status vector.
trans_handle	Pointer to a transaction handle of the transaction about which you want information. The handle must not be NULL when you make this call; it should have been set by InterBase as a result of a previous isc_start_transaction() call.
item_list_buffer_length	Number of bytes in the item-list buffer.
item_list_buffer	Pointer to the item-list buffer.
result_buffer_length	Number of bytes in the result buffer.
result_buffer	Pointer to the result buffer.


isc_transaction_info( ) Example

Syntax	Definition

The following code fragment gets information about a transaction:

static char	/* Declare item-list buffer. */

tra_items[] =

{isc_info_tra_id};

/* Declare result buffer. */

CHAR tra_info[32];

isc_transaction_info(status_vector,

&tr_handle,

sizeof (tra_items), /* Length of item-list buffer. */

&tra_items, /* Address of item-list buffer. */

sizeof (tra_info), /* Length of result buffer. */

&tra_info); /* Address of result buffer. */

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);


isc_vax_integer( )

Syntax	Example	See Also

Description

isc_vax_integer() reverses the byte order of an integer, specified in buffer, and returns the newly ordered value.

A typical use for this function is to convert integer values passed into a database parameter buffer to a format where the least significant byte must be first and the most significant byte last. In InterBase, integer values must be represented in input parameter buffers (e.g., the DPB) and are returned in result buffers ina generic format where the least significant byte is first, and the most significant byte last. isc_vax_integer() is used to convert integers to and from this format.

Return Value

isc_vax_integer() always returns a byte-reversed long integer value.


See Also

isc_attach_database()

isc_database_info()


isc_vax_integer( ) Syntax

Example	Definition

ISC_LONG isc_vax_integer(
char *buffer,
short length);

Parameter	Description
buffer	Pointer to the integer to convert.
length	Length, in bytes, of the integer to convert. Valid lengths are 1, 2, and 4 bytes.


isc_vax_integer( ) Example

Syntax	Definition

The following code fragment converts a 2-byte value, stored in a character buffer that is the result buffer returned by a function such as isc_database_info():

#include <ibase.h>

char *p;

. . .

for(p = res_buffer; *p != isc_info_end;)

{

/* Read item type of next cluster in the result buffer. */

item + *p++;

/* Read length of next value in result buffer, and convert. */

len = isc_vax_integer(p, 2);

p += len;

/* Now process the actual value, len bytes in size. */

. . .

}


isc_version( )

Syntax	Example	See Also

Description

isc_version() determines the database implementation and on-disk structure (ODS) version numbers for the database specified by db_handle. It passes this information in two separate calls to the callback function pointed to by function_name.

function_name should point to an application function that takes two arguments: a void pointer, user_arg, and a char pointer. Applications can pass any kind of parameter desired in user_arg.

isc_version() makes two calls to function_name. First it determines the database implementation number, builds a string containing the information, and calls function_name with user_arg, and a pointer to the string containing the implementation number in the following format:

<implementation>(<class>), version "<version>"

where

<implementation> is a text string, such as “InterBase/NT”.

<class> is a text string specifying the implementation class, such as “access method”.

<version> is a version identification string, such as “4.0”.

The callback function specified by function_name is free to do with this information what it pleases.

After the callback function returns control to isc_version(), isc_version() builds a new string containing the ODS major and minor version numbers, then calls function_name a second time with user_arg, and a pointer to the string containing the ODS version number in the following format:

on disk structure version <ods_major_num>.<ods_minor_num>

where

<ods_major_num> is the major ODS number. Databases with different major version numbers have different physical layouts on disk and are incompatible with one another. A database engine can only access databases with a particular ODS major number.

<ods_minor_num> is the minor ODS number. Differences in the minor ODS number, but not the major one indicate a non-structural change that still permits access by any database engine that recognizes the major version number.

Tip:	If a NULL pointer is passed for function_name, isc_version() sets function_name to point to the C printf() function.

Return Value

If successful, isc_version() returns 0. Otherwise it returns a non-zero value.


See Also

isc_database_info()


isc_version( ) Syntax

Example	Definition

int isc_version(
isc_db_handle *db_handle,
isc_callback function_name,
void *user_arg);

Parameter	Description
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). db_handle must not be NULL, or an error is returned in status_vector.
function_name	Pointer to a function to call with the relevant information. If a NULL pointer is passed in C programs, printf() is called.
user_arg	An application-specified parameter to pass as the first of two arguments to function_name.


isc_version( ) Example

Syntax	Definition

The following code fragment calls isc_version() with a NULL callback function:

#include <ibase.h>

. . .

int ret;

. . .

ret = isc_version(db1, NULL, "\t%s\n";


isc_wait_for_event( )

Syntax	Example	See Also

Description

isc_wait_for_event() is used to wait synchronously until one of a specified group of events is posted. Control is not returned to the calling application until one of the specified events occurs.

Events to wait on are specified in event_buffer, which should have been initially allocated and filled in by a previous call to isc_event_block().

When one of these events is posted, isc_wait_for_event() fills in result_buffer with data that exactly corresponds to that in the initial buffer, except that the event counts will be the updated ones. Control then returns from isc_wait_for_event() to the calling application. The application should then call isc_event_counts() to determine which event was posted.

Note:	To request asynchronous notification of event postings, use isc_que_events() instead of isc_wait_for_event(). You must use asynchronous notifications in Microsoft Windows applications, or wherever a process must not stop processing.

Return Value

isc_wait_for_event() returns the second element of the status vector. Zero indicates success. A nonzero value indicates an error. For InterBase errors, the first element of the status vector is set to 1, and the second element is set to an InterBase error code. To check for an InterBase error, examine the first two elements of the status vector directly.


See Also

isc_event_block()

isc_que_events()


isc_wait_for_event( ) Syntax

Example	Definition

ISC_STATUS isc_wait_for_event(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
short length,
char *event_buffer,
char *result_buffer);

Parameter	Description
status_vector	Pointer to the error status vector.
db_handle	Pointer to a database handle set by a previous call to isc_attach_database(). The handle identifies the database against which the events are expected to be posted. db_handle must not be NULL, or an error is returned in status_vector.
length	Length of the event parameter buffers, returned by the isc_event_block() call which allocated them.
event_buffer	Pointer to the event parameter buffer that specifies the current counts of the events to be waited on. This buffer should have been initially allocated and filled in by a call to isc_event_block().
result_buffer	Pointer to the event parameter buffer to be filled in with updated event counts as a result of this function call. This buffer should have been initially allocated by a call to isc_event_block().


isc_wait_for_event( ) Example

Syntax	Definition

The following program fragment illustrates a call to isc_wait_for_event() to wait for a posting of any of the events named “DEC”, “HP”, or “SUN”.

#include <ibase.h>

#define number_of_stocks 3;

char *event_buffer, *result_buffer;

short length;

length = (short)isc_event_block(

&event_buffer,

&result_buffer,

number_of_stocks,

"DEC", "HP", "SUN");

isc_wait_for_event(

status_vector,

&database_handle,

length, /* Returned from isc_event_block(). */

event_buffer,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

}

/* Call isc_event_counts() to compare event counts in the buffers and thus determine which event(s) were posted. */


Array Descriptor

An array descriptor is a structure defined as follows:

typedef struct {

unsigned char array_desc_dtype; /* Data type. */

char array_desc_scale; /* Scale for numeric data types. */

unsigned short array_desc_length;

/* Length in bytes of each array column entry. */

char array_desc_field_name[32]; /* Column name. */

char array_desc_relation_name[32]; /* Table name. */

short array_desc_dimensions; /* Number of array dimensions. */

short array_desc_flags;

/* Specifies whether array is to be accessed in row-major or column-major order. */

ISC_ARRAY_BOUND array_desc_bounds[16];

/* Lower and upper bounds for each dimension. */

} ISC_ARRAY_DESC;

ISC_ARRAY_BOUND is defined as:

typedef struct {

short array_bound_lower; /* Lower bound. */

short array_bound_upper; /* Upper bound. */

} ISC_ARRAY_BOUND;

An array descriptor contains one ISC_ARRAY_BOUND structure for each dimension.


Descriptor Fields

array_desc_dtype must be expressed as one of the data types in the following table, and array_desc_field_name and array_desc_relation_name must be null-terminated:

array_desc_dtype	Corresponding InterBase Data Type
blr_text	CHAR
blr_text2	CHAR
blr_short	SMALLINT
blr_long	INTEGER
blr_quad	ISC_QUAD structure
blr_float	FLOAT
blr_double	DOUBLE PRECISION
blr_date	DATE
blr_varying	VARCHAR
blr_varying2	VARCHAR
blr_blob_id	ISC_QUAD structure
blr_cstring	NULL-terminated string
blr_cstring2	NULL-terminated string


User Validation

The length of the following parameters is the number of bytes in the string. The value to pass is a string containing information appropriate to the parameter.

Parameter	Description
isc_dpb_user_name	User name.
isc_dpb_password	Password.
isc_dpb_password_enc	Encrypted password.
isc_dpb_sys_user_name	System database administrator’s user name.
isc_dpb_sys_user_name_enc	Encrypted system DBA user’s name.
isc_dpb_license	Check an explicit list of license options.
isc_dpb_encrypt_key	Database encryption key.


Environmental Control

The length of the following parameters is one. The value to pass is the number of buffers to allocate for isc_dpb_num_buffers and zero or one for isc_dpb_dbkey_scope.

Parameter	Description
isc_dpb_num_buffers	Number of cache buffers.
isc_dpb_dbkey_scope	dbkey context scope.


System Management

The length of the following parameters is one and the value is zero or one.

Parameter	Description
isc_dpb_force_write	Force writes to the database to be done asynchronously or synchronously.
isc_dpb_no_reserve	Specify whether or not to reserve a small amount of space on each database page for holding backup versions of records when modifications are made.
isc_dpb_damaged	Specify whether or not the database should be marked as damaged.
isc_dpb_verify	Perform consistency checking of internal structures.


Shadow Control

The length of the following parameters is one and the value is zero (both of which are ignored).

Parameter	Description
isc_dpb_activate_shadow	Activate the database shadow, an optional, duplicate, in-sync copy of the database.
isc_dpb_delete_shadow	Delete the database shadow.


Replay Logging System Control

Parameter	Description
isc_dpb_begin_log	Activate a replay logging system to keep track of all database calls.
isc_dpb_quit_log	Deactivate the replay logging system.


Character Set and Message File Specification

The length of the following parameters is the number of bytes in the string. The value to pass is a string containing information appropriate to the parameter.

Parameter	Description
isc_dpb_lc_messages	Language-specific message file.
isc_dpb_lc_ctype	Character set to be utilized.


BLOB Descriptor

The following table lists the fields in the desc structure:

Parameter	Type	Description
blob_desc_subtype	short	Subtype of the BLOB filter.
blob_desc_charset	short	Character set being used.
blob_desc_segment_size	short	Size of the segment.
blob_desc_field_name [32]	char	Array containing the name of the BLOB field.
blob_desc_relation_name [32]	char	Array containing the name of the table in which the BLOB is stored.


Status Messages

Each status message is one unsigned byte in length:

Item	Description
isc_info_end	End of the messages.
isc_info_truncated	Result buffer is too small to hold any more requested information.
isc_info_error	Requested information is unavailable. Check the status vector for an error code and message.


Statement Type

The following table lists the statement types that can be returned:

Type	Numeric Value
isc_info_sql_stmt_select	1
isc_info_sql_stmt_insert	2
isc_info_sql_stmt_update	3
isc_info_sql_stmt_delete	4
isc_info_sql_stmt_ddl	5
isc_info_sql_stmt_get_segment	6
isc_info_sql_stmt_put_segment	7
isc_info_sql_stmt_exec_procedure	8
isc_info_sql_stmt_start_trans	9
isc_info_sql_stmt_commit	10
isc_info_sql_stmt_rollback	11
isc_info_sql_stmt_select_for_upd	12


Introduction

The InterBase API Guide is a task-oriented explanation of how to write, preprocess, compile, and link database applications using the InterBase Applications Programming Interface (API), and a host programming language, either C or C++.


Who Should Use This Guide

The InterBase API Guide is intended for knowledgeable database applications programmers. It assumes full knowledge of:

SQL and dynamic SQL (DSQL).

Relational database programming.

C programming.

For an introduction to InterBase and SQL, see Getting Started.


Topics Covered in This Guide

The API Guide is divided into two parts:

A task-oriented user's guide that explains how to use API function calls to perform related database tasks, such as attaching to and detaching from a database.

An API function call reference that describes the purpose of each function, its syntax, its parameters, requirements, restrictions, and return values, as well as examples of use and cross-references to related functions.

The following table lists the task-oriented sections in the API Guide, and provides a brief description of them:

Section	Description
Introduction	Introduces the book and describes its intended audience.
Application Requirements	Describes support structures and elements common to programming with API calls.
Windows Programming with the API	Describes special requirements for programming InterBase applications in Microsoft Windows 3.1.
Working with Databases	Describes how to attach to and detach from databases, and how to request information about attachments.
Working with Transactions	Explains how to start transactions in different modes, and how to commit them or roll them back.
Working with Dynamic SQL	Describes how to process DSQL data definition and data manipulation statements using API calls.
Working with BLOB Data	Describes how to select, insert, update, and delete BLOB data in applications.
Working with Array Data	Describes how to select, insert, update, and delete array data in applications.
Working with Conversions	Describes how to select, insert, update, and delete DATE data in applications, and how to reverse the byte order of numbers with isc_vax_integer().
Handling Error Conditions	Describes how to trap and handle database errors in applications.
Working with Events	Explains how triggers interact with applications. Describes how to register interest in events, wait on them, and respond to them in applications.
API Function Reference	Describes the syntax of each function call in detail.


Sample Database and Applications

A sample database and sample application source code can be found in the InterBase examples subdirectory. The API Guide makes use of the sample database and source code for its examples wherever possible.


Application Requirements

This section of the Help file summarizes programming requirements for using categories of API functions in database applications

All API applications must use certain API functions and support structures. For example, all applications connect to at least one database, and run at least one transaction. All applications, therefore, must declare and initialize database handles and transaction handles. They may also need to declare and populate database parameter buffers (DPBs) and transaction parameter buffers (TPBs).

Some API applications may use specific API functions, such as the functions that permit an application to process dynamic SQL (DSQL) statements. These applications may have additional requirements.


Requirements For All Applications

The following sections outline these requirements for all API applications:

Including ibase.h

Database requirements

Transaction requirements


Including Ibase.h

The InterBase subdirectory, include, contains the ibase.h header file, which should be included in all source code modules for API applications. ibase.h contains API function prototypes. It also contains structure typedefs, parameter definitions, and macros required by various API functions.

To include ibase.h in a source code module, insert the following #include near the start of the source code:

#include <ibase.h>

If ibase.h is not on your compiler's search path, you may need to provide a full path specification and enclose the file name in quotation marks.

Failure to include ibase.h can prevent the successful compilation and linking of an application.


Database Requirements

All applications that work with databases must provide one database handle for each database to be accessed. A database handle is a long pointer that is used in API functions to attach to a database and to reference it in subsequent API calls. The InterBase header file, ibase.h, contains a #define useful for declaring database handles.

When establishing a connection to a database, optional database attachment characteristics, such as a user name and password combination, can be passed to the attachment through a database parameter buffer (DPB). Usually, one DPB is set up for each database attachment, although database attachments can also share a DPB.

Declaring Database Handles

A database handle must be declared and initialized to zero before use. The following code illustrates how to declare and initialize a database handle:

#include <ibase.h>

. . .

/* Declare a database handle. */

isc_db_handle db1;

. . .

/* Initialize the handle. */

db1 = 0L;

Setting Up a DPB

A DPB is a byte array describing optional database attachment characteristics. A DPB must be set up and populated before attaching to a database. Parameters that can be passed to the DPB are defined in ibase.h.


Transaction Requirements

All applications must provide one transaction handle for each transaction to be accessed. A transaction handle is a long pointer that is used in API functions to start a transaction and to reference it in subsequent API calls. The InterBase header file, ibase.h, contains a #define useful for declaring transaction handles.

When starting a transaction, optional transaction characteristics, such as access method and isolation level, can be passed to the startup call through a transaction parameter buffer (TPB). Usually, one TPB is set up for each transaction, although transactions with the same operating characteristics can also share a TPB.

Declaring Transaction Handles

A transaction handle must be declared and initialized to zero before use. The following code illustrates how to declare and initialize a transaction handle:

#include "ibase.h"

. . .

/* Declare a transaction handle. */

isc_tr_handle tr1;

. . .

/* Initialize the handle. */

tr1 = 0L;

Setting Up a TPB

A TPB is a byte array containing parameters that describe optional transaction characteristics. In these cases, the TPB must be set up and populated before starting a transaction. Parameters that can be passed to the TPB are defined in ibase.h.


Additional Requirements

The following sections outline possible additional requirements for API applications developed on certain system platforms, such as Microsoft Windows, and for general classes of API functions, such as those that process DSQL statements.


Microsoft Windows Requirements

InterBase client applications for Microsoft Windows have programming requirements specific to that environment and the C/C++ compilers available there.

The InterBase header file, ibase.h, provides prototypes of all API functions. For Windows applications, these prototypes make use of the following declarations:

#define ISC_FAR __far

#define ISC_EXPORT ISC_FAR __cdecl __loadds __export

For example, the isc_attach_database() prototype in ibase.h is:

ISC_STATUS ISC_EXPORT isc_attach_database(
ISC_STATUS ISC_FAR *,
short,
char ISC_FAR,
isc_db_handle ISC FAR *,
short,
char ISC_FAR *);

When Windows client applications make calls and cast C data types, they should make explicit use of the ISC_FAR declaration.

Note:	The ISC_EXPORT keyword is omitted from the API function reference because on all non-Windows platforms it is undefined.


DSQL Requirements

API applications that build or prompt for DSQL queries at run time require careful declaration, initialization, and population of extended SQL descriptor area (XSQLDA) structures for data transfer to and from the database. In addition, many API functions, such as isc_dsql_allocate_statement() and isc_dsql_describe(), also make use of statement handles for DSQL processing.

ibase.h provides typedefs for the XSQLDA structure, and its underlying structure, the XSQLVAR. It also provides a #define for the statement handle, a macro for allocating the appropriate amount of space for an instance of an XSQLDA in an application, and #defines for DSQL information parameters passed to isc_dsql_sql_info().

The following code illustrates how to declare an XSQLDA structure for use in an application, and how to declare a statement handle:

#include <ibase.h>

. . .

XSQLDA *insqlda;

isc_stmt_handle sql_stmt;

. . .


BLOB Requirements

To work with BLOB data that must be filtered, an API application must set up a BLOB parameter buffer (BPB) for each BLOB. A BPB is a variable-length byte vector declared in an application to store control information that dictates BLOB access. The BPB can contain a number of constants, defined in ibase.h, that describe the BLOB and the BLOB subtypes that specify BLOB filtering.

Important:	BLOB filtering is not available on NetWare servers.

Applications that work with BLOB data in an international environment must also declare and populate a BLOB descriptor that contains character set information for the BLOB. The BLOB descriptor structure is defined in ibase.h. To declare a BLOB descriptor, an application must provide code like this:

#include <ibase.h>

. . .

ISC_BLOB_DESC to_desc;

Except on NetWare servers, where they are not supported, BLOB filters enable a BLOB to be translated from one format to another, such as from a compressed state to an decompressed state or vice versa. If BLOB filters are desired, separate filter functions must be created and defined to the database to ensure their use when BLOB data is accessed.

Finally, to access BLOB data, applications must make extensive use of API DSQL functions.


Array Requirements

API functions that handle array processing require the use of an array descriptor structure and array IDs, defined in ibase.h. In addition, applications accessing arrays must make extensive use of API DSQL functions.

The following code illustrates how to declare an array descriptor and array ID variable, and how to initialize an array ID to zero before use:

#include <ibase.h>

. . .

ISC_ARRAY_DESC desc;

ISC_QUAD array_id;

. . .

array_id = 0L;

. . .


Event Requirements

InterBase events are messages passed from a trigger or stored procedure to an application to announce the occurrence of specified conditions or actions, usually database changes such as insertions, modifications, or deletions of records.

Before an application can respond to an event, it must register interest in an event. To register interest in an event, the application must establish and populate two event parameter buffers (EPBs), one for holding the initial occurrence count values for each event of interest, and another for holding the changed occurrence count values. These buffers are passed as parameters to several API event functions, and are used to determine which events have occurred.

In C, each EPB is declared as a char pointer, as follows:

char *event_buffer, *result_buffer;

Once the buffers are declared, isc_event_block() is called to allocate space for them, and to populate them with starting values.


Error-handling Requirements

Most API functions return status information in an error status vector, an array of 20 longs. To handle InterBase error conditions, should they arise, applications should declare a status vector as follows:

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

ISC_STATUS is a #define in ibase.h provided for programming convenience and platform independence.

ibase.h also contains #defines for all InterBase error conditions. Applications can use API error-handling functions to construct error messages from the status vector that are based on these error conditions, or can examine the status vector directly for particular error conditions using the #defines in place of error numbers. Using #defines in this manner makes source code easier to understand and maintain.


Compiling and Linking

On most development platforms, an API application is compiled like any standard C or C++ application. For more information about a particular compiler, consult the compiler's documentation.

On most platforms, InterBase supports dynamic linking of its library at run time. However, two compilers also require explicit links to InterBase-supplied libraries: Microsoft Visual C requires GDS32_MS.LIB and Borland C requires GDS32.LIB.

One other compilation reminder: You must use 4-byte alignment when compiling on 32 bit platforms.


Windows Programming With the InterBase API

This section of the Help file provides information specific to programming InterBase applications on a Microsoft Windows client with C/C++ and Windows 3.1. It assumes familiarity with Borland C/C++ or Microsoft C/C++, InterBase, and the InterBase documentation set, particularly the Language Reference.

Most of the information here also applies to programming client applications on Windows NT, except as noted.


Basic Procedure For Application Development

The basic steps in application development on the InterBase Windows Client are:

Determine the server and client platforms on which the application will run. InterBase servers include NetWare 4.0 and 3.1x, Windows 95, Windows NT, and UNIX. Clients include Windows 3.x, Windows 95, Windows NT, and UNIX.

Code the application in C or C++.

Compile and link the application.

Test and debug the application.

Deploy the application on the production client platform, if different from the development platforms.


Supported PC Development Environments

The InterBase Windows Client enables developers using Borland C++ 3.1 (and above) or Microsoft C++ 7.0 (and above) to develop InterBase SQL client applications that run under Windows and connect to remote InterBase servers using TCP/IP.

SPX/IPX is also supported, but only from 16-bit clients, and only in the NetWare version of InterBase.


User Name and Password Requirements

When an InterBase Windows Client application is compiled, linked, and run, the client must always send a valid user name and password combination to the InterBase server. The server checks the user name and password against the user name and password combinations stored in its security database. If a match is found, the client can attach to InterBase databases on the server. If a match is not found, attachment is denied.

For a successful attachment to occur, the following steps must be taken:

1.	A database administrator must use the Server Manager's security facility to add a client's user name and password to the server's security database.

2.	The Windows client must send a valid user name and password combination to the server. Password is case-sensitive.


Specifying User Name and Password Through the API

A client application must specify a user name and password when it attaches to a database. Failure to provide a valid user name and password combination results in an error. Use the following methods to provide user names and passwords:

Create a database parameter block (DPB) with isc_dpb_user_name and isc_dpb_password, and pass the parameter block using isc_attach_database().

Add isc_dpb_user_name and isc_dpb_password parameters to an existing DPB with isc_expand_dpb.


Using DOS Environment Variables

Windows client applications can use three DOS environment variables to establish program parameters. These variables must be set from DOS before starting Windows. Setting these variables within a DOS window after Windows has been started does not affect any Windows programs, but affects DOS applications in that window.

The following table summarizes these variables, and what they are used for:

Variable	Purpose	Example
ISC_DATABASE	Specifies a default server and database directory to use on the remote server.	SET ISC_DATABASE = ingold:/usr/interbase/examples
ISC_USER	Specifies a user name for the PC client application.	SET ISC_USER = HERMES
ISC_PASSWORD	Specifies a case-sensitive password for the PC client application.	SET_PASSWORD = Ichneumon
The ISC_USER and ISC_PASSWORD environment variables are used together to establish a valid user name and password combination to pass to the remote InterBase database server.


Setting a Default Database Directory

To connect automatically to a default database directory on a remote server, use the DOS SET command to create an environment variable, ISC_DATABASE. ISC_DATABASE should be set to the full path specification for the desired database directory, including host and path names. For example, to set the default database directory to ingold:/usr/interbase/example, the SET command is:

SET ISC_DATABASE=ingold:/usr/interbase/example

Note:	Host name specification is specific to the server's operating system and network protocol. The host syntax in the previous example is for a generic Unix server. For other servers and operating systems, see that system's reference manuals.


Setting a User Name and Password

To set up a default user name and password for use on a PC client, use the DOS SET command to create two new environment variables, ISC_USER, and ISC_PASSWORD. For example, to set a default user name to HERMES, the SET command is:

SET ISC_USER=HERMES

To set the default password to "Ichneumon", the SET command is:

SET ISC_PASSWORD=Ichneumon

Even if ISC_USER and ISC_PASSWORD are set, a different user name and password may be specified in a DPB used as an argument to isc_attach_database(). A user name or password specified in a database parameter block overrides DOS environment variables.

Important:	Do not use the ISC_PASSWORD environment variable when security is a concern. Anyone with a knowledge of DOS and access to a Windows client where an ISC_PASSWORD environment variable is defined can easily view the password.


Data Types

InterBase supports a wide variety of data types for application development. These data types are defined in a typedef to be platform-independent. The InterBase client libraries are also compiled with packed data structures to be compatible with Windows.

For more information about InterBase data types, see the Language Reference.


Pointers

Any pointer passed to or from the InterBase Windows Client library is a far pointer. All pointers passed between the InterBase DLL and the client application (both code and data pointers) must be far 32-bit pointers.


Calling Conventions

On Windows all InterBase API functions use C calling conventions. CDECL should be used for all callback functions in the client library, such as event handling.

On NT all InterBase API functions use __stdcall calling conventions.


Building Applications

InterBase Windows Client applications can use the following compilers:

Borland C/C++ 3.1 and above.

Microsoft C/C++ 7.0 and above.


Using Microsoft

Use packed data structures by specifying the -Zp switch when compiling applications with Microsoft C.

On 16 bit platforms use -Zp1

On 32 bit platforms use -Zp4


Using Borland

When linking an InterBase application with Borland C command-line tools, use either of the following options under Windows 3.1:

Both the /c option (case-sensitive link) and /C option (case-sensitive exports and imports, 16-bit only)

Neither /c nor /C options.

On NT, you must use the /c option (case-sensitive link). There is no /C option.

Note:	There are equivalent general linker options within the Borland Integrated Development Environment (IDE). The default in the IDE is case-sensitive link (/c option) alone, which will cause unresolved linker errors for all of the InterBase entry points.

Project Options

Choose the following options under the specified topic in the IDE Project Options dialog box. The corresponding command-line option is also listed.

Directories

Include directory: C:\INTERBAS\INCLUDE

Library directory: C:\INTERBAS\LIB

This assumes that InterBase was installed in the default directory, C:\INTERBAS.

Compiler

Source language compliance: Borland extensions

32-bit Compiler

Data alignment: Long Word (-a4 option)

16-bit Compiler

Memory model: Large (-ml option). Memory model does not apply to NT.

Data alignment: Byte (-a1 option).

Linker

Choose either:

Case-sensitive link ON (/c option). This is the only choice for NT.

Case-sensitive exports and imports ON (/C option).

Or

Case-sensitive link OFF.

Case-sensitive exports and imports OFF.


Include Files

Applications must include ibase.h header file to pick up the InterBase type definitions and function prototypes.


Linking

Applications must link with the InterBase Windows Client import library, GDS.LIB.

On NT, applications must link with GDS32.LIB for Borland compilers, or GDS32_MS.LIB for Microsoft.


Module Definition File

For 16-bit applications only: The STACKSIZE parameter should be set to at least 10 kilobytes (10240 bytes), but 16 kilobytes (16384) is recommended. A sample .DEF file is included in the EXAMPLES subdirectory of the InterBase installation directory.


Using Dynamic Link Libraries (DLLs)

InterBase applications use the GDS.DLL dynamic link library, which, in turn, loads the appropriate network DLLs. These DLLs are unloaded automatically when the last calling application terminates.

In a 16-bit Windows environment, if the calling application exits abnormally (for example, from a protection fault), it is possible that DLLs aren't properly unloaded from memory. If this occurs, exit and restart Windows to free the resources.

Windows 95 and Windows NT automatically unload the DLLs whenever an application exits (normally or abnormally), so restarts are never necessary in those environments.


Example Programs

Example programs demonstrating how to use the InterBase API are included in the EXAMPLES subdirectory of the InterBase installation directory. There is also a sample .DEF file and a make file for Borland C. To build the example applications, type:

MAKE -F EXAMPLE.MAK

in the EXAMPLES subdirectory.

On NT, there are two make files, MAKEFILE.BOR for the Borland compiler and linker, and MAKEFILE.MSE for the Microsoft compiler and linker. In both files, you must modify the IBASE environment variable to point to an absolute path.

In the .BOR make file, the BC4DIR variable must be modified to point to the absolute path where the Borland compiler and linker can be found.

In the .MSE make file, the MSTDIR variable must be modified to point to the absolute path where the Microsoft compiler and linker can be found.

To build the example applications on NT, use the following command:

MAKE -B -f MAKEFILE.xxx

where xxx is either BOR or MSE as appropriate.


Working With Databases

This section of the Help file describes how to set up a database parameter buffer (DPB) that specifies database attachment parameters, how to set up and initialize database handles, and how to use the five API functions that control database access. It also explains how to set up item request and return buffers prior to retrieving information about an attached database.

The following table summarizes the API functions for working with databases. Functions are listed in the order they typically appear in an application.

Call	Purpose
isc_expand_dpb()	Specifies additional parameters for database access, such as user names and passwords elicited from a user at run time. Uses a previously declared and populated DPB.
isc_attach_database()	Connects to a database and establishes initial parameters for database access, such as number of cache buffers to use. Uses a previously declared and populated DPB.
isc_database_info()	Retrieves requested information about an attached database, such as the version of the on-disk structure (ODS) that it uses.
isc_detach_database()	Disconnects from an attached database and frees system resources allocated to that attachment.
isc_drop_database()	Deletes a database and any support files, such as shadow files.


Connecting to Databases

Connecting to one or more databases is a four-step process:

1.	Creating and initializing a database handle for each database to be attached.

2.	Creating and populating a DPB for each database to be attached.

3.	Optionally calling isc_expand_dpb() prior to actual attachment to add more database parameters to a previously created and populated DPB.

4.	Calling isc_attach_database() for each database to which to connect.


Creating Database Handles

Every database that is accessed in an application must be associated with its own database handle, a pointer to a FILE structure that is used by all API database functions. The ibase.h header file contains the following C typedef declaration for database handles:

typedef void ISC_FAR *isc_db_handle;

To use this typedef for declaring database handles in an application, include ibase.h in each source file module:

#include <ibase.h>

Declaring Database Handles

To establish database handles for use, declare a variable of type isc_db_handle for each database that will be accessed at the same time. The following code declares two handles:

#include <ibase.h>

. . .

isc_db_handle db1;

iac_db_handle db2;

Once a database is no longer attached, its handle can be assigned to a different database in a subsequent attachment. If an application accesses several databases, but only accesses a subset of databases at the same time, it is only necessary to declare as many handles as there will be simultaneous database accesses. For example, if an application accesses a total of three databases, but only attaches to two of them at a time, only two database handles need be declared.

Initializing Database Handles

Before a database handle can be used to attach to a database, it must be set to zero. The following code illustrates how two database handles are set to zero:

#include <ibase.h>

. . .

isc_db_handle db1;

isc_db_handle db2;

. . .

/* Set database handles to zero before attaching to a database. */

db1 = 0L;

db2 = 0L;

Once a database handle is initialized to zero, it can be used in a call to isc_attach_database() to establish a database connection. If a nonzero database handle is passed to isc_attach_database(), the connection fails and an error code is returned.


Creating and Populating a DPB

Database attachments can be optionally tailored in many ways by creating a database parameter buffer (DPB), populating it with desired database characteristics, and passing the address of the DPB to isc_attach_database().

For example, the DPB can contain a user name and password for attaching to a database on a remote server, and it might also contain a parameter that activates a database shadow file.

Usually a separate DPB is created for each database attachment, but if different attachments use the same set of parameters, they can share a DPB. If a DPB is not created or is not passed to isc_attach_database(), the database attachment uses a default set of parameters.

A DPB is a char array variable, specifically declared in an application, that consists of the following parts:

1.	A byte specifying the version of the parameter buffer, always the compile-time constant, isc_dpb_version1.

2.	A contiguous series of one or more clusters of bytes, each describing a single parameter.

Each cluster consists of the following parts:

1.	A one-byte parameter type. There are compile-time constants defined for all the parameter types (e.g., isc_dpb_num_buffers).

2.	A one-byte number specifying the number of bytes that follow in the remainder of the cluster.

3.	A variable number of bytes, whose interpretation (e.g., as a number or as a string of characters) depends on the parameter type.

For example, the following code creates a DPB with a single parameter that sets the number of cache buffers to use when connecting to a database:

char dpb_buffer[256], *dpb, *p;

short dpb_length;

/* Construct the database parameter buffer. */

dpb = dpb_buffer;

*dpb++ = isc_dpb_version1;

*dpb++ = isc_num_buffers;

*dpb++ = 1;

*dpb++ = 90;

dpb_length = dpb - dpb_buffer;

Important:	All numbers in the database parameter buffer must be represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers should have the sign in the last byte. The API function isc_vax_integer() can be used to reverse the byte order of a number.

The following table lists DPB items by purpose:

User Validation	Item
User name.	isc_dpb_user_name
Password.	isc_dpb_password
Encrypted password.	isc_dpb_password_enc
System database administrator's user name.	isc_dpb_sys_user_name
Authorization key for a software license.	isc_dpb_license
Database encryption key.	isc_dpb_encrypt_key
Environmental Control	Item
Number of cache buffers.	isc_dpb_num_buffers
dbkey context scope.	isc_dpb_dbkey_scope
System Management	Item
Force writes to the database to be done asynchronously or synchronously.	isc_dpb_force_write
Specify whether or not to reserve a small amount of space on each database page for holding backup versions of records when modifications are made.	isc_dpb_no_reserve
Specify whether or not the database should be marked as damaged.	isc_dpb_damaged
Perform consistency checking of internal structures.	isc_dpb_verify
Shadow Control	Item
Activate the database shadow, an optional, duplicate, in-sync copy of the database.	isc_dpb_activate_shadow
Delete the database shadow.	isc_dpb_delete_shadow
Replay Logging System Control	Item
Activate a replay logging system to keep track of all database calls.	isc_dpb_begin_log
Deactivate the replay logging system.	isc_dpb_quit_log
Character Set and Message File Specification	Item
Language-specific message file.	isc_dpb_lc_messages
Character set to be utilized.	isc_dpb_lc_ctype
The following table lists DPB parameters in alphabetical order. For each parameter, it lists its purpose, the length, in bytes, of any values passed with the parameter, and the value to pass.

Parameter	Purpose	Length	Value
isc_dpb_activate_shadow	Directive to activate the database shadow, which is an optional, duplicate, in-sync copy of the database.	1 (Ignored)	0 (Ignored)
isc_dpb_damaged	Number signifying whether or not the database should be marked as damaged. 1 = mark as damaged; 0 = do not mark as damaged.	1	0 or 1
isc_dpb_dbkey_scope	Scope of dbkey context. 0 limits scope to the current transaction, 1 extends scope to the database session.	1	0 or 1
isc_dpb_delete_shadow	Directive to delete a database shadow that is no longer needed.	1(Ignored)	0 (Ignored)
isc_dpb_encrypt_key	String encryption key, up to 255 characters.	Number of bytes in string	String containing key
isc_dpb_force_write	Specifies whether database writes are synchronous or asynchronous. 0 = asynchronous; 1 = synchronous.	1	0 or 1
isc_dpb_lc_ctype	String specifying the character set to be utilized.	Number of bytes in string	String containing character set name
isc_dpb_lc_messages	String specifying a language-specific message file.	Number of bytes in string	String containing message file name
isc_dpb_license	String authorization key for a software license.	Number of bytes in string	String containing key
isc_dpb_no_reserve	Specifies whether or not a small amount of space on each database page is reserved for holding backup versions of records when modifications are made. Keeping backup versions on the same page as the primary record optimizes update activity.	0 (default) = reserve space.	1= do not reserve space.10 or 1
isc_dpb_num_buffers	Number of database cache buffers to allocate for use with the database. Default: 75.	1	Number of buffers to allocate
isc_dpb_password	String password, up to 255 characters.	Number of bytes in string	String containing password
isc_dpb_password_enc	String encrypted password, up to 255 characters.	Number of bytes in string	String containing password
isc_dpb_sys_user_name	String system DBA name, up to 255 characters.	Number of bytes in string	String containing SYSDBA name
isc_dpb_user_name	String user name, up to 255 characters.	Number of bytes in string	String containing user name
Note:	Some parameters, such as isc_dpb_delete_shadow, are directives that do not require additional parameters. Even so, you must still provide length and value bytes for these parameters. Set length to 1 and value to 0. InterBase ignores these parameter values, but they are required to maintain the format of the DPB.


Adding Parameters to a DPB

Sometimes it is useful to add parameters to an existing DPB at run time. For example, when an application runs, it might determine a user's name and password and supply those values dynamically. The isc_expand_dpb() function can be used to pass the following additional parameters to a previously created and populated DPB at run time:

Parameter	Purpose
isc_dpb_user_name	String user name, up to 255 characters.
isc_dpb_password	String password, up to 255 characters.
isc_dpb_lc_messages	String specifying a language-specific message file.
isc_dpb_lc_ctype	String specifying the character set to be utilized.
Important:	If you expect to add any of these parameters at run time, then create a larger than necessary DPB before calling isc_expand_dpb(), so that this function does not need to reallocate DPB storage space at run time. isc_expand_dbp() can reallocate space, but that space is not automatically freed when the database is detached.

isc_expand_dpb() requires the following parameters:

Parameter	Type	Description
dpb	char **	Pointer to a DPB.
dpb_size	unsigned short *	Pointer to the current size, in bytes, of the DPB.
. . .	char *	Pointers to item type and items to add to the DPB.
The third parameter in the table, . . ., indicates a variable number of replaceable parameters, each with different names, but each a character pointer.

The following code demonstrates how isc_expand_dpb() is called to add a user name and password to the DPB after they are elicited from a user at run time:

char dpb_buffer[256], *dpb, *p;

char uname[256], upass[256];

short dpb_length;

/* Construct a database parameter buffer. */

dpb = dpb_buffer;

*dpb++ = isc_dpb_version1;

*dpb++ = isc_num_buffers;

*dpb++ = 1;

*dpb++ = 90;

dpb_length = dpb - dpb_buffer;

/* Now ask user for name and password. */

prompt_user("Enter your user name: ");

gets(uname);

prompt_user("\nEnter your password: ");

gets(upass);

/* Add user name and password to DPB. */

dpb = dbp_buffer;

isc_expand_dpb(&dpb, &dpb_length, isc_dpb_user_name, uname,
isc_dpb_password, upass, NULL);


Attaching to a Database

After creating and initializing a database handle, and optionally setting up a DPB to specify connection parameters, use isc_attach_database() to establish a connection to an existing database. Besides allocating system resources for the database connection, isc_attach_database() also associates a specific database with a database handle for use in subsequent API calls that require a handle.

isc_attach_database() expects six parameters:

A pointer to an error status array, where attachment errors can be reported should they occur.

The length, in bytes, of the database name for the database to open. If the database name includes a node name and path, these elements must be counted in the length argument.

A string containing the name of the database to attach. The name can include a node name and path specification.

A pointer to a previously declared and initialized database handle with which to associate the database to attach. All subsequent API calls use the handle to specify access to this database.

The length, in bytes, of the DPB. If no DPB is passed, set this value to zero.

A pointer to the DPB. If no DPB is passed, set this to NULL.

Each database attachment requires a separate call to isc_attach_database().

The following code establishes an attachment to the InterBase example database, employee.gdb, and specifies a DPB to use for the attachment:

#include <ibase.h>

. . .

isc_db_handle db1;

char dpb_buffer[256], *dpb, *p;

short dpb_length;

char *str = "employee.gdb";

ISC_STATUS status_vector[20];

. . .

/* Set database handle to zero before attaching to a database. */

db1 = 0L;

/* Initialize the DPB. */

dpb = dpb_buffer;

*dpb++ = isc_dpb_version1;

*dpb++ = isc_num_buffers;

*dpb++ = 1;

*dpb++ = 90;

dpb_length = dpb - dpb_buffer;

/* Attach to the database. */

isc_attach_database(status_vector, strlen(str), str, &db1, dpb_length,
dbp_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

error_exit();

}

. . .

The following code illustrates how to attach to a database without passing a DPB:

#include <ibase.h>

. . .

isc_db_handle db1;

char *str = "employee.gdb";

ISC_STATUS status_vector[20];

. . .

/* Set database handle to zero before attaching to a database. */

db1 = 0L;

/* Attach to the database. */

isc_attach_database(status_vector, strlen(str), str, &db1, 0, NULL);

if (status_vector[0] == 1 && status_vector[1])

{

error_exit();

}

. . .


Requesting Information About a Database Attachment

After an application attaches to a database, it may need information about the attachment. The isc_database_info() call enables an application to query for attachment information, such as the version of the on-disk structure (ODS) used by the attachment, the number of database cache buffers allocated, the number of databases pages read from or written to, or write-ahead log information.

In addition to a pointer to the error status vector and a database handle, isc_database_info() requires two application-provided buffers, a request buffer, where the application specifies the information it needs, and a result buffer, where InterBase returns the requested information. An application populates the request buffer with information prior to calling isc_database_info(), and passes it both a pointer to the request buffer, and the size, in bytes, of that buffer.

The application must also create a result buffer large enough to hold the information returned by InterBase. It passes both a pointer to the result buffer, and the size, in bytes, of that buffer, to isc_database_info(). If InterBase attempts to pass back more information than can fit in the result buffer, it puts the value, isc_info_truncated, defined in ibase.h, in the final byte of the result buffer.


Request Buffer Items and Result Buffer Values

The request buffer is a char array into which is placed a sequence of byte values, one per requested item of information. Each byte is an item type, specifying the kind of information desired. Compile-time constants for all item types are defined in ibase.h.

The result buffer returns a series of clusters of information, one per item requested. Each cluster consists of three parts:

1.	A one-byte item return type. There are compile-time constants defined for all the item return types in ibase.h.

2.	A two-byte number specifying the number of bytes that follow in the remainder of the cluster.

3.	A value, stored in a variable number of bytes, whose interpretation (e.g., as a number or as a string of characters) depends on the item return type.

A calling program is responsible for interpreting the contents of the result buffer and for deciphering each cluster as appropriate. In many cases, the value simply contains a number or a string (sequence of characters). But in other cases, the value is a number of bytes whose interpretation depends on the item return type.

The clusters returned to the result buffer are not aligned. Furthermore, all numbers are represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers have the sign in the last byte. Convert the numbers to a data type native to your system, if necessary, before interpreting them. The API call, isc_vax_integer(), can be used to perform the conversion.

The following sections list all request buffer items and result buffer values for various categories of database information.

Database Characteristics

Several items are provided for determining database characteristics, such as its size and major and minor ODS version numbers. The following table lists the request buffer items that can be passed, and the information returned in the result buffer for each item type

Request Buffer Item	Result Buffer Contents
isc_info_allocation	Number of database pages allocated.
isc_info_base_level	Database version (level) number:  1 byte containing the number 1 1 byte containing the version number
isc_info_db_id	Database file name and site name: 1 byte containing the number 2 1 byte containing the length, d, of the database file name in bytes A string of d bytes, containing the database file name 1 byte containing the length, l, of the site name in bytes A string of l bytes, containing the site name
isc_info_implementation	Database implementation number: 1 byte containing a 1 1 byte containing the implementation number 1 byte containing a "class" number, either 1 or 12
isc_info_no_reserve	0 or 1. 0, the default, indicates space is reserved on each database page for holding backup versions of modified records. 1 indicates no space is reserved for such records.
isc_info_ods_minor_version	On-disk structure (ODS) minor version number. An increase in a minor version number indicates a non-structural change, one that still allows the database to be accessed by database engines with the same major version number but possibly different minor version numbers.
isc_info_ods_version	ODS major version number. Databases with different major version numbers have different physical layouts. A database engine can only access databases with a particular ODS major version number. Trying to attach to a database with a different ODS number results in an error.
isc_info_page_size	Number of bytes per page of the attached database. Use with isc_info_allocation to determine the size of the database.
isc_info_version	Version identification string of the database implementation: 1 byte containing the number 1 1 byte specifying the length, n, of the following string n bytes containing the version identification string
Environmental Characteristics

Several items are provided for determining environmental characteristics, such as the amount of memory currently in use, or the number of database cache buffers currently allocated. These items are described in the following table:

Request Buffer Item	Result Buffer Contents
isc_info_current_memory	Amount of server memory (in bytes) currently in use.
isc_info_forced_writes	Number specifying the mode in which database writes are performed (0 for asynchronous, 1 for synchronous).
isc_info_max_memory	Maximum amount of memory (in bytes) used at one time since the first process attached to the database.
isc_info_num_buffers	Number of memory buffers currently allocated.
isc_info_sweep_interval	Number of transactions that are committed between "sweeps" to remove database record versions that are no longer needed.
isc_info_user_names	NetWare only. Names of all the users currently attached to the database. For each such user, the result buffer will contain an isc_info_user_names byte followed by a 1-byte length specifying the number of bytes in the user name, followed by the user name.
Note:	Not all environmental information items are available on all platforms.

Performance Statistics

There are four items providing performance statistics for a database. These statistics accumulate for a database from the moment it is first attached by any process until the last remaining process detaches from the database. A program requesting this information, therefore, sees information pertaining to its own attachment and all other attachments.

For example, the value returned for isc_info_reads is the number of reads since the current database was first attached, that is, an aggregate of all reads done by all attached processes, rather than the number of reads done for the calling program since it attached to the database.

The items providing performance statistics are summarized in the following table:

Request Buffer Item	Result Buffer Contents
isc_info_fetches	Number of reads from the memory buffer cache.
isc_info_marks	Number of writes to the memory buffer cache.
isc_info_reads	Number of page reads.
isc_info_writes	Number of page writes.
Database Operation Counts

Several information items are provided for determining the number of various database operations performed by the currently attached calling program. These values are calculated on a per-table basis.

When any of these information items is requested, InterBase returns to the result buffer:

1 byte specifying the item type (e.g., isc_info_insert_count).

2 bytes telling how many bytes compose the subsequent value pairs.

A pair of values for each table in the database on which the requested type of operation has occurred since the database was last attached.

Each pair consists of:

2 bytes specifying the table ID.

4 bytes listing the number of operations (e.g., inserts) done on that table.

Tip:	To determine an actual table name from a table ID, query the system table, RDB$RELATION.

The following table describes the items which return count values for operations on the database:

Request Buffer Item	Result Buffer Contents
isc_info_backout_count	Number of removals of a version of a record.
isc_info_delete_count	Number of database deletes since the database was last attached.
isc_info_expunge_count	Number of removals of a record and all of its ancestors, for records whose deletions have been committed.
isc_info_insert_count	Number of inserts into the database since the database was last attached.
isc_info_purge_count	Number of removals of old versions of fully mature records (records committed, resulting in older--ancestor--versions no longer being needed).
isc_info_read_idx_count	Number of reads done via an index since the database was last attached.
isc_info_read_seq_count	Number of sequential database reads, that is, the number of sequential table scans (row reads) done on each table since the database was last attached.
isc_info_update_count	Number of database updates since the database was last attached.
Write-ahead Log (WAL) Information

On NetWare servers, InterBase supports a write-ahead log (WAL) protocol that records all database changes before they are actually stored in the database itself. The WAL consists of:

WAL buffers, areas of memory set aside to store database changes.

WAL files, disk files used to record changes flushed from the write-ahead buffers.

A WAL writer, the shared process that administers the WAL buffers, log file rollover, and checkpoints for a database.

A physical log file can be partitioned into equal-sized logical log files.

WAL-related information that can be requested is summarized in the following table:

Request Buffer Item	Result Buffer Contents
isc_info_logfile	0, WAL protocol is inactive, or 1, WAL is active.
isc_info_cur_logfile_name	Name of the current physical WAL file.
isc_info_cur_log_part_offset	Partition offset (in bytes) of the beginning of the current logical log file in the current physical WAL file. If the current log file is not partitioned, then the result value for isc_info_cur_log_part_offset result is 0.
isc_info_num_wal_buffers	Number of memory WAL buffers used to store database changes.
isc_info_wal_buffer_size	Number of bytes in each WAL buffer.
isc_info_wal_ckpt_length	Number of bytes of data that determine a "checkpoint" interval; whenever that number of bytes have been recorded in the WAL file(s), the database on disk is brought up to date with respect to the previous checkpoint.
isc_info_wal_cur_ckpt_interval	Number specifying how many bytes have been written so far in the log file(s) towards the next checkpoint.
isc_info_wal_prv_ckpt_fname	Name of the physical log file in which the previous checkpoint occurred.
isc_info_wal_prv_ckpt_poffset	Partition offset (in bytes) of the beginning of the logical log file in which the previous checkpoint occurred. This offset is relative to the beginning of the physical log file specified in isc_info_wal_prv_ckpt_fname. It is 0 if that physical log file is not partitioned.
isc_info_wal_grpc_wait_usecs	Number of microseconds that specify a "group commit" interval; after the specified number of microseconds have passed, all changes stored in WAL buffers for transactions committed since the beginning of the interval are flushed to the WAL file.
isc_info_wal_num_io	Total number of log file writes performed for the database since the first attachment in this session.
isc_info_wal_avg_io_size	Average number of bytes written to the log file(s) since the first attachment in this session.
isc_info_wal_num_commits	Number of transactions committed since the first attachment in this session.
isc_info_wal_avg_grpc_size	Average number of transactions committed per "group commit" interval.


Isc_database_info( ) Call Example

The following code requests the page size and the number of buffers for the currently attached database, then examines the result buffer:

char db_items[] = {

isc_info_page_size, isc_info_num_buffers,

isc_info_end};

char res_buffer[40], *p, item;

int length;

SLONG page_size = 0L, num_buffers = 0L;

ISC_STATUS status_vector[20];

isc_database_info(

status_vector,

&handle,	/* Set in previous isc_attach_database() call. */

sizeof(db_items),

db_items,

sizeof(res_buffer),

res_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

/* An error occurred. */

isc_print_status(status_vector);

return(1);

};

/* Extract the values returned in the result buffer. */

for (p = res_buffer; *p != isc_info_end ;)

{

item = *p++

length = isc_vax_integer(p, 2);

p += 2;

switch (item)

{

case isc_info_page_size:

page_size = isc_vax_integer(p, length);

break;

case isc_info_num_buffers:

num_buffers = isc_vax_integer(p, length);

break;

default:

break;

}

p += length;

};


Disconnecting From Databases

When an application is finished accessing a database, and any changes are committed or rolled back, the application should disconnect from the database, release system resources allocated for the attachment, and set the database handle to zero with a call to isc_detach_database().

isc_detach_database() requires two arguments: a pointer to the error status vector, and a pointer to the handle of the database from which to detach. For example, the following statement detaches from the database pointed to by the database handle, db1:

isc_detach_database(status_vector, &db1);

Each database to detach requires a separate call to isc_detach_database().


Deleting a Database

To remove a database from the system if it is no longer needed, use isc_drop_database(). This function permanently wipes out a database, erasing its data, metadata, and all of its supporting files, such as secondary files, shadow files, and write-ahead log files.

A database can only be deleted if it is previously attached with a call to isc_attach_database(). The call to isc_attach_database() establishes a database handle for the database. That handle must be passed in the call to isc_drop_database().

For example, the following code deletes the database pointed to by the database handle, db1:

#include <ibase.h>

. . .

isc_db_handle db1;

char *str = "employee.gdb";

ISC_STATUS status_vector[20];

. . .

/* Set database handle to zero before attaching to a database. */

db1 = 0L;

/* Attach to the database. */

isc_attach_database(status_vector, strlen(str), str, &db1, 0, NULL);

if (status_vector[0] == 1 && status_vector[1])

{

error_exit();

}

isc_drop_database(status_vector, &db1);

if (status_vector[0] == 1 && status_vector[1])

{

error_exit();

}

. . .


Working With Transactions

This section of the Help file describes how to set up a transaction parameter buffer (TPB) that contains parameters, how to set up and initialize transaction handles, and how to use the API functions that control transactions. It also explains how to retrieve a transaction ID.

All data definition and data manipulation in an application takes place in the context of one or more transactions, one or more statements that work together to complete a specific set of actions that must be treated as an atomic unit of work.

The following table summarizes the API functions most commonly used when working with transactions. Functions are listed in the order they typically appear in an application.

Function	Purpose
isc_start_transaction()	Starts a new transaction against one or more databases. Uses a previously declared and populated TPB.
isc_commit_retaining()	Commits a transaction's changes, and preserves the transaction context for further transaction processing.
isc_commit_transaction()	Commits a transaction's changes, and ends the transaction.
isc_rollback_transaction()	Rolls back a transaction's changes, and ends the transaction.
In addition to these functions, the following table lists less frequently used API transaction functions in the order they typically appear when used:

Function	Purpose
isc_start_multiple()	Starts a new transaction against one or more databases. Used instead of isc_start_transaction() for programming languages such as FORTRAN, that do not support variable numbers of arguments to functions.
isc_prepare_transaction()	Performs the first phase of a two-phase commit, prior to calling isc_commit_transaction(). Used only when it is absolutely necessary to override InterBase's automatic two-phase commit.
isc_prepare_transaction2()	Performs the first phase of a two-phase commit, prior to calling isc_commit_transaction(). Used only when absolutely necessary to override InterBase's automatic two-phase commit.


Starting Transactions

Starting transactions is a three-step process:

1.	Creating and initializing a transaction handle for each simultaneous transaction to be started.

2.	Optionally creating and populating a TPB for each transaction.

3.	Calling isc_start_transaction() for each transaction to start.

Note:	Programmers writing applications that do not permit function calls to pass a variable number of parameters must use isc_start_multiple() instead of isc_start_transaction().


Creating Transaction Handles

Every transaction that is used in an application must be associated with its own transaction handle, a pointer to an address that is used by all API transaction functions. The ibase.h header file contains the following C typedef declaration for transaction handles:

typedef void ISC_FAR *isc_tr_handle;

To use this typedef for declaring transaction handles in an application, include ibase.h in each source file module:

#include <ibase.h>

Declaring Transaction Handles

To establish transaction handles for use, declare a variable of type isc_tr_handle for each simultaneously active transaction. The following code declares two handles:

#include <ibase.h>

. . .

isc_tr_handle tr1;

iac_tr_handle tr2;

Once a transaction is committed or rolled back, its handle can be assigned to a different transaction in a subsequent call to isc_start_transaction(). If an application uses several transactions, but only starts a subset of transactions at the same time, it is only necessary to declare as many handles as there will be simultaneously active transactions. For example, if an application starts a total of three transactions, but only runs two of them at the same time, only two transaction handles need be declared.

Initializing Transaction Handles

Before a transaction handle can be used to start a new transaction, it must be set to zero. The following code illustrates how two transaction handles are set to zero:

#include <ibase.h>

. . .

isc_tr_handle tr1;

isc_tr_handle tr2;

. . .

/* Set transaction handles to zero before starting a transaction. */

tr1 = 0L;

tr2 = 0L;

Once a transaction handle is initialized to zero, it can be used in a call to isc_start_transaction() to establish a new transaction. If a nonzero transaction handle is passed to isc_start_transaction(), the startup fails and an error code is returned.


Creating a Transaction Parameter Buffer

The transaction parameter buffer (TPB) is an optional, application-defined byte vector, passed as an argument to isc_start_transaction(), that sets up a transaction's attributes, its operating characteristics, such as whether the transaction has read and write access to tables, or read-only access, and whether or not other simultaneously active transactions can share table access with the transaction. Each transaction may have its own TPB, or transactions that share operating characteristics can use the same TPB.

If a TPB is not created for a transaction, a NULL pointer must be passed to isc_start_transaction() in its place. A default set of attributes is automatically assigned to such transactions.

A TPB is declared in a C program as a char array of one-byte elements. Each element is a parameter that describes a single transaction attribute. A typical declaration is as follows:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_read_committed,

isc_tpb_no_rec_version,

isc_tpb_wait};

This example makes use of parameter constants defined in the InterBase header file, ibase.h. The first element in every TPB must be the isc_tpb_version3 constant. The following table lists available TPB constants, describes their purposes, and indicates which constants are assigned as a default set of attributes when a NULL TPB pointer is passed to isc_start_transaction():

Parameter	Description
isc_tpb_version3	InterBase version 3 transaction.
isc_tpb_consistency	Table-locking transaction model.
isc_tpb_concurrency	High throughput, high concurrency transaction with acceptable consistency. Use of this parameter takes full advantage of the InterBase multi-generational transaction model. (Default.)
isc_tpb_shared	Concurrent, shared access of a specified table among all transactions. Use in conjunction with isc_tpb_lock_read and isc_tpb_lock_write to establish the lock option. (Default.)
isc_tpb_protected	Concurrent, restricted access of a specified table. Use in conjunction with isc_tpb_lock_read and isc_tpb_lock_write to establish the lock option.
isc_tpb_wait	Lock resolution specifies that the transaction is to wait until locked resources are released before retrying an operation. (Default.)
isc_tpb_nowait	Lock resolution specifies that the transaction is not to wait for locks to be released, but instead, a lock conflict error should be returned immediately.
isc_tpb_read	Read-only access mode that allows a transaction only to select data from tables.
isc_tpb_write	Read-write access mode of that allows a transaction to select, insert, update, and delete table data. (Default.)
isc_tpb_lock_read	Read-only access of a specified table. Use in conjunction with isc_tpb_shared, isc_tpb_protected, and isc_tpb_exclusive to establish the lock option.
isc_tpb_lock_write	Read-write access of a specified table. Use in conjunction with isc_tpb_shared, isc_tpb_protected, and isc_tpb_exclusive to establish the lock option. (Default.)
isc_tpb_read_committed	High throughput, high concurrency transaction that can read changes committed by other concurrent transactions. Use of this parameter takes full advantage of the InterBase multi-generational transaction model.
isc_tpb_rec_version	Enables an isc_tpb_read_committed transaction to read the most recently committed version of a record even if other, uncommitted versions are pending.
isc_tpb_no_rec_version	Enables an isc_tpb_read_committed transaction to read only the latest committed version of a record. If an uncommitted version of a record is pending and isc_tpb_wait is also specified, then the transaction waits for the pending record to be committed or rolled back before proceeding. Otherwise, a lock conflict error is reported at once.
TPB parameters specify the following classes of information:

Transaction version number is used internally by the InterBase engine. It is always be the first attribute specified in the TPB, and must always be set to isc_tpb_version3.

Access mode describes the actions that can be performed by the functions associated with the transaction. Valid access modes are:

isc_tpb_read

isc_tpb_write

Isolation level describes the view of the database given a transaction as it relates to actions performed by other simultaneously occurring transactions. Valid isolation levels are:

isc_tpb_concurrency

isc_tpb_consistency

isc_tpb_read_committed, isc_tpb_rec_version

isc_tpb_read_committed, isc_tpb_no_rec_version

Lock resolution describes how a transaction should react if a lock conflict occurs. Valid lock resolutions are:

isc_tpb_wait

isc_tpb_nowait

Table reservation optionally describes an access method and lock resolution for a specified table that the transaction accesses. When table reservation is used, tables are reserved for the specified access when the transaction is started, rather than when the transaction actually accesses the table. Valid reservations are:

isc_tpb_shared, isc_tpb_lock_write

isc_tpb_shared, isc_tpb_lock_read

isc_tpb_protected, isc_tpb_lock_write

isc_tpb_protected, isc_tpb_lock_read

TPB parameters are described in detail in the following sections.

Specifying the Transaction Version Number

The first parameter in a TPB must always specify the version number for transaction processing. It must always be set to isc_tpb_version3. The following TPB declaration illustrates the correct use and position of this parameter:

static char isc_tpb[] = {isc_tpb_version3, ...};

Specifying Access Mode

The access mode parameter describes the actions a transaction can perform against a table. The default access mode, isc_tpb_write, enables a transaction to read data from a table and write data to it. A second access mode, isc_tpb_read, restricts table access to read only. For example, the following TPB declaration specifies a read-only transaction:

static char isc_tpb[] = {isc_tpb_version3, isc_tpb_read};

A TPB should only specify one access mode parameter. If more than one is specified, later declarations override earlier ones.

If a TPB is declared that omits the access mode parameter, InterBase interprets transaction access as read and write.

Specifying Isolation Level

The isolation level parameter specifies the view of the database permitted a transaction as it relates to actions performed by other simultaneously occurring transactions.

isc_tpb_concurrency

By default, after a transaction starts it cannot access committed changes to a table made by other simultaneous transactions, even though it shares access to the table with them. Such a transaction has an isolation level of isc_tpb_concurrency, meaning it can have concurrent access to tables also accessed simultaneously by other transactions. The following declaration creates a TPB specifying an isolation level of isc_tpb_concurrency:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency};

isc_tpb_read_committed

A second isolation level, isc_tpb_read_committed, offers all the advantages of the isc_tpb_concurrency isolation level and additionally enables a transaction to access changes committed by other simultaneous transactions. Two other parameters, isc_tpb_rec_version, and isc_tpb_no_rec_version, should be used with the isc_tpb_read_committed parameter. They offer refined control over the committed changes a transaction is permitted to access:

isc_tpb_no_rec_version, the default refinement, specifies that a transaction can only read the latest version of a row. If a change to a row is pending, but not yet committed, the row cannot be read.

isc_tpb_rec_version specifies that a transaction can read the latest committed version of a row, even if a more recent uncommitted version is pending.

The following declaration creates a TPB with a read committed isolation level, and specifies that the transaction can read the latest committed version of a row:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_read_committed,

isc_tpb_rec_version};

isc_tpb_consistency

InterBase also supports a restrictive isolation level. isc_tpb_consistency prevents a transaction from accessing tables if they are written to by other transactions; it also prevents other transactions from writing to a table once this transaction writes to it. This isolation level is designed to guarantee that if a transaction writes to a table before other simultaneous read and write transactions, then only it can change a table's data. Because it essentially restricts (and often prevents) shared access to tables, isc_tpb_consistency should be used with care.

A TPB should only specify one isolation mode parameter (and one refinement parameter, if isolation mode is isc_tpb_read_committed). If more than one is specified, later declarations override earlier ones.

If a TPB is declared that omits the isolation mode parameter, InterBase interprets it as isc_tpb_concurrency.

Isolation Level Interactions

To determine the possibility for lock conflicts between two transactions accessing the same database, each transaction's isolation level and access mode must be considered. The following table summarizes possible combinations:

As this table illustrates, isc_tpb_concurrency and isc_tpb_read_committed transactions offer the least chance for conflicts. For example, if t1 is an isc_tpb_concurrency transaction with isc_tpb_write access, and t2 is an isc_tpb_read_committed transaction with isc_tpb_write access, t1 and t2 only conflict when they attempt to update the same rows. If t1 and t2 have isc_tpb_read access, they never conflict with other transactions.

An isc_tpb_consistency transaction with isc_tpb_write access is guaranteed that if it gains access to a table that it alone can update a table, but it conflicts with all other simultaneous transactions except for isc_tpb_concurrency and isc_tpb_read_committed transactions running in isc_tpb_read mode. An isc_tpb_consistency transaction with isc_tpb_read access is compatible with any other read-only transaction, but conflicts with any transaction that attempts to insert, update, or delete data.

Specifying Lock Resolution

The lock resolution parameter describes what happens if a transaction encounters an access conflict during a write operation (update and delete operations on existing rows). There are two possible choices for this parameter:

isc_tpb_wait, the default, specifies that the transaction should wait until locked resources are released. Once the resources are released, the transaction retries its operation.

isc_tpb_nowait specifies that the transaction should return a lock conflict error without waiting for locks to be released.

For example, the following declaration creates a TPB with write access, a concurrency isolation mode, and a lock resolution of isc_tpb_nowait:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_nowait};

A TPB should only specify one lock resolution parameter. If more than one is specified, later declarations override earlier ones.

If a TPB is declared that omits the lock resolution parameter, InterBase interprets it as isc_tpb_concurrency.

Specifying Table Reservation

Ordinarily, transactions gain specific access to tables only when they actually read from or write to them. Optional table reservation parameters can be passed in the TPB. Table reservation optionally describes an access method and lock resolution for a specified table that the transaction accesses. When table reservation is used, tables are reserved for the specified access when the transaction is started, rather than when the transaction actually accesses the table. Table reservation is only useful in an environment where simultaneous transactions share database access. It has three main purposes:

Prevent possible deadlocks and update conflicts that can occur if locks are taken only when actually needed (the default behavior).

Provide for dependency locking, the locking of tables that may be affected by triggers and integrity constraints. While explicit dependency locking is not required, it can assure that update conflicts do not occur because of indirect table conflicts.

Change the level of shared access for one or more individual tables in a transaction. For example, an isc_tpb_write transaction with an isolation level of isc_tpb_concurrency may need exclusive update rights for a single table, and could use a reservation parameter to guarantee itself sole write access to the table.

Valid reservations are:

isc_tpb_shared, isc_tpb_lock_write, which permits any transaction with an access mode of isc_tpb_write and isolation levels of isc_tpb_concurrency or isc_tpb_read_committed, to update, while other transactions with these isolation levels and an access mode of isc_tpb_read can read data.

isc_tpb_shared, isc_tpb_lock_read, which permits any transaction to read data, and any transaction with an access mode of isc_tpb_write to update. This is the most liberal reservation mode.

isc_tpb_protected, isc_tpb_lock_write, which prevents other transactions from updating. Other transactions with isolation levels of isc_tpb_concurrency or isc_tpb_read_committed can read data, but only this transaction can update.

isc_tpb_protected, isc_tpb_lock_read, which prevents all transactions from updating, but permits all transactions to read data.

The name of the table to reserve must immediately follow the reservation parameters. For example, the following TPB declaration reserves a table, EMPLOYEE, for protected read access:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_nowait,

isc_tpb_protected, isc_tpb_lock_read, "EMPLOYEE"};

Several tables can be reserved at the same time. The following declaration illustrates how two tables are reserved, one for protected read, the other for protected write:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_nowait,

isc_tpb_protected, isc_tpb_lock_read, "COUNTRY",

isc_tpb_protected, isc_tpb_lock_write, "EMPLOYEE"};

Using the Default TPB

Providing a TPB for a transaction is optional. If one is not provided, then a NULL pointer must be passed to isc_start_transaction() in place of a pointer to the TPB. In this case, InterBase treats a transaction as if the following TPB had been declared for it:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_wait};


Calling Isc_start_transaction( )

Once transaction handles and TPBs are prepared, a transaction can be started by calling isc_start_transaction() using the following syntax:

ISC_STATUS isc_start_transaction(
ISC_STATUS *status vector,
isc_tr_handle *trans_handle,
short db_count,
isc_db_handle *&db_handle,
unsigned short tpb_length,
char *tpb_ad);

For a transaction that runs against a single database, set db_count to 1. db_handle should be a database handle set with a previous call to isc_attach_database(). tpb_length is the size of the TPB passed in the next parameter, and tpb_ad is the address of the TPB. The following code illustrates a typical call to isc_start_transaction():

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

isc_db_handle db1;

isc_tr_handle tr1;

static char isc_tbp[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_wait};

. . .

/* Initialize database and transaction handles here. */

db1 = 0L;

tr1 = 0L;

. . .

/* Code for attaching to database here is omitted. */

isc_start_transaction(status_vector,
&tr1,

1,

&db1,

(unsigned short) sizeof(isc_tpb),

isc_tpb);

A transaction can be opened against multiple databases. To do so, set the db_count parameter to the number of databases against which the transaction runs, then for each database, repeat the db_handle, tpb_length, and tpb_ad parameters as a group once for each database. For example, the following code fragment assumes that two databases are connected when the transaction is started:

isc_start_transaction(status_vector,
&tr1,

2,

&db1,

(unsigned short) sizeof(isc_tpb),

&tpb);

&db2,

(unsigned short) sizeof(isc_tpb),

&tpb);


Calling Isc_start_multiple( )

An alternate method for starting a transaction against multiple databases is to use isc_start_multiple(). Using isc_start_multiple() is not recommended unless you:

Are using a language that does not support a variable number of arguments in a function call.

Do not know how many databases you want to attach to when coding the start of a transaction.

C programmers should seldom need to use this function.

isc_start_multiple() passes information about each target database to InterBase through an array of transaction existence blocks (TEBs). There must be one TEB for each database against which a transaction runs. A TEB is a structure you must declare in your applications as follows:

typdef struct {

long *db_ptr;

long tpb_len;

char *tpb_ptr;

} ISC_TEB;

db_ptr is a pointer to a previoulsy declared, initialized, and populated database handle. tpb_len is the size, in bytes, of the transaction parameter buffer (TPB) to use for the database, and tpb_ptr is a pointer to the TPB itself.

To use a TEB structure in an application, declare an array variable of type ISC_TEB. The number of array dimensions should correspond to the number of databases that the transaction runs against. For example, the following declaration creates an array of two TEBs, capable of supporting two databases:

ISC_TEB teb_array[2];

Once an array of TEBs is declared, and corresponding TBPs are created and populated for each database, values may be assigned to the appropriate fields in the TEBs. For example, the following code illustrates how two TEBs are filled:

. . .

ISC_STATUS status_vector[20];

isc_db_handle db1, db2;

isc_tr_handle trans;

ISC_TEB teb_array[2];

. . .

db1 = db2 = 0L;

trans = 0L;

/* Code assumes that two TPBs, isc_tpb1, and isc_tpb2, are created here. */

/* Code assumes databases are attached here. */

/* assign values to TEB array */

teb_array[0].db_ptr = &db1;

teb_array[0].tpb_len = sizeof(isc_tpb1);

teb_array[0].tpb_ptr = isc_tpb1;

teb_array[1].db_ptr = &db2;

teb_array[1].tpb_len = sizeof(isc_tpb2);

teb_array[1].tpb_ptr = isc_tpb2;

. . .

After the TEBs are loaded with values, isc_start_multiple() can be called using the following syntax:

ISC_STATUS isc_start_multiple(

ISC_STATUS *status_vector,

isc_tr_handle *trans_handle,

short db_handle_count,

void *teb_vector_address);

For example, the following statements starts a two-database transaction:

. . .

ISC_STATUS status_vector[20];

isc_db_handle db1, db2;

isc_tr_handle trans;

ISC_TEB teb_array[2];

. . .

db1 = db2 = 0L;

trans = 0L;

/* Code assumes that two TPBs, isc_tpb1, and isc_tpb2, are created here. */

/* Code assumes databases are attached here. */

/* assign values to TEB array */

teb_array[0].db_ptr = &db1;

teb_array[0].tpb_len = sizeof(isc_tpb1);

teb_array[0].tpb_ptr = isc_tpb1;

teb_array[1].db_ptr = &db2;

teb_array[1].tpb_len = sizeof(isc_tpb2);

teb_array[1].tpb_ptr = isc_tpb2;

/* Start the transaction */

isc_start_multiple(status_vector, &trans, 2, teb_array);

. . .


Ending Transactions

When a transaction's tasks are complete, or an error prevents a transaction from completing, the transaction must be ended to set the database to a consistent state. There are two API functions that end transactions:

isc_commit_transaction() makes a transaction's changes permanent in the database. For transactions that span databases, this function performs an automatic, two-phase commit to ensure that all changes are made successfully.

isc_rollback_transaction() undoes a transaction's changes, returning the database to its previous state, before the transaction started. This function is typically used when one or more errors occur that prevent a transaction from completing successfully.

Both isc_commit_transaction() and isc_rollback_transaction() close the record streams associated with the transaction, reinitialize the transaction name to zero, and release system resources allocated for the transaction. Freed system resources are available for subsequent use by any application or program.

isc_rollback_transaction() is frequently used inside error-handling routines to clean up transactions when errors occur. It can also be used to roll back a partially completed transaction prior to retrying it, and it can be used to restore a database to its prior state if a program encounters an unrecoverable error.

The API offers three additional functions for controlling transactions:

isc_commit_retaining() commits a transaction but retains the current transaction's context--the system resources and cursor states used in the transaction--without requiring the overhead of ending a transaction, starting a new one, and reestablishing cursor states. In a busy, multi-user environment, maintaining transaction context for each user speeds up processing and uses fewer system resources than closing a transaction and opening a new one.

isc_prepare_transaction() and isc_prepare_transaction2() enable an application to perform the first phase of an automatic, two-phase commit in its own time, then issue a call to isc_commit_transaction() to complete the commit.

Important:	 If the program ends before a transaction ends, a transaction is automatically rolled back, but databases are not closed. If a program ends without closing the database, data loss or corruption is possible. Therefore, open databases should always be closed by issuing an explicit call to isc_detach_database().


Using Isc_commit_transaction( )

Use isc_commit_transaction() to write transaction changes permanently to a database. isc_commit_transaction() closes the record streams associated with the transaction, resets the transaction name to zero, and frees system resources assigned to the transaction for other uses. The complete syntax for isc_commit_transaction() is:

ISC_STATUS isc_commit_transaction(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle);

For example, the following call commits a transaction:

isc_commit_transaction(status_vector, &trans);

where status_vector is a pointer to a previously declared error status vector, and trans is a pointer to a previously declared and initialized transaction handle.

Tip:	 Even transactions started with an access mode of isc_tpb_read should be ended with a call to isc_commit_transaction() rather than isc_rollback_transaction(). The database is not changed, but the overhead required to start subsequent transactions is greatly reduced.

Using isc_commit_retaining( )

To write transaction changes to the database without establishing a new transaction context--the names, system resources, and current state of cursors used in a transaction--use isc_commit_retaining() instead of isc_commit_transaction(). In a busy, multi-user environment, maintaining the transaction context for each user speeds up processing and uses fewer system resources than closing and starting a new transaction for each action. The complete syntax for isc_commit_retaining() is:

ISC_STATUS isc_commit_retaining(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle);

isc_commit_retaining() writes all pending changes to the database, ends the current transaction without closing its record stream and cursors and without freeing its system resources, then starts a new transaction and assigns the existing record streams and system resources to the new transaction.

For example, the following call commits a specified transaction, preserving the current cursor status and system resources:

isc_commit_retaining(status_vector, &trans);

where status_vector is a pointer to a previously declared error status vector, and trans is a pointer to a previously declared and initialized transaction handle.

A call to isc_rollback_transaction() issued after isc_commit_retaining() only rolls back updates and writes occurring after the call to isc_commit_retaining().

Using isc_prepare_transaction( )

When a transaction is committed against multiple databases using isc_commit_transaction(), InterBase automatically performs a two-phase commit. During the first phase of the commit, the InterBase engine polls all database participants to make sure they are still available, writes a message describing the transaction to the RDB$TRANSACTION_DESCRIPTION field of the RDB$TRANSACTION system table, then puts the transaction into a limbo state. It is during the second phase that transaction changes are actually committed to the database.

Some applications may have their own, additional requirements to make of the two-phase commit. These applications can call isc_prepare_transaction() to execute the first phase of the two-phase commit, then perform their own, additional tasks before completing the commit with a call to isc_commit_transaction().

The syntax for isc_prepare_transaction() is:

ISC_STATUS isc_prepare_transaction(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle);

For example, the following code fragment illustrates how an application might call isc_prepare_transaction(), then its own routines, before completing a commit with isc_commit_transaction():

ISC_STATUS status_vector[20];

isc_db_handle db1;

isc_tr_handle trans;

. . .

/* Initialize handles. */

db1 = 0L;

trans = 0L;

. . .

/* Code assumes a database is attached here, */

/* and a transaction started. */

. . .

/* Perform first phase of two-phase commit. */

isc_prepare_transaction(status_vector, &trans);

/* Application does its own processing here. */

my_app_function();

/* Now complete the two-phase commit. */

isc_commit_transaction(status_vector, &trans);

Important:	 It is generally a dangerous practice to delay the second phase of the commit after completing the first, because delays increase the chance that network or server problems can occur between phases.


Using Isc_prepare_transaction2( )

Like isc_prepare_transaction(), isc_prepare_transaction2() performs the first phase of a two-phase commit, except that isc_prepare_transaction2() enables
an application to supply its own transaction description for insertion into the RDB$TRANSACTION_DESCRIPTION field of the RDB$TRANSACTION
system table.

Warning:	Do not use this call without first examining and understanding the information InterBase stores in RDB$TRANSACTION_DESCRIPTION during an automatic, two-phase commit. Storage of improper or incomplete information can prevent database recovery if the two-phase commit fails.


Using Isc_rollback_transaction( )

Use isc_rollback_transaction() to restore the database to its condition prior to the start of the transaction. isc_rollback_transaction() also closes the record streams associated with the transaction, resets the transaction name to zero, and frees system resources assigned to the transaction for other uses. isc_rollback_transaction() typically appears in error-handling routines. The syntax for isc_rollback_transaction() is:

ISC_STATUS isc_rollback_transaction(
ISC_STATUS *status_vector,
isc_tr_handle *trans_handle);

For example, the following call rolls back a transaction:

isc_rollback_transaction(status_vector, &trans);

where status_vector is a pointer to a previously declared error status vector, and trans is a pointer to a previously declared and initialized transaction handle.


Working With Dynamic SQL

This series of topics describes how to use API dynamic SQL (DSQL) functions to handle dynamically created SQL statements for data definition and manipulation. Using low-level API calls enables client applications to build SQL statements or solicit them from users at run time, providing users with a familiar database interface. It also provides developers low-level access to InterBase features, such as multiple databases, not normally available at a higher level with embedded DSQL statements. For example, the InterBase isql utility is a DSQL application built on low-level API calls.

All API DSQL function names begin with isc_dsql to make it easier to distinguish them from other API calls.


Overview of the DSQL Programming Process

Building and executing DSQL applications with the API involve the following general steps:

Embedding DSQL API functions in an application.

Using host-language facilities, such as data types and macros, to provide input and output areas for passing statements and parameters at run time.

Programming methods that use these statements and facilities to process SQL statements at run time.


DSQL API Limitations

Although DSQL offers many advantages, it also has the following limitations:

Dynamic transaction processing is not permitted; all named transactions must be declared at compile time.

Dynamic access to BLOB and array data is not supported; BLOB and array data can be accessed, but only through standard, statically processed SQL statements, or through low-level API calls.

Database creation is restricted to CREATE DATABASE statements executed within the context of EXECUTE IMMEDIATE.


Accessing Databases

The InterBase API permits applications to attach to multiple databases simultaneously using database handles. Database handles must be declared and initialized when an application is compiled. Separate database handles should be supplied and initialized for each database accessed simultaneously. For example, the following code creates a single handle, db1, and initializes it to zero:

#include <ibase.h>

isc_db_handle db1;

. . .

db1 = 0L;

Once declared and initialized, a database handle can be assigned dynamically to a database at run time as follows:

#include <ibase.h>

. . .

char dbname[129];

ISC_STATUS status_vector[20];

. . .

prompt_user("Name of database to open: ");

gets(dbname);

isc_attach_database(status_vector, 0, dbname, &db1, NULL, NULL);

A database handle can be used to attach to different databases as long as a previously attached database is first detached with isc_detach_database(), which automatically sets database handles to NULL. The following statements detach from a database, set the database handle to zero, and attach to a new database:

isc_detach_database(status_vector, &db1);

isc_attach_database(status_vector, 0, "employee.gdb", &db1, NULL, NULL);


Handling Transactions

InterBase requires that all transaction handles be declared when an application is compiled. Once fixed at compile time, transaction handles cannot be changed at run time, nor can new handles be declared dynamically at run time. Most API functions that process SQL statements at run time, such as isc_dsql_describe(), isc_dsql_describe_bind(), isc_dsql_execute(), isc_dsql_execute2(), isc_dsql_execute_immediate(), isc_dsql_exec_immed2(), and isc_dsql_prepare(), support the inclusion of a transaction handle parameter. The SQL statements processed by these functions cannot pass transaction handles even if the SQL syntax for the statement permits the use of a TRANSACTION clause.

Before a transaction handle can be used, it must be declared and initialized to zero. The following code declares, initializes, and uses a transaction handle in an API call that allocates and prepares an SQL statement for execution:

#include <ibase.h>

. . .

isc_tr_handle trans; /* Declare a transaction handle. */

isc_stmt_handle stmt; /* Declare a statement handle. */

char *sql_stmt = "SELECT * FROM EMPLOYEE";

isc_db_handle db1;

ISC_STATUS status_vector[20];

. . .

trans = 0L; /* Initialize the transaction handle to zero. */

stmt = NULL; /* Set handle to NULL before allocation. */

/* This code assumes both that a database attachment is made, */

/* and a transaction is started here. */

. . .

/* Allocate the SQL statement handle. */

isc_dsql_allocate_statement(status_vector, &db1, &stmt);

/* Prepare the statement for execution. */

isc_dsql_prepare(status_vector, &trans, &stmt, 0, sql_stmt, 1, NULL);

Note:	The SQL SET TRANSACTION statement cannot be prepared with isc_dsql_prepare(), but it can be processed with isc_dsql_execute_immediate() if:

1.	Previous transactions are first committed or rolled back.

2.	The transaction handle is set to NULL.

For more information about using SQL statements, see the Programmer's Guide. For more information about SQL statement syntax, see the Language Reference.


Creating a Database

To create a new database in an API application:

1.	Detach from any currently attached databases with isc_detach_database(). Detaching from a database automatically sets its database handle to NULL.

2.	Build the CREATE DATABASE statement to process.

3.	Execute the statement with isc_dsql_execute_immediate() or isc_dsql_exec_immed2().

For example, the following statements disconnect from any currently attached databases, and create a new database. Any existing database handles are set to NULL, so that they can be used to connect to the new database in future DSQL statements.

char *str = "CREATE DATABASE \"new_emp.gdb\"";

. . .

isc_detach_database(status_vector, &db1);

isc_dsql_execute_immediate(status_vector, &db1, &trans, 0, str, 1,
NULL);


Processing BLOB Data

BLOB processing is not directly supported using DSQL, nor are BLOB cursors supported. Applications that process SQL statements can use API calls to handle BLOB processing.


Processing Array Data

Array processing is not directly supported using DSQL. DSQL applications can use API calls to process array data.


Writing an API Application to Process SQL Statements

Writing an API application that processes SQL statements enables a developer to code directly to InterBase at a low level, while presenting end users a familiar SQL interface. API SQL applications are especially useful when any of the following are not known until run time:

The text of the SQL statement

The number of host variables

The data types of host variables

References to database objects

Writing an API DSQL application is more complex than programming embedded SQL applications with regular SQL because for most DSQL operations, the application needs explicitly to allocate and process an extended SQL descriptor area (XSQLDA) data structure to pass data to and from the database.

To use the API to process a DSQL statement, follow these basic steps:

1.	Determine if API calls can process the SQL statement.

2.	Represent the SQL statement as a character string in the application.

3.	If necessary, allocate one or more XSQLDAs for input parameters and return values.

4.	Use appropriate API programming methods to process the SQL statement.


Determining If API Calls Can Process an SQL Statement

Except as noted elsewhere in this section of the Help file, DSQL functions can process most SQL statements. For example, DSQL can process data manipulation statements such as DELETE and INSERT, data definition statements such as ALTER TABLE and CREATE INDEX, and SELECT statements.

The following table lists SQL statements that cannot be processed by DSQL functions:

Statement	Statement
CLOSE	DECLARE CURSOR
DESCRIBE	EXECUTE
EXECUTE IMMEDIATE	FETCH
OPEN	PREPARE
These statements are used to process DSQL requests or to handle SQL cursors, which must always be specified when an application is written. Attempting to use them with DSQL results in run-time errors.


Representing an SQL Statement As a Character String

Within a DSQL application, an SQL statement can come from different sources. It might come directly from a user who enters a statement at a prompt, as does isql. Or it might be generated by the application in response to user interaction. Whatever the source of the SQL statement, it must be represented as an SQL statement string, a character string that is passed to DSQL for processing.

SQL statement strings do not begin with the EXEC SQL prefix or end with a semicolon (;) as they do in typical embedded applications. For example, the following host-language variable declaration is a valid SQL statement string:

char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = 256";

The semicolon that appears at the end of this char declaration is a C terminator, and not part of the SQL statement string.

Specifying Parameters in SQL Statement Strings

SQL statement strings often include value parameters, expressions that evaluate to a single numeric or character value. Parameters can be used anywhere in statement strings where SQL expects a value that is not the name of a database object.

A value parameter in a statement string can be passed as a constant, or passed as a placeholder at run time. For example, the following statement string passes 256 as a constant:

char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = 256";

It is also possible to build strings at run time from a combination of constants. This method is useful for statements where the variable is not a true constant, or it is a table or column name, and where the statement is executed only once in the application.

To pass a parameter as a placeholder, the value is passed as a question mark (?) embedded within the statement string:

char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = ?";

When a DSQL function processes a statement containing a placeholder, it replaces the question mark with a value supplied in an extended SQL descriptor area (XSQLDA) previously declared and populated in the application. Use placeholders in statements that are prepared once, but executed many times with different parameter values.

Replaceable value parameters are often used to supply values in SQL SELECT statement WHERE clause comparisons and in the UPDATE statement SET clause.


Understanding the XSQLDA
Understanding XSQLDA and XSQLVAR

All DSQL applications must declare one or more extended SQL descriptor areas (XSQLDAs). The XSQLDA structure definition can be found in the ibase.h header file in the InterBase include directory. Applications declare instances of the XSQLDA for use.

The XSQLDA is a host-language data structure that DSQL uses to transport data to or from a database when processing an SQL statement string. There are two types of XSQLDAs: input descriptors and output descriptors. Both input and output descriptors are implemented using the XSQLDA structure.

One field in the XSQLDA, sqlvar, is an XSQLVAR structure. The sqlvar is especially important, because one XSQLVAR must be defined for each input parameter or column returned. Like the XSQLDA, the XSQLVAR is a structure defined in ibase.h in the InterBase include directory.

Applications do not declare instances of the XSQLVAR ahead of time, but must, instead, dynamically allocate storage for the proper number of XSQLVAR structures required for each DSQL statement before it is executed, then deallocate it, as appropriate, after statement execution.

The following figure illustrates the relationship between the XSQLDA and the XSQLVAR:

An input XSQLDA consists of a single XSQLDA structure and one XSQLVAR structure for each input parameter. An output XSQLDA also consists of one XSQLDA structure and one XSQLVAR structure for each data item returned by the statement. An XSQLDA and its associated XSQLVAR structures are allocated as a single block of contiguous memory.

The isc_dsql_prepare(), isc_dsql_describe(), and isc_dsql_describe_bind() functions can be used to determine the proper number of XSQLVAR structures to allocate, and the XSQLDA_LENGTH macro can be used to allocate the proper amount of space.


XSQLDA Field Descriptions

The following table describes the fields that comprise the XSQLDA structure:

Field Definition	Description
short version	Indicates the version of the XSQLDA structure. Set by an application. The current version is defined in ibase.h as SQLDA_VERSION1.
char sqldaid[8]	Reserved for future use.
ISC_LONG sqldabc	Reserved for future use.
short sqln	Indicates the number of elements in the sqlvar array. Set by the application. Whenever the application allocates storage for a descriptor, it should set this field.
short sqld	Indicates the number of parameters (for an input XSQLDA), or the number of select-list items (for an output XSQLDA). Set by InterBase during an isc_dsql_describe(), isc_dsql_describe_bind(), or isc_dsql_prepare().For an input descriptor, an sqld of 0 indicates that the SQL statement has no parameters. For an output descriptor, an sqld of 0 indicates that the SQL statement is not a SELECT statement.
XSQLVAR sqlvar	The array of XSQLVAR structures. The number of elements in the array is specified in the sqln field.


XSQLVAR Field Descriptions

The following table describes the fields that comprise the XSQLVAR structure:

Field Definition	Description
short sqltype	Indicates the SQL data type of parameters or select-list items. Set by InterBase during isc_dsql_describe(), isc_dsql_describe_bind(), or isc_dsql_prepare().
short sqlscale	Provides scale, specified as a negative number, for exact numeric data types (DECIMAL, NUMERIC). Set by InterBase during isc_dsql_describe(), isc_dsql_describe_bind(), or isc_dsql_prepare().
short sqlsubtype	Specifies the subtype for BLOB data. Set by InterBase during isc_dsql_describe(), isc_dsql_describe_bind(), or isc_dsql_prepare().
short sqllen	Indicates the maximum size, in bytes, of data in the sqldata field. Set by InterBase during isc_dsql_describe(), isc_dsql_describe_bind(), or isc_dsql_prepare().
char *sqldata	For input descriptors, specifies either the address of a select-list item or a parameter. Set by the application.For output descriptors, contains a value for a select-list item. Set by InterBase.
short *sqlind	On input, specifies the address of an indicator variable. Set by an application.On output, specifies the address of column indicator value for a select-list item following a FETCH. A value of 0 indicates that the column is not NULL; a value of -1 indicates the column is NULL. Set by InterBase.
short sqlname_length	Specifies the length, in bytes, of the data in field, sqlname. Set by InterBase during isc_dsql_prepare() or isc_dsql_describe().
char sqlname[32]	Contains the name of the column. Not NULL (\0) terminated. Set by InterBase during isc_dsql_prepare() or isc_dsql_describe().
short relname_length	Specifies the length, in bytes, of the data in field, relname. Set by InterBase during isc_dsql_prepare() or isc_dsql_describe().
char relname[32]	Contains the name of the table. Not NULL (\0) terminated. Set by InterBase during isc_dsql_prepare() or isc_dsql_describe().
short ownname_length	Specifies the length, in bytes, of the data in field, ownname. Set by InterBase during isc_dsql_prepare() or isc_dsql_describe().
char ownname[32]	Contains the name of the table owner. Not NULL (\0) terminated. Set by InterBase during isc_dsql_prepare() or isc_dsql_describe().
short aliasname_length	Specifies the length, in bytes, of the data in field, aliasname. Set by InterBase during isc_dsql_prepare() or isc_dsql_describe().
char aliasname[32]	Contains the alias name of the column. If no alias exists, contains the column name. Not NULL (\0) terminated. Set by InterBase during isc_dsql_prepare() or isc_dsql_describe().


Input Descriptors

Input descriptors are used to process SQL statement strings that contain parameters. Before an application can execute a statement with parameters, it must supply values for them. The application indicates the number of parameters passed in the XSQLDA sqld field, then describes each parameter in a separate XSQLVAR structure. For example, the following statement string contains two parameters, so an application must set sqld to 2, and describe each parameter:

char *str = "UPDATE DEPARTMENT SET BUDGET = ? WHERE LOCATION = ?";

When the statement is executed, the first XSQLVAR supplies information about the BUDGET value, and the second XSQLVAR supplies the LOCATION value.


Output Descriptors

Output descriptors return values from an executed query to an application. The sqld field of the XSQLDA indicates how many values were returned. Each value is stored in a separate XSQLVAR structure. The XSQLDA sqlvar field points to the first of these XSQLVAR structures. The following statement string requires an output descriptor:

char *str = "SELECT * FROM CUSTOMER WHERE CUST_NO > 100";


Using the XSQLDA_LENGTH Macro

The ibase.h header file defines a macro, XSQLDA_LENGTH, to calculate the number of bytes that must be allocated for an input or output XSQLDA. XSQLDA_LENGTH is defined as follows:

#define XSQLDA_LENGTH (n) (sizeof (XSQLDA) + (n- 1) * sizeof(XSQLVAR))

n is the number of parameters in a statement string, or the number of select-list items returned from a query. For example, the following C statement uses the XSQLDA_LENGTH macro to specify how much memory to allocate for an XSQLDA with 5 parameters or return items:

XSQLDA *my_xsqlda;

. . .

my_xsqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(5));

. . .


SQL Data Type Macro Constants

InterBase defines a set of macro constants to represent SQL data types and NULL status information in an XSQLVAR. An application should use these macro constants to specify the data type of parameters and to determine the data types of select-list items in an SQL statement. The following table lists each SQL data type, its corresponding macro constant expression, C data type or InterBase typedef, and whether or not the sqlind field is used to indicate a parameter or variable that contains NULL or unknown data:

SQL Data Type	Macro Expression	C Data Type or typedef	sqlind Used?
Array	SQL_ARRAY	ISC_QUAD	No
Array	SQL_ARRAY + 1	ISC_QUAD	Yes
BLOB	SQL_BLOB	ISC_QUAD	No
BLOB	SQL_BLOB + 1	ISC_QUAD	Yes
CHAR	SQL_TEXT	char[ ]	No
CHAR	SQL_TEXT + 1	char[ ]	Yes
DATE	SQL_DATE	ISC_QUAD	No
DATE	SQL_DATE + 1	ISC_QUAD	Yes
DECIMAL	SQL_SHORT, SQL_LONG, or SQL_DOUBLE	int, long, or double	No
DECIMAL	SQL_SHORT + 1, SQL_LONG + 1, or SQL_DOUBLE + 1	int, long, or double	Yes
DOUBLE PRECISON	SQL_DOUBLE	double	No
DOUBLE PRECISION	SQL_DOUBLE + 1	double	Yes
INTEGER	SQL_LONG	long	No
INTEGER	SQL_LONG + 1	long	Yes
FLOAT	SQL_FLOAT	float	No
FLOAT	SQL_FLOAT + 1	float	Yes
NUMERIC	SQL_SHORT, SQL_LONG, or SQL_DOUBLE	int, long, or double	No
NUMERIC	SQL_SHORT + 1, SQL_LONG + 1, or SQL_DOUBLE + 1	int, long, or double	Yes
SMALLINT	SQL_SHORT	short	No
SMALLINT	SQL_SHORT + 1	short	Yes
VARCHAR	SQL_VARYING	First 2 bytes: short containing the length of the character string. Remaining bytes: char[ ]	No
VARCHAR	SQL_VARYING + 1	First 2 bytes: short containing the length of the character string. Remaining bytes: char[ ]	Yes
Note:	DECIMAL and NUMERIC data types are stored internally as SMALLINT, INTEGER, or DOUBLE PRECISION data types. To specify the correct macro expression to provide for a DECIMAL or NUMERIC column, use isql to examine the column definition in the table to see how InterBase is storing column data, then choose a corresponding macro expression.

The data type information for a parameter or select-list item is contained in the sqltype field of the XSQLVAR structure. The value contained in sqltype provides two pieces of information:

The data type of the parameter or select-list item.

Whether sqlind is used to indicate NULL values. If sqlind is used, its value specifies whether the parameter or select-list item is NULL (-1), or not NULL (0).

For example, if sqltype equals SQL_TEXT, the parameter or select-list item is a CHAR that does not use sqlind to check for a NULL value (because, in theory, NULL values are not allowed for it). If sqltype equals SQL_TEXT + 1, then sqlind can be checked to see if the parameter or select-list item is NULL.

Tip:	The C language expression, sqltype & 1, provides a useful test of whether a parameter or select-list item can contain a NULL. The expression evaluates to 0 if the parameter or select-list item cannot contain a NULL, and 1 if the parameter or select-list item can contain a NULL. The following code fragment demonstrates how to use the expression:

if (sqltype & 1 == 0)

{

/* parameter or select-list item that CANNOT contain a NULL */

}

else

{

/* parameter or select-list item CAN contain a NULL */

}

By default, both isc_dsql_prepare() and isc_dsql_describe() return a macro expression of type + 1, so sqlind should always be examined for NULL values with these statements.


Handling Varying String Data Types

VARCHAR, CHARACTER VARYING, and NCHAR VARYING data types require careful handling in DSQL. The first two bytes of these data types contain string length information, while the remainder of the data contains the actual bytes of string data to process.

To avoid having to write code to extract and process variable-length strings in an application, it is possible to force these data types to fixed length using SQL macro expressions.

Applications can, instead, detect and process variable-length data directly. To do so, they must extract the first two bytes from the string to determine the byte-length of the string itself, then read the string, byte-by-byte, into a null-terminated buffer.


Handling NUMERIC and DECIMAL Data Types

DECIMAL and NUMERIC data types are stored internally as SMALLINT, INTEGER, or DOUBLE PRECISION data types, depending on the precision and scale defined for a column definition that uses these types. To determine how a DECIMAL or NUMERIC value is actually stored in the database, use isql to examine the column definition in the table. If NUMERIC is reported, then data is actually being stored as DOUBLE PRECISION.

When a DECIMAL or NUMERIC value is stored as a SMALLINT or INTEGER, the value is stored as a whole number. During retrieval in DSQL, the sqlscale field of the XSQLVAR is set to a negative number that indicates the factor of 10 by which the whole number (returned in sqldata), must be divided in order to produce the correct NUMERIC or DECIMAL value with its fractional part. If sqlcale is -1, then the number must be divided by 10, if it is -2, then the number must be divided by 100, -3 by 1000, and so forth.


Coercing Data Types

Sometimes when processing DSQL input parameters and select-list items, it is desirable or necessary to translate one data type to another. This process is referred to as data type coercion. For example, data type coercion is often used when parameters or select-list items are of type VARCHAR. The first two bytes of VARCHAR data contain string length information, while the remainder of the data is the string to process. By coercing the data from SQL_VARYING to SQL_TEXT, data processing can be simplified.

Coercion can only be from one compatible data type to another. For example, SQL_VARYING to SQL_TEXT, or SQL_SHORT to SQL_LONG.

Coercing Character Data Types

To coerce SQL_VARYING data types to SQL_TEXT data types, change the sqltype field in the parameter's or select-list item's XSQLVAR structure to the desired SQL macro data type constant. For example, the following statement assumes that var is a pointer to an XSQLVAR structure, and that it contains an SQL_VARYING data type to convert to SQL_TEXT:

var->sqltype = SQL_TEXT;

After coercing a character data type, provide proper storage space for it. The XSQLVAR field, sqllen, contains information about the size of the uncoerced data. Set the XSQLVAR sqldata field to the address of the data.

Coercing Numeric Data Types

To coerce one numeric data type to another, change the sqltype field in the parameter's or select-list item's XSQLVAR structure to the desired SQL macro data type constant. For example, the following statement assumes that var is a pointer to an XSQLVAR structure, and that it contains an SQL_SHORT data type to convert to SQL_LONG:

var->sqltype = SQL_LONG;

Note:	Do not coerce a larger data type to a smaller one. Data can be lost in such a translation.

Setting a NULL Indicator

If a parameter or select-list item contains a NULL value, the sqlind field should be used to indicate its NULL status. Appropriate storage space must be allocated for sqlind before values can be stored there.

Before insertion, set sqlind to -1 to indicate that NULL values are legal. Otherwise, set sqlind to 0.

After selection, an sqlind of -1 indicates a field contains a NULL value. Other values indicate a field contains non-NULL data.


Aligning Numerical Data

Ordinarily, when a variable with a numeric data type is created, the compiler will ensure that the variable is stored at a properly aligned address, but when numeric data is stored in a dynamically allocated buffer space, such as can be pointed to by the XSQLDA and XSQLVAR structures, the programmer must take precautions to ensure that the storage space is properly aligned.

Certain platforms, in particular those with RISC processors, require that numerical data in dynamically allocated storage structures be aligned properly in memory. Alignment is dependent both on data type and platform.

For example, a short integer on a Sun SPARCstation must be located at an address divisible by 2, while a long on the same platform must be located at an address divisible by 4. In most cases, a data item is properly aligned if the address of its starting byte is divisible by the correct alignment number. Consult specific system and compiler documentation for alignment requirements.

A useful rule of thumb is that the size of a data type is always a valid alignment number for the data type. For a given type T, if size of (T) equals n, then addresses divisible by n are correctly aligned for T. The following macro expression can be used to align data:

#define ALIGN(ptr, n) ((ptr + n - 1) & ~(n - 1))

where ptr is a pointer to char.

The following code illustrates how the ALIGN macro might be used:

char *buffer_pointer, *next_aligned;

next_aligned = ALIGN(buffer_pointer, sizeof(T));


DSQL Programming Methods

There are four possible DSQL programming methods for handling an SQL statement string. The best method for processing a string depends on the type of SQL statement in the string, and whether or not it contains placeholders for parameters. The following decision table explains how to determine the appropriate processing method for a given string:

Is it a query?	Does it have placeholders?	Processing method to use:
No	No	Method 1
No	Yes	Method 2
Yes	No	Method 3
Yes	Yes	Method 4


Method 1: Non-query Statements Without Parameters

There are two ways to process an SQL statement string containing a non-query statement without placeholder parameters:

Use isc_dsql_execute_immediate() to prepare and execute the string a single time.

Use isc_dsql_allocate_statement() to allocate a statement string for the statement to execute, isc_dsql_prepare() to parse the statement for execution and assign it a name, then use isc_dsql_execute() to carry out the statement's actions as many times as required in an application.

Using isc_dsql_execute_immediate( )

To execute a statement string a single time, use isc_dsql_execute_immediate():

1.	Elicit a statement string from the user or create one that contains the SQL statement to be processed. For example, the following statement creates an SQL statement string:

char *str = "UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05";

2.	Parse and execute the statement string using isc_dsql_execute_immediate():

isc_dsql_execute_immediate(status_vector, &db1, &trans, 0, str, 1, NULL);

isc_dsql_execute_immediate() also accepts string literals. For example,

isc_dsql_execute_immediate(status_vector, &db1, &trans, 0,
"UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05", 1, NULL);

Using isc_dsql_prepare( ) and isc_dsql_execute( )

To execute a statement string several times, use isc_dsql_allocate_statement(), isc_dsql_prepare(), and isc_dsql_execute():

1.	Elicit a statement string from the user or create one that contains the SQL statement to be processed. For example, the following statement creates an SQL statement string:

char *str = "UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05";

2.	Declare and initialize an SQL statement handle, then allocate it with isc_dsql_allocate_statement():

isc_stmt_handle stmt; /* Declare a statement handle. */

stmt = NULL; /* Set handle to NULL before allocation. */

. . .

isc_dsql_allocate_statement(status_vector, &db1, &stmt);

3.	Parse the statement string with isc_dsql_prepare(). This sets the statement handle (stmt) to refer to the parsed format. The statement handle is used in subsequent calls to isc_dsql_execute():

isc_dsql_prepare(status_vector, &trans, &stmt, 0, str, 1, NULL);

isc_dsql_prepare() also accepts string literals. For example,

isc_dsql_prepare(status_vector, &trans, &stmt, 0,
"UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05", 1, NULL);

4.	Execute the named statement string using isc_dsql_execute(). For example, the following statement executes a statement string named stmt:

isc_dsql_execute(status_vector, &trans, &stmt, 1, NULL);

Note:	Once a statement string is prepared, it can be executed as many times as required in an application.


Method 2: Non-query Statements With Parameters

There are two steps to processing an SQL statement string containing a non-query statement with placeholder parameters:

1.	Creating an input XSQLDA to process a statement string's parameters.

2.	Preparing and executing the statement string with its parameters.

Creating the Input XSQLDA

Placeholder parameters are replaced with actual data before a prepared SQL statement string is executed. Because those parameters are unknown when the statement string is created, an input XSQLDA must be created to supply parameter values at execute time. To prepare the XSQLDA, follow these steps:

1.	Declare a variable to hold the XSQLDA needed to process parameters. For example, the following declaration creates an XSQLDA called in_sqlda:

XSQLDA *in_sqlda;

2.	Optionally declare a variable for accessing the XSQLVAR structure of the XSQLDA:

XSQLVAR *var;

Declaring a pointer to the XSQLVAR structure is not necessary, but can simplify referencing the structure in subsequent statements.

3.	Allocate memory for the XSQLDA using the XSQLDA_LENGTH macro. The following statement allocates storage for in_sqlda:

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));

In this statement space for 10 XSQLVAR structures is allocated, allowing the XSQLDA to accommodate up to 10 parameters.

4.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field to indicate the number of XSQLVAR structures allocated:

in_sqlda->version = SQLDA_VERSION1;

in_sqlda->sqln = 10;

Preparing and Executing a Statement String With Parameters

After an XSQLDA is created for holding a statement string's parameters, the statement string can be created and prepared. Local variables corresponding to the placeholder parameters in the string must be assigned to their corresponding sqldata fields in the XSQLVAR structures.

To prepare and execute a non-query statement string with parameters, follow these steps:

1.	Elicit a statement string from the user or create one that contains the SQL statement to be processed. For example, the following statement creates an SQL statement string with placeholder parameters:

char *str = "UPDATE DEPARTMENT SET BUDGET = ?, LOCATION = ?";

This statement string contains two parameters: a value to be assigned to the BUDGET column and a value to be assigned to the LOCATION column.

2.	Declare and initialize an SQL statement handle, then allocate it with isc_dsql_allocate():

isc_stmt_handle stmt; /* Declare a statement handle. */

stmt = NULL; /* Set handle to NULL before allocation. */

. . .

isc_dsql_allocate_statement(status_vector, &db1, &stmt);

3.	Parse the statement string with isc_dsql_prepare().  This sets the statement handle (stmt) to refer to the parsed format. The statement handle is used in subsequent calls to isc_dsql_describe_bind() and isc_dsql_execute():

isc_dsql_prepare(status_vector, &trans, &stmt, 0, str, 1, in_sqlda);

4.	Use isc_dsql_describe_bind() to fill the input XSQLDA with information about the parameters contained in the SQL statement:

isc_dsql_decribe_bind(status_vector, &stmt, 1, in_sqlda);

5.	Compare the value of the sqln field of the XSQLDA to the value of the sqld field to make sure enough XSQLVARs are allocated to hold information about each parameter. sqln should be at least as large as sqld. If not, free the storage previously allocated to the input descriptor, reallocate storage to reflect the number of parameters specified by sqld, reset sqln and version, then execute isc_dsql_describe_bind() again:

if (in_sqlda->sqld > in_sqlda->sqln)

{

n = in_sqlda->sqld;

free(in_sqlda);

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));

in_sqlda->sqln = n

in_sqlda->version = SQLDA_VERSION1;

isc_dsql_describe_bind(status_vector, &stmt, 1, in_sqlda);

}

6.	Process each XSQLVAR parameter structure in the XSQLDA. Processing a parameter structure involves up to four steps:

Coercing a parameter's data type (optional).

Allocating local storage for the data pointed to by the sqldata field of the XSQLVAR. This step is only required if space for local variables is not allocated until run time. The following example illustrates dynamic allocation of local variable storage space.

Providing a value for the parameter consistent with its data type (required).

Providing a NULL value indicator for the parameter.

The following code example illustrates these steps, looping through each XSQLVAR structure in the in_sqlda XSQLDA:

for (i=0, var = in_sqlda->sqlvar; i < in_sqlda->sqld; i++, var++)

{

/* Process each XSQLVAR parameter structure here.

The parameter structure is pointed to by var. */

dtype = (var->sqltype & ~1) /* drop NULL flag for now */

switch(dtype)

{

case SQL_VARYING: /* coerce to SQL_TEXT */

var->sqltype = SQL_TEXT;

/* allocate local variable storage */

var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);

. . .

break;

case SQL_TEXT:

var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);

/* provide a value for the parameter */

. . .

break;

case SQL_LONG:

var->sqldata = (char *)malloc(sizeof(long));

/* provide a value for the parameter */

*(long *)(var->sqldata) = 17;

break;

. . .

}	/* end of switch statement */

if (sqltype & 1)

{

/* allocate variable to hold NULL status */

var->sqlind = (short *)malloc(sizeof(short));

}

}	/* end of for loop */

1.	Execute the named statement string with isc_dsql_execute(). For example, the following statement executes a statement string named stmt:

isc_dsql_execute(status_vector, &trans, &stmt, 1, in_sqlda);

Re-executing the Statement String

Once a non-query statement string with parameters is prepared, it can be executed as often as required in an application. Before each subsequent execution, the input XSQLDA can be supplied with new parameter and NULL indicator data.


Method 3: Query Statements Without Parameters

There are three steps to processing an SQL query statement string without parameters:

1.	Preparing an output XSQLDA to process the select-list items returned when the query is executed.

2.	Preparing the statement string.

3.	Using a cursor to execute the statement and retrieve select-list items from the output XSQLDA.

Preparing the Output XSQLDA

Most queries return one or more rows of data, referred to as a select-list. Because the number and kind of items returned are unknown when a statement string is created, an output XSQLDA must be created to store select-list items that are returned at run time. To prepare the XSQLDA, follow these steps:

1.	Declare a variable to hold the XSQLDA needed to store the column data for each row that will be fetched. For example, the following declaration creates an XSQLDA called out_sqlda:

XSQLDA *out_sqlda;

2.	Optionally declare a variable for accessing the XSQLVAR structure of the XSQLDA:

XSQLVAR *var;

Declaring a pointer to the XSQLVAR structure is not necessary, but can simplify referencing the structure in subsequent statements.

3.	Allocate memory for the XSQLDA using the XSQLDA_LENGTH macro. The following statement allocates storage for out_sqlda:

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));

Space for 10 XSQLVAR structures is allocated in this statement, enabling the XSQLDA to accommodate up to 10 select-list items.

4.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field of the XSQLDA to indicate the number of XSQLVAR structures allocated:

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 10;

Preparing a Query Statement String Without Parameters

After an XSQLDA is created for holding the items returned by a query statement string, the statement string can be created, prepared, and described. When a statement string is executed, InterBase creates the select-list of selected rows.

To prepare a query statement string, follow these steps:

1.	Elicit a statement string from the user or create one that contains the SQL statement to be processed. For example, the following statement creates an SQL statement string that performs a query:

char *str = "SELECT * FROM CUSTOMER";

The statement appears to have only one select-list item (*). The asterisk is a wildcard symbol that stands for all of the columns in the table, so the actual number of items returned equals the number of columns in the table.

2.	Declare and initialize an SQL statement handle, then allocate it with isc_dsql_allocate():

isc_stmt_handle stmt; /* Declare a statement handle. */

stmt = NULL; /* Set handle to NULL before allocation. */

. . .

isc_dsql_allocate_statement(status_vector, &db1, &stmt);

3.	Parse the statement string with isc_dsql_prepare().  This sets the statement handle (stmt) to refer to the parsed format. The statement handle is used in subsequent calls to statements such as isc_dsql_describe() and isc_dsql_execute():

isc_dsql_prepare(status_vector, &trans, &stmt, 0, str, 1, NULL);

4.	Use isc_dsql_describe() to fill the output XSQLDA with information about the select-list items returned by the statement:

isc_dsql_describe(status_vector, &trans, &stmt, out_sqlda);

5.	Compare the sqln field of the XSQLDA to the sqld field to determine if the output descriptor can accommodate the number of select-list items specified in the statement. If not, free the storage previously allocated to the output descriptor, reallocate storage to reflect the number of select-list items specified by sqld, reset sqln and version, then execute isc_dsql_describe() again:

if (out_sqlda->sqld > out_sqlda->sqln)

{

n = out_sqlda->sqld;

free(out_sqlda);

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));

out_sqlda->sqln = n;

out_sqlda->version = SQLDA_VERSION1;

isc_dsql_describe(status_vector, &trans, 1, out_sqlda);

}

6.	Set up an XSQLVAR structure for each item returned. Setting up an item structure involves the following steps:

Coercing an item's data type (optional).

Allocating local storage for the data pointed to by the sqldata field of the XSQLVAR. This step is only required if space for local variables is not allocated until run time. The following example illustrates dynamic allocation of local variable storage space.

Providing a NULL value indicator for the parameter.

The following code example illustrates these steps, looping through each XSQLVAR structure in the out_sqlda XSQLDA:

for (i=0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++)

{

dtype = (var->sqltype & ~1) /* drop flag bit for now */

switch(dtype)

{

case SQL_VARYING:

var->sqltype = SQL_TEXT;

var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2);

break;

case SQL_TEXT:

var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);

break;

case 	SQL_LONG:

var->sqldata = (char *)malloc(sizeof(long));

break;

. . .

/* process remaining types */

}	/* end of switch statements */

if (sqltype & 1)

{

/* allocate variable to hold NULL status */

var->sqlind = (short *)malloc(sizeof(short));

}

}	/* end of for loop */

Executing a Statement String Within the Context of a Cursor

To retrieve select-list items from a prepared statement string, the string can be executed within the context of a cursor. All cursor declarations in InterBase are fixed statements inserted into the application before it is compiled. DSQL application developers must anticipate the need for cursors when writing the application and declare them ahead of time.

A cursor is only needed to process positioned UPDATE and DELETE statements made against the rows retrieved by isc_dsql_fetch() for SELECT statements that specify an optional FOR UPDATE OF clause.

The following descriptions apply to the situations when a cursor is needed.

A looping construct is used to fetch a single row at a time from the cursor and to process each select-list item (column) in that row before the next row is fetched.

To execute a statement string within the context of a cursor and retrieve rows of select-list items, follow these steps:

1.	Execute the prepared statement with isc_dsql_execute():

isc_dsql_execute(status_vector, &trans, &stmt, 1, NULL);

2.	Declare and open a cursor for the statement string with isc_dsql_set_cursor_name(). For example, the following statement declares a cursor, dyn_cursor, for the SQL statement string, stmt:

isc_dsql_set_cursor_name(status_vector, &stmt, "dyn_cursor", NULL);

Opening the cursor causes the statement string to be executed, and an active set of rows to be retrieved.

3.	Fetch one row at a time and process the select-list items (columns) it contains with isc_dsql_fetch(). For example, the following loops retrieve one row at a time from dyn_cursor and process each item in the retrieved row with an application-specific function called process_column():

while ((fetch_stat =

isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda))

== 0)

{

for (i = 0; i < out_sqlda->sqld; i++)

{

process_column(sqlda->sqlvar[i]);

}

}

if (fetch_stat != 100L)

{

/* isc_dsql_fetch returns 100 if no more rows remain to be

retrieved */

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

return(1);

}

The process_column() function mentioned in this example processes each returned select-list item. The following skeleton code illustrates how such a function can be set up:

void process_column(XSQLVAR *var)

{

/* test for NULL value */

if ((var->sqltype & 1) && (*(var->sqlind) = -1))

{

/* process the NULL value here */

}

else

{

/* process the data instead */

}

. . .

}

4.	When all the rows are fetched, close the cursor with isc_dsql_free_statement():

isc_dsql_free_statement(status_vector, &stmt, DSQL_close);

Re-executing a Query Statement String Without Parameters

Once a query statement string without parameters is prepared, it can be executed as often as required in an application by closing and reopening its cursor.


Method 4: Query Statements With Parameters

There are four steps to processing an SQL query statement string with placeholder parameters:

1.	Preparing an input XSQLDA to process a statement string's parameters.

2.	Preparing an output XSQLDA to process the select-list items returned when the query is executed.

3.	Preparing the statement string and its parameters.

4.	Using a cursor to execute the statement using input parameter values from an input XSQLDA, and to retrieve select-list items from the output XSQLDA.

Preparing the Input XSQLDA

Placeholder parameters are replaced with actual data before a prepared SQL statement string is executed. Because those parameters are unknown when the statement string is created, an input XSQLDA must be created to supply parameter values at run time. To prepare the XSQLDA, follow these steps:

1.	Declare a variable to hold the XSQLDA needed to process parameters. For example, the following declaration creates an XSQLDA called in_sqlda:

XSQLDA *in_sqlda;

2.	Optionally declare a variable for accessing the XSQLVAR structure of the XSQLDA:

XSQLVAR *var;

Declaring a pointer to the XSQLVAR structure is not necessary, but can simplify referencing the structure in subsequent statements.

3.	Allocate memory for the XSQLDA using the XSQLDA_LENGTH macro. The following statement allocates storage for in_slqda:

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));

Note:	In this statement, space for 10 XSQLVAR structures is allocated, allowing the XSQLDA to accommodate up to 10 input parameters. Once structures are allocated, assign values to the sqldata fields.

4.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field of the XSQLDA to indicate the number of XSQLVAR structures allocated:

in_sqlda->version = SQLDA_VERSION1;

in_sqlda->sqln = 10;

Preparing the Output XSQLDA

Most queries return one or more rows of data, referred to as a select-list. Because the number and kind of items returned are unknown when a statement string is executed, an output XSQLDA must be created to store select-list items that are returned at run time. To prepare the XSQLDA, follow these steps:

1.	Declare a variable to hold the XSQLDA needed to process parameters. For example, the following declaration creates an XSQLDA called out_sqlda:

XSQLDA *out_sqlda;

2.	Optionally declare a variable for accessing the XSQLVAR structure of the XSQLDA:

XSQLVAR *var;

Declaring a pointer to the XSQLVAR structure is not necessary, but can simplify referencing the structure in subsequent statements.

3.	Allocate memory for the XSQLDA using the XSQLDA_LENGTH macro. The following statement allocates storage for out_sqlda:

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));

Space for 10 XSQLVAR structures is allocated in this statement, enabling the XSQLDA to accommodate up to 10 select-list items.

4.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field of the XSQLDA to indicate the number of XSQLVAR structures allocated:

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 10;

Preparing a Query Statement String With Parameters

After an input and an output XSQLDA are created for holding a statement string's parameters, and the select-list items returned when the statement is executed, the statement string can be created and prepared. When a statement string is prepared, InterBase replaces the placeholder parameters in the string with information about the actual parameters used. The information about the parameters must be assigned to the input XSQLDA (and perhaps adjusted) before the statement can be executed. When the statement string is executed, InterBase stores select-list items in the output XSQLDA.

To prepare a query statement string with parameters, follow these steps:

1.	Elicit a statement string from the user or create one that contains the SQL statement to be processed. For example, the following statement creates an SQL statement string with placeholder parameters:

char *str = "SELECT * FROM DEPARTMENT WHERE BUDGET = ?, LOCATION = ?";

This statement string contains two parameters: a value to be assigned to the BUDGET column and a value to be assigned to the LOCATION column.

2.	Declare and initialize an SQL statement handle, then allocate it with isc_dsql_allocate():

isc_stmt_handle stmt; /* Declare a statement handle. */

stmt = NULL; /* Set handle to NULL before allocation. */

. . .

isc_dsql_allocate_statement(status_vector, &db1, &stmt);

3.	Prepare the statement string with isc_dsql_prepare().  This sets the statement handle (stmt) to refer to the parsed format. The statement handle is used in subsequent calls to isc_dsql_describe(), isc_dsql_describe_bind(), and isc_dsql_execute2():

isc_dsql_prepare(status_vector, &trans, &stmt, 0, str, 1, out_xsqlda);

4.	Use isc_dsql_describe_bind() to fill the input XSQLDA with information about the parameters contained in the SQL statement:

isc_dsql_decribe_bind(status_vector, &stmt, 1, in_xsqlda);

5.	Compare the sqln field of the XSQLDA to the sqld field to determine if the input descriptor can accommodate the number of parameters contained in the statement. If not, free the storage previously allocated to the input descriptor, reallocate storage to reflect the number of parameters specified by sqld, reset sqln and version, then execute isc_dsql_describe_bind() again:

if (in_sqlda->sqld > in_sqlda->sqln)

{

n = in_sqlda->sqld;

free(in_sqlda);

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));

in_sqlda->sqln = n;

in_sqlda->version = SQLDA_VERSION1;

isc_dsql_decribe_bind(status_vector, &stmt, 1, in_xsqlda);

}

6.	Process each XSQLVAR parameter structure in the input XSQLDA. Processing a parameter structure involves up to four steps:

Coercing a parameter's data type (optional).

Allocating local storage for the data pointed to by the sqldata field of the XSQLVAR. This step is only required if space for local variables is not allocated until run time. The following example illustrates dynamic allocation of local variable storage space.

Providing a value for the parameter consistent with its data type (required).

Providing a NULL value indicator for the parameter.

These steps must be followed in the order presented. The following code example illustrates these steps, looping through each XSQLVAR structure in the in_sqlda XSQLDA:

for (i=0, var = in_sqlda->sqlvar; i < in_sqlda->sqld; i++, var++)

{

/* Process each XSQLVAR parameter structure here.

The parameter structure is pointed to by var.*/

dtype = (var->sqltype & ~1) /* drop flag bit for now */

switch(dtype)

{

case SQL_VARYING: /* coerce to SQL_TEXT */

var->sqltype = SQL_TEXT;

/* allocate proper storage */

var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);

/* Provide a value for the parameter. See case SQL_LONG. */

. . .

break;

case SQL_TEXT:

var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);

/* Provide a value for the parameter. See case SQL_LONG. */

. . .

break;

case SQL_LONG:

var->sqldata = (char *)malloc(sizeof(long));

/* Provide a value for the parameter. */

*(long *)(var->sqldata) = 17;

break;

. . .

}	/* end of switch statement */

if (sqltype & 1)

{

/* allocate variable to hold NULL status */

var->sqlind = (short *)malloc(sizeof(short));

}

}	/* end of for loop */

7.	Use isc_dsql_describe() to fill the output XSQLDA with information about the select-list items returned by the statement:

isc_dsql_describe(status_vector, &trans, &stmt, out_xsqlda);

8.	Compare the sqln field of the XSQLDA to the sqld field to determine if the output descriptor can accommodate the number of select-list items specified in the statement. If not, free the storage previously allocated to the output descriptor, reallocate storage to reflect the number of select-list items specified by sqld, reset sqln and version, and execute DESCRIBE OUTPUT again:

if (out_sqlda->sqld > out_sqlda->sqln)

{

n = out_sqlda->sqld;

free(out_sqlda);

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));

out_sqlda->sqln = n;

out_sqlda->version = SQLDA_VERSION1;

isc_dsql_describe(status_vector, &trans, &stmt, out_xsqlda);

}

9.	Set up an XSQLVAR structure for each item returned. Setting up an item structure involves the following steps:

Coercing an item's data type (optional).

Allocating local storage for the data pointed to by the sqldata field of the XSQLVAR. This step is only required if space for local variables is not allocated until run time. The following example illustrates dynamic allocation of local variable storage space.

Providing a NULL value indicator for the parameter (optional).

The following code example illustrates these steps, looping through each XSQLVAR structure in the out_sqlda XSQLDA:

for (i=0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++)

{

dtype = (var->sqltype & ~1) /* drop flag bit for now */

switch(dtype)

{

case SQL_VARYING:

var->sqltype = SQL_TEXT;

break;

case SQL_TEXT:

var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);

break;

case 	SQL_LONG:

var->sqldata = (char *)malloc(sizeof(long));

break;

/* process remaining types */

}	/* end of switch statements */

if (sqltype & 1)

{

/* allocate variable to hold NULL status */

var->sqlind = (short *)malloc(sizeof(short));

}

}	/* end of for loop */

Executing a Query Statement String Within the Context of a Cursor

To retrieve select-list items from a statement string, the string must be executed within the context of a cursor. All cursor declarations in InterBase are fixed, embedded statements inserted into the application before it is compiled. DSQL application developers must anticipate the need for cursors when writing the application and declare them ahead of time.

A looping construct is used to fetch a single row at a time from the cursor and to process each select-list item (column) in that row before the next row is fetched.

To execute a statement string within the context of a cursor and retrieve rows of select-list items, follow these steps:

1.	Execute the statement with isc_dsql_execute2():

isc_dsql_execute2(status_vector, &trans, &stmt, 1, in_xsqlda, out_xsqlda);

2.	Declare and open a cursor for the statement string with isc_dsql_set_cursor_name(). For example, the following statement declares a cursor, dyn_cursor, for the prepared SQL statement string, stmt:

isc_dsql_set_cursor_name(status_vector, &stmt, "dyn_cursor", NULL);

Note:	Opening the cursor causes the statement string to be executed, and an active set of rows to be retrieved.

3.	Fetch one row at a time with isc_dsql_fetch() and process the select-list items (columns) it contains. For example, the following loops retrieve one row at a time from dyn_cursor and process each item in the retrieved row with an application-specific function called process_column():

while ((fetch_stat =

isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda))

== 0)

{

for (i = 0; i < out_sqlda->sqld; i++)

{

process_column(sqlda->sqlvar[i]);

}

}

if (fetch_stat != 100L)

{

/* isc_dsql_fetch returns 100 if no more rows remain to be

retrieved */

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

return(1);

}

4.	When all the rows are fetched, close the cursor with isc_dsql_free_statement():

isc_dsql_free_statement(status_vector, &stmt, DSQL_close);

Re-executing a Query Statement String With Parameters

Once a query statement string with parameters is prepared, it can be used as often as required in an application. Before each subsequent use, the input XSQLDA can be supplied with new parameter and NULL indicator data. The cursor must be closed and reopened before processing can occur.


Determining an Unknown Statement Type At Run Time

An application can use isc_dsql_sql_info() to determine the statement type of an unknown prepared statement, for example, a statement entered by the user at run time.

Requested information can include:

Statement type.

Number of input parameters required by the statement.

Number of output values returned by the statement.

Detailed information regarding each input parameter or output value, including its data type, scale, and length.

To use isc_dsql_sql_info(), allocate an item-list buffer that describes the type of information requested, and allocate a result buffer, where the function can return the desired information. For example, to determine the statement type of an unknown, but prepared statement, you would allocate a one-element item-list buffer, and fill it with the macro constant, isc_info_sql_stmt_type, defined in ibase.h:

char type_item[];

type_item[] = {isc_info_sql_stmt_type};

Additional information item macros for requested items can be found in ibase.h under the comment, "SQL information items."

The result buffer must be large enough to contain any data returned by the call. The proper size for this buffer depends on the information requested. If not enough space is allocated, then isc_dsql_sql_info() puts the predefined value, isc_info_truncated, in the last byte of the result buffer. Generally, when requesting statement type information, 8 bytes is a sufficient buffer size. Declaring a larger than necessary buffer is also safe. A request to identify a statement type returns the following information in the result buffer:

1.	One byte containing isc_info_sql_stmt_type.

2.	Two bytes containing a number, n, telling how many bytes compose the subsequent value.

3.	One or two bytes specifying the statement type. The following table lists the statement types that can be returned:

Type	Numeric Value
isc_info_sql_stmt_select	1
isc_info_sql_stmt_insert	2
isc_info_sql_stmt_update	3
isc_info_sql_stmt_delete	4
isc_info_sql_stmt_ddl	5
isc_info_sql_stmt_get_segment	6
isc_info_sql_stmt_put_segment	7
isc_info_sql_stmt_exec_procedure	8
isc_info_sql_stmt_start_trans	9
isc_info_sql_stmt_commit	10
isc_info_sql_stmt_rollback	11
isc_info_sql_stmt_select_for_upd	12

4.	A final byte containing the value isc_info_end (0).

The values placed in the result buffer are not aligned. Furthermore, all numbers are represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers have the sign in the last byte. Convert the numbers to a data type native to your system before interpreting them.

Note:	All information about a statement except its type can be more easily determined by calling functions other than isc_dsql_sql_info(). For example, to determine the information to fill in an input XSQLDA, call isc_dsql_describe_bind(). To fill in an output XSQLDA, call isc_dsql_prepare() or isc_dsql_describe().


Working With BLOB Data

This section of the Help file describes the binary large object (BLOB) data type and how to work with it using API functions.

Warning:	BLOB filters are not available on NetWare servers.

The following is a list of API functions for working with BLOB data.

Function	Purpose
isc_blob_default_desc()	Loads a BLOB descriptor with default information about a BLOB, including its subtype, character set, and segment size.
isc_blob_gen_bpb()	Generates a BLOB parameter buffer (BPB) from source and target BLOB descriptors to allow dynamic access to BLOB subtype and character set information.
isc_blob_info()	Returns information about an open BLOB.
isc_blob_lookup_desc()	Determines the subtype, character set, and segment size of a BLOB, given a table name and BLOB column name.
isc_blob_set_desc()	Initializes a BLOB descriptor from parameters passed to it.
isc_cancel_blob()	Discards a BLOB.
isc_close_blob()	Closes an open BLOB.
isc_create_blob2()	Creates and opens a BLOB for write access, and optionally specifies a filter to be used to translate the BLOB from one subtype to another.
isc_get_segment()	Retrieves data from a BLOB column in a row returned by execution of a SELECT statement.
isc_open_blob2()	Opens an existing BLOB for retrieval, and optionally specifies a filter to be used to translate the BLOB from one subtype to another.
isc_put_segment()	Writes data into a BLOB.


What Is a BLOB?

A BLOB is a binary large object that cannot easily be stored in a database as one of the standard data types. You can use a BLOB to store large amounts of data of various types, including:

Bitmapped images

Sounds

Video segments

Text

InterBase support of BLOB data provides all the advantages of a database management system, including transaction control, maintenance, and access using standard API function calls. BLOB data is stored in the database itself. Other systems only store pointers in the database to non-database files. InterBase stores the actual BLOB data in the database, and establishes a unique identification handle in the appropriate table to point to the database location of the BLOB. By maintaining the BLOB data within the database, InterBase greatly improves access to and management of the data.


How Are BLOB Data Stored?

BLOB is the InterBase data type that can represent various objects, such as bitmapped images, sound, video, and text. Before you store these items in the database, you create or manage them as platform- or product-specific files or data structures, such as:

TIFF, PICT, .BMP, .WMF, .GEM, TARGA or other bitmapped or vector-graphic files

MIDI or .WAV sound files

Audio Video Interleaved Format (.AVI) or QuickTime video files

ASCII, .MIF, .DOC, .WPx or other text files

CAD files

You must programmatically load these files from memory into the database, as you do any other data items or records you intend to store in InterBase.


BLOB Subtypes

Although you manage BLOB data in ways similar to other data types, InterBase provides more flexible data typing rules for BLOB data. Because there are many native data types that you can define as BLOB data, InterBase treats them generically and allows you to define your own data type, known as a subtype. InterBase also provides two predefined subytpes: 0, an unstructured subtype generally applied to binary data or data of an indeterminate type, and 1, applied to plain text.

User-defined subtypes must always be represented as negative integers between -1 and -32,678.

A BLOB column's subtype is specified when the BLOB column is defined.

The application is responsible for ensuring that data stored in a BLOB column agrees with its subtype; InterBase does not check the type or format of BLOB data.


BLOB Database Storage

Rather than storing BLOB data directly in the BLOB field of a table record, InterBase stores a BLOB ID there. A BLOB ID is a unique numeric value that references BLOB data. The BLOB data is stored elsewhere in the database, in a series of BLOB segments, units of BLOB data read and written in chunks. BLOB segments can be of varying length. The length of an individual segment is specified when it is written.

Segments are handy when working with data that is too large for one application memory buffer. But it is not necessary to use multiple segments; you can put all your BLOB data in a single segment.

When an application creates a BLOB, it must write data to it a segment at a time. When an application reads a BLOB, it reads a segment at a time.


BLOB Data Operations

InterBase supports the following operations on BLOB data:

Reading from a BLOB

Writing to a BLOB, which involves the following operations:

Inserting a new row that includes BLOB data.

Replacing the data referenced by a BLOB column of a row.

Updating the data referenced by a BLOB column of a row.

Deleting a BLOB

The following sections describe how to perform these operations. These examples do not include the use of filters to convert data from one subtype to another as it is read or written.

Dynamic SQL (DSQL) API functions and the XSQLDA data structure are needed to execute SELECT, INSERT, and UPDATE statements required to select, insert, or update relevant BLOB data. The following sections include descriptions of the DSQL programming methods required to execute the sample statements provided.


Reading Data From a BLOB

There are six steps required for reading data from an existing BLOB:

1.	Creating a SELECT statement query that specifies selection of the BLOB column (and any other columns desired) in the rows of interest.

2.	Preparing an output XSQLDA structure to hold the column data for each row that is fetched.

3.	Preparing the SELECT statement for execution.

4.	Executing the statement.

5.	Fetching the selected rows one by one.

6.	Reading and processing the BLOB data from each row.

Creating the SELECT Statement

Elicit a statement string from the user or create one that consists of the SQL query that will select rows containing the BLOB data of interest. For example, the following creates an SQL query statement string that selects three columns from various rows in the PROJECT table:

char *str =
"SELECT PROJ_NAME, PROJ_DESC, PRODUCT FROM PROJECT WHERE \
PRODUCT IN ("software", "hardware", "other") ORDER BY PROJ_NAME";

Preparing the Output XSQLDA

Most queries return one or more rows of data, referred to as a select-list. An output XSQLDA must be created to store the column data for each row that is fetched. For a BLOB column, the column data is an internal BLOB identifier (BLOB ID) that is needed to access the actual data. To prepare the XSQLDA, follow these steps:

1.	Declare a variable to hold the XSQLDA. For example, the following declaration creates an XSQLDA called out_sqlda:

XSQLDA *out_sqlda;

2.	Allocate memory for the XSQLDA using the XSQLDA_LENGTH macro. The XSQLDA must contain one XSQLVAR substructure for each column to be fetched. The following statement allocates storage for an output XSQLDA (out_sqlda) with three XSQLVAR substructures:

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(3));

3.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field of the XSQLDA to indicate the number of XSQLVAR substructures allocated:

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 3;

Preparing the SELECT Statement for Execution

After an XSQLDA is created for holding the column data for each selected row, the query statement string can be prepared for execution. Follow these steps:

1.	Declare and initialize an SQL statement handle, then allocate it with isc_dsql_allocate():

isc_stmt_handle stmt; 	/* Declare a statement handle. */

stmt = NULL; 	/* Set handle to NULL before allocation. */

isc_dsql_allocate_statement(status_vector, &db_handle, &stmt);

2.	Ready the statement string for execution with isc_dsql_prepare(). This checks the string (str) for syntax errors, parses it into a format that can be efficiently executed, and sets the statement handle (stmt) to refer to this parsed format. The statement handle is used in a later call to isc_dsql_execute().

If isc_dsql_prepare() is passed a pointer to the output XSQLDA, as in the following example, it will fill in most fields of the XSQLDA and all its XSQLVAR substructures with information such as the data type, length, and name of the corresponding columns in the statement.

A sample call to isc_dsql_prepare() is:

isc_dsql_prepare(

status_vector,

&trans,	/* Set by previous isc_start_transaction() call. */

&stmt,	/* Statement handle set by this function call. */

0,	/* Specifies statement string is null-terminated. */

str,	/* Statement string. */

1,	/* XSQLDA version number. */

out_sqlda	/* XSQLDA for storing column data. */

);

3.	Set up an XSQLVAR structure for each column. Setting up an XSQLVAR structure involves the following steps:

For columns whose types are known at compile time:

Specify the column's data type (if it was not set by isc_dsql_prepare(), as previously described).

Point the sqldata field of the XSQLVAR to an appropriate local variable.

For columns whose types are not known until run time:

Coerce the item's data type (optional), for example, from SQL_VARYING to SQL_TEXT.

Dynamically allocate local storage for the data pointed to by the sqldata field of the XSQLVAR.

For both:

Specify the number of bytes of data to be retrieved into sqldata.

 Provide a NULL value indicator for the parameter.

 Data retrieval for BLOB (and array) columns is different from other types of columns, so the XSQLVAR fields must be set differently. For non-BLOB (and non-array) columns, isc_dsql_prepare() sets each XSQLVAR sqltype field to the appropriate field type, and the data retrieved when a select-list row's data is fetched is placed into the sqldata space allocated for the column. For BLOB columns, the type must be set to SQL_BLOB (or SQL_BLOB + 1 if a NULL indicator is desired). InterBase stores the internal BLOB identifier (BLOB ID), not the BLOB data, in the sqldata space when a row's data is fetched, so you must point sqldata to an area the size of a BLOB ID.

The following code example illustrates the assignments for BLOB and non-BLOB columns whose types are known at compile time.

#define PROJLEN 20

#define TYPELEN 12

ISC_QUAD blob_id;

char proj_name[PROJLEN + 1];

char prod_type[TYPELEN + 1];

short flag0, flag1, flag2;

out_sqlda->sqlvar[0].sqldata = proj_name;

out_sqlda->sqlvar[0].sqltype = SQL_TEXT + 1;

out_sqlda->sqlvar[0].sqllen = PROJLEN;

out_sqlda->sqlvar[0].sqlind = &flag0;

out_sqlda->sqlvar[1].sqldata = (char *) &blob_id;

out_sqlda->sqlvar[1].sqltype = SQL_BLOB + 1;

out_sqlda->sqlvar[1].sqllen = sizeof(ISC_QUAD);

out_sqlda->sqlvar[1].sqlind = &flag1;

out_sqlda->sqlvar[2].sqldata =  prod_type;

out_sqlda->sqlvar[2].sqltype = SQL_TEXT + 1;

out_sqlda->sqlvar[2].sqllen = S TYPELEN;

out_sqlda->sqlvar[2].sqlind = &flag2;

Executing the Statement

Once the query statement string is prepared, it can be executed:

isc_dsql_execute(

status_vector,

&trans,	/* set by previous isc_start_transaction() call */

&stmt,	/* allocated above by isc_dsql_allocate_statement() */

1,	/* XSQLDA version number */

NULL	/* NULL since stmt doesn't have input values */

);

This statement creates a select-list, the rows returned by execution of the statement.

Fetching Selected Rows

A looping construct is used to fetch (into the output XSQLDA) the column data for a single row at a time from the select-list and to process each row before the next row is fetched. Each execution of isc_dsql_fetch() fetches the column data into the corresponding XSQLVAR substructures of out_sqlda. For the BLOB column, the BLOB ID, not the actual BLOB data, is fetched.

ISC_STATUS fetch_stat;

long SQLCODE;

. . .

while ((fetch_stat =

isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda))

== 0)

{

proj_name[PROJLEN] = '\0';

prod_type[TYPELEN] = '\0';

printf("\nPROJECT: %-20s TYPE: %-15s\n\n",

proj_name, prod_type);

/* Read and process the BLOB data (see next section) */

}

if (fetch_stat != 100L)

{

/* isc_dsql_fetch returns 100 if no more rows remain to be

retrieved */

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

return(1);

}

Reading and Processing the BLOB Data

To read and process the BLOB data:

1.	Declare and initialize a BLOB handle:

isc_blob_handle blob_handle; /* Declare a BLOB handle. */

blob_handle = NULL; /* Set handle to NULL before using it */

2.	Create a buffer for holding each BLOB segment as it is read. Its size should be the maximum size segment your program expects to be read from the BLOB.

char blob_segment[80];

3.	Declare an unsigned short variable into which InterBase will store the actual length of each segment read:

unsigned short actual_seg_len;

4.	Open the BLOB with the fetched blob_id:

isc_open_blob2(

status_vector,

&db_handle,

&trans,

&blob_handle,	 /* set by this function to refer to the BLOB */

&blob_id,	/* BLOB ID put into out_sqlda by isc_dsql_fetch() */

0, 	/* BPB length = 0; no filter will be used */

NULL 	/* NULL BPB, since no filter will be used */

);

5.	Read all the BLOB data by calling isc_get_segment() repeatedly to get each BLOB segment and its length. Process each segment read. In the following example, "processing" consists of printing each BLOB as it is read:

blob_stat = isc_get_segment(

status_vector,

&blob_handle,	/* set by isc_open_blob2()*/

&actual_seg_len, 	/* length of segment read */

sizeof(blob_segment), 	/* length of segment buffer */

blob_segment	/* segment buffer */

);

while (blob_stat == 0 || status_vector[1] == isc_segment)

{

/* isc_get_segment returns 0 if a segment was successfully read. */

/* status_vector[1] is set to isc_segment if only part of a */

/* segment was read due to the buffer (blob_segment) not being */

/* large enough. In that case, the following calls to */

/* isc_get_segment() read the rest of the buffer. */

printf("%*.*s", actual_seg_len, actual_seg_len, blob_segment);

blob_stat = isc_get_segment(status_vector, &blob_handle,

&actual_seg_len, sizeof(blob_segment), blob_segment);

printf("\n");

};

printf("\n");

6.	Close the BLOB:

isc_close_blob(status_vector, &blob_handle);


Writing Data to a BLOB

You need to create a new BLOB and write data to it in order to do any of the following:

Include BLOB data in a row to be inserted into a table.

Replace the data referenced by a BLOB column of a row.

Update the data referenced by a BLOB column of a row.

The entry in a BLOB column of a row does not actually contain BLOB data. Rather, it has a BLOB ID referring to the data, which is stored elsewhere. So, to set or modify a BLOB column, you need to set (or reset) the BLOB ID stored in it. If a BLOB column contains a BLOB ID, and you modify the column to refer to a different BLOB (or to contain NULL), the BLOB referenced by the previously stored BLOB ID will be deleted during the next garbage collection.

All these operations require the following steps:

1.	Prepare an appropriate DSQL statement. This will be an INSERT statement if you are inserting a new row into a table, or an UPDATE statement for modifying a row. Each of these statements will need a corresponding input XSQLDA structure for supplying parameter values to the statement at run time. The BLOB ID of a new BLOB will be one of the values passed.

2.	Create a new BLOB, and write data into it.

3.	Associate the BLOB ID of the new BLOB with the BLOB column of the table row by executing the UPDATE or INSERT statement.

Note that you cannot update BLOB data directly. If you want to modify BLOB data, you must:

Create a new BLOB.

Read the old BLOB data into a buffer where you can edit or modify it.

Write the modified data to the new BLOB.

Prepare and execute an UPDATE statement that will modify the BLOB column to contain the BLOB ID of the new BLOB, replacing the old BLOB's BLOB ID.

The sections below describe the steps required to insert, replace, or update BLOB data.

Preparing the UPDATE or INSERT Statement

To prepare an UPDATE or INSERT statement for execution, follow these steps:

1.	Elicit an UPDATE or INSERT statement string from the user or create one for inserting a row or updating the row containing the BLOB column of interest. For example, the following statement is for updating the BLOB column named PROJ_DESC in the row of the table, PROJECT, whose PROJ_ID field contains a value specified at run time:

char *upd_str =

"UPDATE PROJECT SET PROJ_DESC = ? WHERE PROJ_ID = ?";

As an example of an INSERT statement, the following inserts a new row containing values in four columns:

char *in_str = "INSERT INTO PROJECT (PROJ_NAME, PROJ_DESC, PRODUCT, PROJ_ID) VALUES (?, ?, ?, ?)";

The remaining steps refer only to UPDATE statements, but the actions apply to INSERT statements as well.

2.	Declare a variable to hold the input XSQLDA needed to supply parameter values to the UPDATE statement at run time. For example, the following declaration creates an XSQLDA called in_sqlda:

XSQLDA *in_sqlda;

3.	Allocate memory for the input XSQLDA using the XSQLDA_LENGTH macro. The XSQLDA must contain one XSQLVAR substructure for each parameter to be passed to the UPDATE statement. The following statement allocates storage for an input XSQLDA (in_sqlda) with two XSQLVAR substructures:

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

4.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field to indicate the number of XSQLVAR structures allocated:

in_sqlda->version = SQLDA_VERSION1;

in_sqlda->sqln = 2;

5.	Set up the XSQLVAR structure in the XSQLDA for each parameter to be passed. Setting up an XSQLVAR structure involves the following steps:

Specifying the item's data type.

For parameters whose types are known at compile time: Pointing the sqldata field of the XSQLVAR to an appropriate local variable that will contain the data to be passed.

For parameters whose types are not known until run time: Allocating local storage for the data pointed to by the sqldata field of the XSQLVAR.

Specifying the number of bytes of data.

 Data storage for BLOB (and array) columns is different from other types of columns, so the XSQLVAR fields must be set differently. For non-BLOB (and non-array) columns, input parameter data comes from the space pointed to by sql-data. For BLOB columns, you must set the type to SQL_BLOB (or SQL_BLOB + 1 if a NULL indicator is desired). Your application must store space for the internal BLOB identifier, not the BLOB data, in the sqldata space.

The following code example illustrates the assignments for one text column and one BLOB column, where the column types are known at compile time.

#define PROJLEN 5

char proj_id[PROJLEN + 1];

ISC_QUAD blob_id;

in_sqlda->sqlvar[0].sqldata = (char *) &blob_id;

in_sqlda->sqlvar[0].sqltype = SQL_BLOB + 1;

in_sqlda->sqlvar[0].sqllen = sizeof(ISC_QUAD);

in_sqlda->sqlvar[1].sqldata = proj_id;

in_sqlda->sqlvar[1].sqltype = SQL_TEXT;

in_sqlda->sqlvar[1].sqllen = 5;

 The proj_id variable should be assigned a value at run time (unless the value is known at compile time). The blob_id variable should be set to refer to the newly created BLOB, as described in the following sections.

Creating a New BLOB and Storing Data

To create a new BLOB containing the data to be written:

1.	Declare and initialize a BLOB handle:

isc_blob_handle blob_handle; /* Declare a BLOB handle. */

blob_handle = NULL;	/* Set handle to NULL before using it */

2.	Declare and initialize a BLOB ID:

ISC_QUAD blob_id; 	/* Declare a BLOB ID. */

blob_id = NULL; 	/* Set handle to NULL before using it */

3.	Create a new BLOB by calling isc_create_blob2():

isc_create_blob2(

status_vector,

&db_handle,

&trans,

&blob_handle,	 /* set by this function to refer to the new BLOB */

&blob_id,	/* BLOB ID set by this function */

0, 	/* BLOB Parameter Buffer length = 0; no filter will be used */

NULL 	/* NULL BLOB Parameter Buffer, since no filter will be used */

);

This function creates a new BLOB, opens it for write access, and sets blob_handle to point to the new BLOB.

 isc_create_blob2() also assigns the BLOB a BLOB ID, and sets blob_id to point to the BLOB ID. Note that blob_id is the variable pointed to by the sqldata field of the UPDATE statement input parameter that specifies the BLOB column to be updated. Thus, when the UPDATE statement is executed, this new BLOB will be used to update the BLOB column.

4.	Write all the data to be written to the BLOB by making a series of calls to isc_put_segment(). The following example reads lines of data, and concatenates each to the BLOB referenced by blob_handle. (get_line() reads the next line of data to be written.)

char *line;

unsigned short len;

. . .

line = get_line();

while (line)

{

len = strlen(line);

isc_put_segment(

status_vector,

&blob_handle,	/* set by previous isc_create_blob2() */

len,	/* length of buffer containing data to write */

line	/* buffer containing data to write into BLOB */

);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

};

line = get_line();

};

5.	Close the BLOB:

isc_close_blob(status_vector, &blob_handle);

Associating the New BLOB With the BLOB Column

Execute the UPDATE statement to associate the new BLOB with the BLOB column in the row selected by the statement:

isc_dsql_execute_immediate(

status_vector,

&db_handle,

&trans,

0,	/* indicates string to execute is null-terminated */

upd_str,	/* UPDATE statement string to be executed */

1,	/* XSQLDA version number */

in_sqlda	/* XSQLDA supplying parameters to UPDATE statement */

);


Deleting a BLOB

There are four ways to delete a BLOB:

Delete the row containing the BLOB. You can use DSQL to execute a DELETE statement.

Replace the BLOB with a different one. If a BLOB column contains a BLOB ID, and you modify the column to refer to a different BLOB, the BLOB referenced by the previously stored BLOB ID will be deleted during the next garbage collection.

Reset to NULL the column referring to the BLOB, for example, by using DSQL to execute a statement like the following:

UPDATE PROJECT SET PROJ_DESC = NULL WHERE PROJ_ID = "VBASE"

The BLOB referenced by the previously stored BLOB ID will be deleted during the next garbage collection.

Discard a BLOB after it has been created but before it has been associated with a particular column of a table row. Use the isc_cancel_blob() function, as in:

isc_cancel_blob(status_vector, &blob_handle);


Requesting Information About an Open BLOB

After an application opens a BLOB, it can obtain information about the BLOB. The isc_blob_info() call enables an application to query for BLOB information such as the total number of segments in the BLOB, or the length, in bytes, of the longest segment.

In addition to a pointer to the error status vector and a BLOB handle, isc_blob_info() requires two application-provided buffers, an item-list buffer, where the application specifies the information it needs, and a result buffer, where InterBase returns the requested information. An application populates the item-list buffer with information requests prior to calling isc_blob_info(), and passes it both a pointer to the item-list buffer, and the size, in bytes, of that buffer.

The application must also create a result buffer large enough to hold the information returned by InterBase. It passes both a pointer to the result buffer, and the size, in bytes, of that buffer to isc_blob_info(). If InterBase attempts to pass back more information than can fit in the result buffer, it puts the value, isc_info_truncated, defined in ibase.h, in the final byte of the result buffer.


Item-list Buffer Items and Result Buffer Values

The item-list buffer is a char array into which is placed a sequence of byte values, one per requested item of information. Each byte is an item type, specifying the kind of information desired. Compile-time constants for all item types are defined in ibase.h:

#define isc_info_blob_num_segments        4

#define isc_info_blob_max_segment         5

#define isc_info_blob_total_length        6

#define isc_info_blob_type                7

The result buffer returns a series of clusters of information, one per item requested. Each cluster consists of three parts:

1.	A one-byte item type. Each is the same as one of the item types in the item-list buffer.

2.	A 2-byte number specifying the number of bytes that follow in the remainder of the cluster.

3.	A value, stored in a variable number of bytes, whose interpretation depends on the item type.

A calling program is responsible for interpreting the contents of the result buffer and for deciphering each cluster as appropriate.

The clusters returned to the result buffer are not aligned. Furthermore, all numbers are represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers have the sign in the last byte. Convert the numbers to a data type native to your system, if necessary, before interpreting them. The API call, isc_vax_integer(), can be used to perform the conversion.

The following table lists items about which information can be requested and returned, and the values reported:

Request and Return Item	Return Value
isc_info_blob_num_segments	Total number of segments.
isc_info_blob_max_segment	Length of the longest segment.
isc_info_blob_total_length	Total size, in bytes, of BLOB.
isc_info_blob_type	Type of BLOB (0: segmented, or 1: stream).
In addition to the information InterBase returns in response to a request, InterBase can also return one or more of the following status messages to the result buffer. Each status message is one unsigned byte in length:

Item	Description
isc_info_end	End of the messages.
isc_info_truncated	Result buffer is too small to hold any more requested information.
isc_info_error	Requested information is unavailable. Check the status vector for an error code and message.


Isc_blob_info( ) Call Example

The following code requests the number of segments and the maximum segment size for a BLOB after it is opened, then examines the result buffer:

char blob_items[] = {

isc_info_blob_max_segment, isc_info_blob_num_segments};

char res_buffer[20], *p, item;

short length;

SLONG max_size = 0L, num_segments = 0L;

ISC_STATUS status_vector[20];

isc_open_blob2(

status_vector,

&db_handle,	/* database handle, set by isc_attach_database() */

&tr_handle, /* transaction handle, set by isc_start_transaction() */

&blob_handle,	 /* set by this function to refer to the BLOB */

&blob_id,	/* BLOB ID of the BLOB to open */

0, 	/* BPB length = 0; no filter will be used */

NULL 	/* NULL BPB, since no filter will be used */

);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}

isc_blob_info(

status_vector,

&blob_handle,	/* Set in isc_open_blob2() call above. */

sizeof(blob_items),	/* Length of item-list buffer. */

blob_items,	/* Item-list buffer. */

sizeof(res_buffer),	/* Length of result buffer. */

res_buffer	/* Result buffer */

);

if (status_vector[0] == 1 && status_vector[1])

{

/* An error occurred. */

isc_print_status(status_vector);

isc_close_blob(status_vector, &blob_handle);

return(1);

};

/* Extract the values returned in the result buffer. */

for (p = res_buffer; *p != isc_info_end ;)

{

item = *p++

length = (short)isc_vax_integer(p, 2);

p += 2;

switch (item)

{

case isc_info_blob_max_segment:

max_size = isc_vax_integer(p, length);

break;

case isc_info_blob_num_segments:

num_segments = isc_vax_integer(p, length);

break;

case isc_info_truncated:

/* handle error */

break;

default:

break;

}

p += length;

};


BLOB Descriptors

A BLOB descriptor is used to provide dynamic access to BLOB information. For example, it can be used to store information about BLOB data for filtering (conversion) purposes, such as character set information for text BLOB data and subtype information for text and non-text BLOB data. Two BLOB descriptors are needed whenever a filter will be used when writing to or reading from a BLOB : one to describe the filter source data, and the other to describe the target.

A BLOB descriptor is a structure defined in the ibase.h header file as follows:

typedef struct {

    short blob_desc_subtype; /* type of BLOB data */

    short blob_desc_charset; /* character set */

    short blob_desc_segment_size; /* segment size */

    unsigned char blob_desc_field_name [32]; /* BLOB column name */

    unsigned char blob_desc_relation_name [32]; /* table name */

} ISC_BLOB_DESC;

For more information about the character sets recognized by InterBase, see the Language Reference.

The segment size of a BLOB is the maximum number of bytes that an application is expected to write to or read from the BLOB. You can use this size to allocate your own buffers.

The blob_desc_relation_name and blob_desc_field_name fields contain null-
terminated strings.


Populating a BLOB Descriptor

There are four possible ways to populate a BLOB descriptor. You can do so by:

Calling isc_blob_default_desc(). This stores default values into the descriptor fields. The default subtype is 1 (TEXT), segment size is 80 bytes, and charset is the default charset for your process.

Calling isc_blob_lookup_desc(). This accesses the database system metadata tables to look up and copy information for the specified BLOB column into the descriptor fields.

Calling isc_blob_set_desc(). This initializes the descriptor from parameters you call it with, rather than accessing the database metadata.

Setting the descriptor fields directly.

The following example calls isc_blob_lookup_desc() to look up the current subtype and character set information for a BLOB column named PROJ_DESC in a table named PROJECT. It stores the information into the source descriptor, from_desc.

isc_blob_lookup_desc (

status_vector,

&db_handle;	/* Set by previous isc_attach_database() call. */

&tr_handle,	/* Set by previous isc_start_transaction() call. */

"PROJECT",	/* Table name. */

"PROJ_DESC", /* Column name. */

&from_desc,	/* BLOB descriptor filled in by this function call. */

&global	/* Global column name, returned by this function. */

)


Filtering BLOB Data

A BLOB filter is a routine that translates BLOB data from one subtype to another.

InterBase includes a set of special internal BLOB filters that convert from subtype 0 (unstructured data) to subtype 1 (TEXT), and from subtype 1 to subtype 0.

In addition to using these standard filters, you can write your own external filters to provide special data translation. For example, you might develop a filter to convert one image format to another, for instance to display the same image on monitors with different resolutions. Or you might convert a binary BLOB to plain text and back again to be able to move the file more easily from one system to another.

If you define filters, you can assign them subtype identifiers from -32,768 to -1.

The following sections provide an overview of how to write BLOB filters, followed by details of how to write an application that requires filtering. For more information about writing BLOB filters, see the Programmer's Guide.

Important:	 BLOB filters are available for databases residing on all InterBase server platforms except NetWare, where BLOB filters cannot be created or used.


Using Your Own Filters

Unlike the standard InterBase filters that convert between subtype 0 and subtype 1, an external BLOB filter is generally part of a library of routines you create and link to an application.

You can write BLOB filters in C or Pascal (or any language that can be called from C). To use your own filters, follow these steps:

1.	Decide which filters you need to write.

2.	Write the filters in a host language.

3.	Build a shared filter library.

4.	Make the filter library available.

5.	Define the filters to the database.

6.	Write an application that requests filtering.

Steps numbered 2, 5, and 6 are described in greater detail in the following sections.


Declaring an External BLOB Filter to the Database

To declare an external filter to a database, use the DECLARE FILTER statement. For example, the following statement declares the filter, SAMPLE:

DECLARE FILTER SAMPLE

INPUT TYPE -1 OUTPUT_TYPE -2

ENTRY POINT "FilterFunction"

MODULE_NAME "filter.dll";

In the example, the filter's input subtype is defined as -1 and its output subtype as -2. If subtype -1 specifies lowercase text, and subtype -2 uppercase text, then the purpose of filter SAMPLE would be to translate BLOB data from lowercase text to uppercase text.

The ENTRY_POINT and MODULE_NAME parameters specify the external routine that InterBase calls when the filter is invoked. The MODULE_NAME parameter specifies filter.dll, the dynamic link library containing the filter's executable code. The ENTRY_POINT parameter specifies the entry point into the DLL. Although the example shows only a simple file name, it is good practice to specify a fully-qualified path name, since users of your application need to load the file.


Writing an External BLOB Filter

If you choose to write your own filters, you must have a detailed understanding of the data types you plan to translate. InterBase does not do strict data type checking on BLOB data; it is your responsibility.

Defining the Filter Function

When writing a filter, you must include an entry point, known as a filter function, in the declaration section of the program. InterBase calls the filter function when an application performs a BLOB access operation on a BLOB specified to use the filter. All communication between InterBase and the filter is through the filter function. The filter function itself may call other functions that comprise the filter executable.

You declare the name of the filter function and the name of the filter executable with the ENTRY_POINT and MODULE_NAME parameters of the DECLARE FILTER statement.

A filter function must have the following declaration calling sequence:

filter_function_name(short action, isc_blob_ctl control);

The parameter, action, is one of eight possible action macro definitions, and the parameter, control, is an instance of the isc_blob_ctl BLOB control structure, defined in the InterBase header file, ibase.h.

The following listing of a skeleton filter declares the filter function, jpeg_filter:

#include <ibase.h>

#define SUCCESS 0

#define FAILURE 1

ISC_STATUS jpeg_filter(short action, isc_blob_ctl control)

{

ISC_STATUS status = SUCCESS;

switch (action)

{

case isc_blob_filter_open:
. . .

break;

case isc_blob_filter_get_segment:

. . .

break;

case isc_blob_filter_create:

. . .

break;

case isc_blob_filter_put_segment:

. . .

break;

case isc_blob_filter_close:

. . .

break;

case isc_blob_filter_alloc:

. . .

break;

case isc_blob_filter_free:

. . .

break;

case isc_blob_filter_seek:

. . .

break;

default:

. . .

break;

}

return status;

}

InterBase passes one of eight possible actions to the filter function, jpeg_filter, by way of the action parameter, and also passes an instance of the BLOB control structure, isc_blob_ctl, by way of the parameter, control.

The ellipses (. . .) in the previous listing represent code that performs some operations based on each action, or event, that is listed in the case statement. Most of the actions correspond to API functions called by an application. For more information regarding the types of code to write for each action, see the Programmer's Guide.

Defining the BLOB Control Structure

The isc_blob_ctl BLOB control structure provides the fundamental method of data exchange between InterBase and a filter.

The BLOB control structure is defined as a typedef, isc_blob_ctl, in ibase.h, as follows:

typedef struct isc_blob_ctl {

ISC_STATUS (*ctl_source)();

/* Internal InterBase BLOB access routine. */

struct isc_blob_ctl *ctl_source_handle;

/* Instance of isc_blob_ctl to pass to
internal InterBase BLOB access routine. */

short ctl_to_sub_type;	/* Target subtype. */

short ctl_from_sub_type;	/* Source subtype. */

unsigned short ctl_buffer_length; /* Length of ctl_buffer. */

unsigned short ctl_segment_length; /* Length of current segment. */

unsigned short ctl_bpb_length; /* BLOB parameter buffer length. */

char *ctl_bpb;	/* Pointer to BLOB parameter buffer. */

unsigned char *ctl_buffer; /* Pointer to segment buffer. */

ISC_LONG ctl_max_segment; /* Length of longest BLOB segment. */

ISC_LONG ctl_number_segments; /* Total number of segments. */

ISC_LONG ctl_total_length; /* Total length of BLOB. */

ISC_STATUS *ctl_status;	/* Pointer to status vector. */

long ctl_data[8];	/* Application-specific data. */

} *ISC_BLOB_CTL;

The purpose of certain isc_blob_ctl fields depend on the action being performed.

For example, when an application calls the isc_put_segment() API function, InterBase passes an isc_blob_filter_put_segment action to the filter function. The buffer pointed to by the ctl_buffer field of the control structure passed to the filter function contains the segment data to be written, as specified by the application in its call to isc_put_segment(). Because the buffer contains information passed into the filter function, it is called an IN field. The filter function should include instructions in the case statement under the isc_blob_filter_put_segment case for performing the filtering and then passing the data on for writing to the database. This can be done by calling the *ctl_source internal InterBase BLOB access routine. For more information about ctl_source, see the Programmer's Guide.

On the other hand, when an application calls the isc_get_segment() API function, the buffer pointed to by ctl_buffer in the control structure passed to a filter function is empty. In this situation, InterBase passes an isc_blob_filter_get_segment action to the filter function. The filter function isc_blob_filter_get_segment action handling should include instructions for filling ctl_buffer with segment data from the database to return to the application. This can be done by calling the *ctl_source internal InterBase BLOB access routine. In this case, because the buffer is used for filter function output, it is called an OUT field.

The following table describes each of the fields in the isc_blob_ctl BLOB control structure, and whether they are used for filter function input (IN), or output (OUT).

Field Name	Description
(*ctl_source)()	Pointer to the internal InterBase BLOB access routine. (IN)
*ctl_source_handle	Pointer to an instance of isc_blob_ctl to be passed to the internal InterBase BLOB access routine. (IN)
ctl_to_sub_type	Target subtype. Information field. Provided to support multi-purpose filters that can perform more than one kind of translation. This field and the next one enable such a filter to decide which translation to perform. (IN)
ctl_from_sub_type	Source subtype. Information field. Provided to support multi-purpose filters that can perform more than one kind of translation. This field and the previous one enable such a filter to decide which translation to perform. (IN)
ctl_buffer_length	For isc_blob_filter_put_segment, field is an IN field that contains the length of the segment data contained in ctl_buffer.For isc_blob_filter_get_segment, field is an IN field set to the size of the buffer pointed at by ctl_buffer, which is used to store the retrieved BLOB data.
ctl_segment_length	Length of current segment. For isc_blob_filter_put_segment, field is not used.For isc_blob_filter_get_segment, field is an OUT field set to the size of the retrieved segment (or partial segment, in the case when the buffer length ctl_buffer_length is less than the actual segment length).
ctl_bpb_length	Length of the BLOB parameter buffer.
*ctl_bpb	Pointer to the BLOB parameter buffer.
*ctl_buffer	Pointer to segment buffer. For isc_blob_filter_put_segment, field is an IN field that contains the segment data.For isc_blob_filter_get_segment, field is an OUT field the filter function fills with segment data for return to the application.
ctl_max_segment	Length, in bytes, of the longest segment in the BLOB. Initial value is 0. The filter function sets this field. This field is information only.
ctl_number_segments	Total number of segments in the BLOB. Initial value is 0. The filter function sets this field. This field is information only.
ctl_total_length	Total length, in bytes, of the BLOB. Initial value is 0. The filter function sets this field. This field is information only.
*ctl_status	Pointer to InterBase status vector. (OUT)
ctl_data [8]	8-element array of application-specific data. Use this field to store resource pointers, such as memory pointers and file handles created by the isc_blob_filter_open handler, for example. Then, the next time the filter function is called, the resource pointers will be available for use. (IN/OUT)
Programming Filter Function Actions

When an application invokes a BLOB API function on a BLOB to be filtered, InterBase passes a corresponding action message to the filter function by way of the action parameter. There are eight possible actions. The following action macro definitions are declared in the ibase.h file:

#define isc_blob_filter_open 	0

#define isc_blob_filter_get_segment 	1

#define isc_blob_filter_close 	2

#define isc_blob_filter_create 	3

#define isc_blob_filter_put_segment 	4

#define isc_blob_filter_alloc 	5

#define isc_blob_filter_free 	6

#define isc_blob_filter_seek 	7

The following table lists the actions, and specifies when the filter function is invoked with each particular action. Most of the actions are the result of events that occur when an application invokes a BLOB API function.

Action	When Filter is Invoked With Corresponding Action
isc_blob_filter_open	Invoked when an application calls isc_open_blob2().
isc_blob_filter_get_segment	Invoked when an application calls isc_get_segment().
isc_blob_filter_close	Invoked when an application calls isc_close_blob().
isc_blob_filter_create	Invoked when an application calls isc_create_blob2().
isc_blob_filter_put_segment	Invoked when an application calls isc_put_segment().
isc_blob_filter_alloc	Invoked when InterBase initializes filter processing. Not a result of a particular application action.
isc_blob_filter_free	Invoked when InterBase ends filter processing. Not a result of a particular application action.
isc_blob_filter_seek	Reserved for internal filter use; not used by external filters
This concludes the overview of writing BLOB filters. For detailed information about filters and how to program filter function actions, as well as a reference to a filter application example, see the Programmer's Guide.


Writing an Application That Requests Filtering

To request filtering of BLOB data as it is read from or written to a BLOB, follow these steps in your application:

1.	Create a BLOB parameter buffer (BPB) specifying the source and target sub-types, and optionally character sets (for TEXT subtypes).

2.	Call either isc_open_blob2() or isc_create_blob2() to open a BLOB for read or write access, respectively. In the call, pass the BPB, whose information InterBase will use to determine which filter should be called.

Understanding the BLOB Parameter Buffer

A BLOB parameter buffer (BPB) is needed whenever a filter will be used when writing to or reading from a BLOB.

The BPB is a char array variable, specifically declared in an application, that contains the source and target subtypes. When data is read from or written to the BLOB associated with the BPB, InterBase will automatically invoke an appropriate filter, based on the source and target subtypes specified in the BPB.

If the source and target subtypes are both 1 (TEXT), and the BPB also specifies different source and target character sets, then when data is read from or written to the BLOB associated with the BPB, InterBase will automatically convert each character from the source to the target character set.

A BLOB parameter buffer can be generated in one of two ways:

Indirectly, through API calls to create source and target descriptors and then generate the BPB from the information in the descriptors.

Directly by populating the BPB array with appropriate values.

If you generate a BPB via API calls, you do not need to know the format of the BPB. But if you wish to directly generate a BPB, then you must know the format.

Both approaches are described in the following sections. The format of the BPB is documented in the section about directly populating the BPB.

Generating a BLOB Parameter Buffer using API Calls

To generate a BPB indirectly, use API calls to create source and target BLOB descriptors, and then call isc_blob_gen_bpb() to generate the BPB from the information in the descriptors. Follow these steps:

1.	Declare two BLOB descriptors, one for the source, and the other for the target. For example,

#include "ibase.h"

ISC_BLOB_DESC from_desc, to_desc;

2.	Store appropriate information in the BLOB descriptors, by calling one of the functions isc_blob_default_desc(), isc_blob_lookup_desc(), or isc_blob_set_desc(), or by setting the descriptor fields directly. The following example looks up the current subtype and character set information for a BLOB column named GUIDEBOOK in a table named TOURISM, and stores it into the source descriptor, from_desc. It then sets the target descriptor, to_desc to the default subtype (TEXT) and character set, so that the source data will be converted to plain text.

isc_blob_lookup_desc (

status_vector,

&db_handle;	/* set in previous isc_attach_database() call */

&tr_handle,	/* set in previous isc_start_transaction() call */

"TOURISM",	/* table name */

"GUIDEBOOK", /* column name */

&from_desc,	/* BLOB descriptor filled in by this function call */

&global);

if (status_vector[0] == 1 && status_vector[1])

{

/* process error */

isc_print_status(status_vector);

return(1);

};

isc_blob_default_desc (

&to_desc,	/* BLOB descriptor filled in by this function call */

"",	/* NULL table name; it's not needed in this case */

"");	/* NULL column name; it's not needed in this case */

3.	Declare a character array which will be used as the BPB. Make sure it is at least as large as all the information that will be stored in the buffer.

char bpb[20];

4.	Declare an unsigned short variable into which InterBase will store the actual length of the BPB data:

unsigned short actual_bpb_length;

5.	Call isc_blob_gen_bpb() to populate the BPB based on the source and target BLOB descriptors passed to isc_blob_gen_bpb(). For example,

isc_blob_gen_bpb(

status_vector,

&to_desc,	/* target BLOB descriptor */

&from_desc,	/* source BLOB descriptor */

sizeof(bpb), /* length of BPB buffer */

bpb, 	/* buffer into which the generated BPB will be stored */

&actual_bpb_length 	/* actual length of generated BPB */

);

Generating a BLOB Parameter Buffer Directly

It is possible to directly generate a BPB.

A BPB consists of the following parts:

A byte specifying the version of the parameter buffer, always the compile-time constant, isc_bpb_version1.

A contiguous series of one or more clusters of bytes, each describing a single parameter.

Each cluster consists of the following parts:

A one-byte parameter type. There are compile-time constants defined for all the parameter types (e.g., isc_bpb_target_type).

A one-byte number specifying the number of bytes that follow in the remainder of the cluster.

A variable number of bytes, whose interpretation depends on the parameter type.

Important:	 All numbers in the BLOB parameter buffer must be represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers should have the sign in the last byte. The API function isc_vax_integer() can be used to reverse the byte order of a number.

The following table lists the parameter types and their meaning:

Parameter Type	Description
isc_bpb_target_type	Target subtype.
isc_bpb_source_type	Source subtype.
isc_bpb_target_interp	Target character set.
isc_bpb_source_interp	Source character set.
The BPB must contain isc_bpb_version1 at the beginning, and must contain clusters specifying the source and target subtypes. Character set clusters are optional. If the source and target subtypes are both 1 (TEXT), and the BPB also specifies different source and target character sets, then when data is read from or written to the BLOB associated with the BPB, InterBase will automatically convert each character from the source to the target character set.

The following is an example of directly creating a BPB for a filter whose source subtype is -4 and target subtype is 1 (TEXT):

char bpb[] = {

isc_bpb_version1,

isc_bpb_target_type,

1,	/* # bytes that follow which specify target subtype */

1,	/* target subtype (TEXT) */

isc_bpb_source_type,

1,	/* # bytes that follow which specify source subtype */

-4,	/* source subtype*/

};

Of course, if you do not know the source and target subtypes until run time, you can assign those values in the appropriate BPB locations at run time.

Requesting Filter Usage

You request usage of a filter when opening or creating a BLOB for read or write access. In the call to isc_open_blob2() or isc_create_blob2(), pass the BPB, whose information InterBase will use to determine which filter should be called.

The following example illustrates creating and opening a BLOB for write access.

Opening a BLOB for read access requires additional steps to select the appropriate BLOB to be opened.

isc_blob_handle blob_handle; /* declare at beginning */

ISC_QUAD blob_id; /* declare at beginning */

. . .

isc_create_blob2(

status_vector,

&db_handle,

&tr_handle,

&blob_handle, 	/* to be filled in by this function */

&blob_id, 	/* to be filled in by this function */

actual_bpb_length, 	/* length of BPB data */

&bpb 	/* BLOB parameter buffer */

)

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}


Working With Array Data

This section of the Help file describes arrays of data types and how to work with them using API functions. It shows how to set up an array descriptor specifying the array or array subset to be retrieved or written to, and how to use the two API functions that control access to arrays.

The following table summarizes the API functions for working with arrays. First the functions that can be used to populate an array descriptor are listed, followed by the functions for accessing array data.

Function	Purpose
isc_array_lookup_desc()	Looks up and stores into an array descriptor the data type, length, scale, and dimensions for all elements in the specified array column of the specified table.
isc_array_lookup_bounds()	Performs the same actions as the function, isc_array_lookup_desc(), but also looks up and stores the upper and lower bounds of each dimension.
isc_array_set_desc()	Initializes an array descriptor from parameters passed to it.
isc_array_get_slice()	Retrieves data from an array.
isc_array_put_slice()	Writes data to an array.


Introduction to Arrays

InterBase supports arrays of most data types. Using an array enables multiple data items to be stored in a single column. InterBase can treat an array as a single unit, or as a series of separate units, called slices. Using an array is appropriate when:

The data items naturally form a set of the same data type.

The entire set of data items in a single database column must be represented and controlled as a unit, as opposed to storing each item in a separate column.

Each item must also be identified and accessed individually.

The data items in an array are called array elements. An array can contain elements of any InterBase data type except BLOB, and cannot be an array of arrays. All of the elements of a particular array are of the same data type.

InterBase supports multi-dimensional arrays, arrays with 1 to 16 dimensions. Multi-dimensional arrays are stored in row-major order.

Array dimensions have a specific range of upper and lower boundaries, called subscripts. The array subscripts are defined when an array column is created. For information about creating an array, see the Language Reference.


Array Database Storage

Rather than storing array data directly in the array field of a table record, InterBase stores an array ID there. An array ID is a unique numeric value that references array data. The array data is stored elsewhere in the database.


Array Descriptors

An array descriptor is used to describe an array or array subset to be retrieved or written to the structure, ISC_ARRAY_DESC. ISC_ARRAY_DESC is defined in the InterBase ibase.h header file as follows:

typedef struct {

unsigned char array_desc_dtype; 	/* Data type */

char array_desc_scale; 	/* Scale for numeric data types */

unsigned short array_desc_length;

/* Length in bytes of each array element */

char array_desc_field_name [32]; 	/* Column name */

char array_desc_relation_name [32]; 	/* Table name */

short array_desc_dimensions; /* Number of array dimensions */

short array_desc_flags;

/* Specifies whether array is to be accessed in row-major or

column-major order */

ISC_ARRAY_BOUND array_desc_bounds [16];

/* Lower and upper bounds for each dimension */

} ISC_ARRAY_DESC;

ISC_ARRAY_BOUND is defined as:

typedef struct {

short array_bound_lower; /* lower bound */

short array_bound_upper; /* upper bound */

} ISC_ARRAY_BOUND;

An array descriptor contains 16 ISC_ARRAY_BOUND structures, one for each possible dimension. An array with n dimensions has upper and lower bounds set for the first n ISC_ARRAY_BOUND structures. The number of actual array dimensions is specified in the array_desc_dimensions field of the array descriptor.

When you retrieve data from an array, you supply an array descriptor defining the array slice (entire array or subset of contiguous array elements) to be retrieved. Similarly, when you write data to an array, you supply an array descriptor defining the array slice to be written to.


Populating an Array Descriptor

There are four ways to populate an array descriptor:

Call isc_array_lookup_desc(), which looks up (in the system metadata tables) and stores in an array descriptor the data type, length, scale, and dimensions for a specified array column in a specified table. This function also stores the table and column name in the descriptor, and initializes its array_desc_flags field to indicate that the array is to be accessed in row-major order. For example,

isc_array_lookup_desc(

status_vector,

&db_handle,	/* Set by isc_attach_database() */

&tr_handle,	/* Set by isc_start_transaction() */

"PROJ_DEPT_BUDGET",	/* table name */

"QUART_HEAD_CNT",	/* array column name */

&desc	/* descriptor to be filled in */

);

Call isc_array_lookup_bounds(), which looks and functions the same as a call to isc_array_lookup_desc(), except that the function isc_array_lookup_bounds() also looks up and stores into the array descriptor the upper and lower bounds of each dimension.

Call isc_array_set_desc(), which initializes the descriptor from parameters, rather than by accessing the database metadata.  For example,

short dtype = SQL_TEXT;

short len = 8;

short numdims = 2;

isc_array_set_desc(

status_vector,

"TABLE1",	/* table name */

"CHAR_ARRAY",	/* array column name */

&dtype,	/* data type of elements */

&len,	/* length of each element */

&numdims,	/* number of array dimensions */

&desc	/* descriptor to be filled in */

);

Setting the descriptor fields directly. An example of setting the array_desc_dimensions field of the descriptor, desc, is:

desc.array_desc_dimensions = 2;


Accessing Array Data

InterBase supports the following operations on array data:

Reading from an array or array slice.

Writing to an array:

Including a new array in a row to be inserted into a table.

Replacing the array referenced by an array column of a row with a new array.

Updating the array referenced by an array column of a row by modifying the array data or a slice of the data.

Deleting an array.

Dynamic SQL (DSQL) API functions and the XSQLDA data structure are needed to execute SELECT, INSERT, and UPDATE statements required to select, insert, or update relevant array data. The following sections include descriptions of the DSQL programming methods required to execute the sample statements provided.

Note:	The following array operations are not supported:

Referencing array dimensions dynamically in DSQL.

Setting individual array elements to NULL.

Using aggregate functions, such as MIN() and MAX(), with arrays.

Referencing arrays in the GROUP BY clause of a SELECT.

Creating views that select from array slices.


Reading Data From an Array

There are seven steps required for reading data from an array or slice of an array:

1.	Create a SELECT statement that specifies selection of the array column (and any other columns desired) in the rows of interest.

2.	Prepare an output XSQLDA structure to hold the column data for each row that is fetched.

3.	Prepare the SELECT statement for execution.

4.	Execute the statement.

5.	Populate an array descriptor with information describing the array or array slice to be retrieved.

6.	Fetch the selected rows one by one.

7.	Read and process the array data from each row.

Creating the SELECT Statement

Elicit a statement string from the user or create one that consists of the SQL query that will select rows containing the array data of interest. In your query, specify the array column name and the names of any other columns containing data you are interested in. For example, the following creates an SQL query statement string that selects an array column named QUART_HEAD_CNT and another column named DEPT_NO from the table, PROJ_DEPT_BUDGET:

char *sel_str =

"SELECT DEPT_NO, QUART_HEAD_CNT FROM PROJ_DEPT_BUDGET \

WHERE year = 1994 AND PROJ_ID = 'VBASE'";

Preparing the Output XSQLDA

Most queries return one or more rows of data, referred to as a select-list. An output XSQLDA must be created to store the column data for each row that is fetched. For an array column, the column data is an internal array identifier (array ID) that is needed to access the actual data. To prepare the XSQLDA, follow these steps:

1.	Declare a variable to hold the XSQLDA. For example, the following declaration creates an XSQLDA called out_sqlda:

XSQLDA *out_sqlda;

2.	Allocate memory for the XSQLDA using the XSQLDA_LENGTH macro. The XSQLDA must contain one XSQLVAR substructure for each column to be fetched. The following statement allocates storage for an output XSQLDA (out_sqlda) with two XSQLVAR substructures:

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

3.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field of the XSQLDA to indicate the number of XSQLVAR substructures allocated:

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 2;

Preparing the SELECT Statement for Execution

After an XSQLDA is created for holding the column data for each selected row, the query statement string can be prepared for execution. Follow these steps:

1.	Declare and initialize an SQL statement handle, then allocate it with isc_dsql_allocate_statement():

isc_stmt_handle stmt; 	/* Declare a statement handle. */

stmt = NULL; 	/* Set handle to NULL before allocation. */

isc_dsql_allocate_statement(status_vector, &db_handle, &stmt);

2.	Ready the statement string for execution with isc_dsql_prepare(). This checks the string (sel_str) for syntax errors, parses it into a format that can be efficiently executed, and sets the statement handle (stmt) to refer to this parsed format. The statement handle is used in a later call to isc_dsql_execute().

If isc_dsql_prepare() is passed a pointer to the output XSQLDA, as in the following example, it will fill in most fields of the XSQLDA and all its XSQLVAR substructures with information such as the data type, length, and name of the corresponding columns in the statement.

A sample call to isc_dsql_prepare() is:

isc_dsql_prepare(

status_vector,

&trans,	/* Set by previous isc_start_transaction() call. */

&stmt,	/* Statement handle set by this function call. */

0,	/* Specifies statement string is null-terminated. */

sel_str,	/* Statement string. */

1,	/* XSQLDA version number. */

out_sqlda	/* XSQLDA for storing column data. */

);

3.	Set up an XSQLVAR structure for each column. Setting up an XSQLVAR structure involves the following steps:

For columns whose types are known at compile time:

Specify the column's data type (if it was not set by isc_dsql_prepare(), as previously described).

Point the sqldata field of the XSQLVAR to an appropriate local variable.

For columns whose types are not known until run time:

Coerce the item's data type (optional); for example, from SQL_VARYING to SQL_TEXT.

Dynamically allocate local storage for the data pointed to by the sqldata field of the XSQLVAR.

For both:

Provide a NULL value indicator for the parameter.

 Data retrieval for array (and BLOB) columns is different from other types of columns, so the XSQLVAR fields must be set differently. For non-array (and non-BLOB) columns, isc_dsql_prepare() sets each XSQLVAR sqltype field to the appropriate field type, and the data retrieved when a select list row's data is fetched is placed into the sqldata space allocated for the column. For array columns, the type is set to SQL_ARRAY (or SQL_ARRAY + 1 if the array column is allowed to be NULL). InterBase stores the internal array identifier (array ID), not the array data, in the sqldata space when a row's data is fetched, so you must point sqldata to an area the size of an array ID.

The following code example illustrates the assignments for array and non-array columns whose types are known at compile time

ISC_QUAD array_id = 0L;

char dept_no[6];

short flag0, flag1;

out_sqlda->sqlvar[0].sqldata = (char *) dept_no;

out_sqlda->sqlvar[0].sqltype = SQL_TEXT + 1;

out_sqlda->sqlvar[0].sqlind = &flag0;

out_sqlda->sqlvar[1].sqldata = (char *) &array_id;

out_sqlda->sqlvar[1].sqltype = SQL_ARRAY + 1;

out_sqlda->sqlvar[1].sqlind = &flag1;

Executing the Statement

Once the query statement string is prepared, it can be executed:

isc_dsql_execute(

status_vector,

&trans,	/* set by previous isc_start_transaction() call */

&stmt,	/* set above by isc_dsql_prepare() */

1,	/* XSQLDA version number */

NULL	/* NULL since stmt doesn't have input values */

);

This statement creates a select-list, the rows returned by execution of the
statement.

Populating the Array Descriptor

To prepare an array descriptor that describes the array or array slice to be read, follow these steps:

1.	Create the array descriptor:

ISC_ARRAY_DESC desc;

2.	Fill in the descriptor with information regarding the array column from which data will be read. Do this either by calling one of the functions isc_array_lookup_bounds(), isc_array_lookup_desc(), or isc_array_set_desc(), or by directly filling in the descriptor.

Ensure the descriptor boundaries are set to those of the slice to be read.

If you want to retrieve all the array data (that is, not just a smaller slice), set the boundaries to the full boundaries as initially declared for the array column. This is guaranteed to be the case if you fill in the descriptor by calling isc_array_lookup_bounds(), as in:

ISC_ARRAY_DESC desc;

isc_array_lookup_bounds(

status_vector,

&db_handle,

&trans,

"PROJ_DEPT_BUDGET",	/* table name */

"QUART_HEAD_CNT",	/* array column name */

&desc);

Suppose the array column, QUART_HEAD_CNT, is a one-dimensional array consisting of four elements, and it was declared to have a lower subscript bound of 1 and an upper bound of 4 when it was created. Then after the above call to isc_array_lookup_bounds(), the array descriptor fields for the boundaries contain the following information:

desc.array_desc_bounds[0].array_bound_lower == 1

desc.array_desc_bounds[0].array_bound_upper == 4

If you want to read just a slice of the array, then modify the upper andor lower bounds appropriately. For example, if you just want to read the first two elements of the array, then modify the upper bound to the value 2, as in:

desc.array_desc_bounds[0].array_bound_upper = 2

Fetching Selected Rows

A looping construct is used to fetch (into the output XSQLDA) the column data for a single row at a time from the select-list and to process each row before the next row is fetched. Each execution of isc_dsql_fetch() fetches the column data for the next row into the corresponding XSQLVAR structures of out_sqlda. For the array column, the array ID, not the actual array data, is fetched.

ISC_STATUS fetch_stat;

long SQLCODE;

. . .

while ((fetch_stat =

isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda))

== 0)

{

/* Read and process the array data */

}

if (fetch_stat != 100L)

{

/* isc_dsql_fetch returns 100 if no more rows remain to be

retrieved */

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

return(1);

}

Reading and Processing the Array Data

To read and process the array or array slice data:

1.	Create a buffer for holding the array data to be read. Make it large enough to hold all the elements in the slice to be read (which could be the entire array). For example, the following declares an array buffer large enough to hold 4 long elements:

long hcnt[4];

2.	Declare a short variable for specifying the size of the array buffer:

short len;

3.	Set the variable to the buffer length:

len = sizeof(hcnt);

4.	Read the array or array slice data into the buffer by calling isc_array_get_slice(). Process the data read. In the following example, the array is read into the hcnt array buffer, and "processing" consists of printing the data:

isc_array_get_slice(

status_vector,

&db_handle,	/* set by isc_attach_database()*/

&trans,	/* set by isc_start_transaction() */

&array_id, 	/* array ID put into out_sqlda by isc_dsql_fetch()*/

&desc,	/* array descriptor specifying slice to be read */

(void *) hcnt,	/* buffer into which data will be read */

(long *) &len	/* length of buffer */

);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}

/* Make dept_no a null-terminated string */

dept_no[out_sqlda->sqlvar[0].sqllen] = '\0';

printf("Department #: %s\n\n", dept_no);

printf("\tCurrent head counts: %ld %ld %ld %ld\n",

hcnt[0], hcnt[1], hcnt[2], hcnt[3]);


Writing Data to an Array

isc_array_put_slice() is called to write data to an array or array slice. Use it to:

Include a new array in a row to be inserted into a table.

Replace the current contents of an array column of a row with a new array.

Update the array referenced by an array column of a row by modifying the array data or a slice of the data.

The entry in an array column of a row does not actually contain array data. Rather, it has an array ID referring to the data, which is stored elsewhere. So, to set or modify an array column, you need to set or change the array ID stored in it. If an array column contains an array ID, and you modify the column to refer to a different array (or to contain NULL), the array referenced by the previously stored array ID will be deleted during the next garbage collection.

The following steps are required to insert, replace, or update array data:

1.	Prepare an array descriptor with information describing the array (or slice) to be written to.

2.	Prepare an array buffer with the data to be written.

3.	Prepare an appropriate DSQL statement. This will be an INSERT statement if you are inserting a new row into a table, or an UPDATE statement for modifying an existing row.

4.	Call isc_array_put_slice() to create a new array (possibly copying an existing one), and to write the data from the array buffer into the array or array slice.

5.	Associate the new array with an array column of the table row being modified or inserted by executing the UPDATE or INSERT statement. This sets the array column to contain the array ID of the new array.

Preparing the Array Descriptor

To prepare an array descriptor that specifies the array or array slice to be written to, follow these steps:

1.	Create the array descriptor:

ISC_ARRAY_DESC desc;

2.	Fill in the descriptor with information regarding the array column to which data will be written. Do this either by calling one of the functions isc_array_lookup_bounds(), isc_array_lookup_desc(), or isc_array_set_desc(), or by directly filling in the descriptor.

Ensure the descriptor boundaries are set to those of the slice to be
written to.

If you want to write to the entire array (i.e., not just a smaller slice), set the boundaries to the full boundaries as initially declared for the array column. This is guaranteed to be the case if you fill in the descriptor by calling isc_array_lookup_bounds(), as in:

isc_array_lookup_bounds(

status_vector,

db_handle,

&trans,

"PROJ_DEPT_BUDGET",	/* table name */

"QUART_HEAD_CNT",	/* array column name */

&desc);

Suppose the array column, QUART_HEAD_CNT, is a one-dimensional array consisting of four elements, and it was declared to have a lower subscript bound of 1 and an upper bound of 4 when it was created. Then after a call to isc_array_lookup_bounds(), the array descriptor fields for the boundaries contain the following information:

desc.array_desc_bounds[0].array_bound_lower == 1

desc.array_desc_bounds[0].array_bound_upper == 4

If you just want to write to (or modify) a slice of the array, then change the upper and lower bound appropriately. For example, if you just want to write to the first two elements of the array, then modify the upper bound to the value 2, as in:

desc.array_desc_bounds[0].array_bound_upper == 2

Preparing the Array Buffer With Data

1.	Create an array buffer to hold the data to be written to the array. Make it large enough to hold all the elements in the slice to be written (which could be the entire array). For example, the following declares an array buffer large enough to hold 4 long elements:

long hcnt[4];

2.	Create a variable specifying the length of the array buffer:

short len;

len = sizeof(hcnt);

3.	Fill the array buffer with the data to be written.

If you are creating a new array, then fill the buffer with data. For example,

hcnt[0] = 4;

hcnt[1] = 5;

hcnt[2] = 6;

hcnt[3] = 6;

To modify existing array data instead of creating a new one, then perform all the steps listed above under "Reading Data from an Array" to read the existing array data into the array buffer. Modify the data in the buffer.

Preparing the UPDATE or INSERT Statement

To prepare an UPDATE or INSERT statement for execution, follow these steps:

1.	Elicit an UPDATE or INSERT statement string from the user or create one for inserting a new row or updating the row(s) containing the array column(s) of interest. For example, the following statement is for updating the array column named QUART_HEAD_CNT in the specified row of the table, PROJ_DEPT_BUDGET. The department number and quarterly head counts are assumed to be supplied at run time:

char *upd_str =

"UPDATE PROJ_DEPT_BUDGET SET QUART_HEAD_CNT = ? WHERE \

YEAR = 1994 AND PROJ_ID = "MKTPR" AND DEPT_NO = ?";

As an example of an INSERT statement, the following is for inserting a new row into the PROJ_DEPT_BUDGET table, with column data supplied at run time:

char *upd_str =

"INSERT INTO PROJ_DEPT_BUDGET (YEAR, PROJ_ID, DEPT_NO, \

QUART_HEAD_CNT) VALUES (?, ?, ?, ?)";

The remaining steps refer only to UPDATE statements, but the actions apply to INSERT statements as well.

2.	Declare a variable to hold the input XSQLDA needed to supply parameter values to the UPDATE statement at run time. For example, the following declaration creates an XSQLDA called in_sqlda:

XSQLDA *in_sqlda;

3.	Allocate memory for the input XSQLDA using the XSQLDA_LENGTH macro. The XSQLDA must contain one XSQLVAR substructure for each parameter to be passed to the UPDATE statement. The following statement allocates storage for an input XSQLDA (in_sqlda) with two XSQLVAR substructures:

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

4.	Set the version field of the XSQLDA to SQLDA_VERSION1, and set the sqln field to indicate the number of XSQLVAR structures allocated:

in_sqlda->version = SQLDA_VERSION1;

in_sqlda->sqln = 2;

5.	Set up the XSQLVAR structure in the XSQLDA for each parameter to be passed. Setting up an XSQLVAR structure involves the following steps:

Specifying the item's data type.

For parameters whose types are known at compile time: Point the sqldata field of the XSQLVAR to an appropriate local variable that will contain the data to be passed.

For parameters whose types are not known until run time: Allocate local storage for the data pointed to by the sqldata field of the XSQLVAR.

Specifying the number of bytes of data.

Data storage for array (and BLOB) columns is different from other types of columns, so the XSQLVAR fields must be set differently. For non-array (and non-BLOB) columns, input parameter data comes from the space pointed to by sql-data. For array columns, set the type to SQL_ARRAY (or SQL_ARRAY + 1 if the array column is allowed to be NULL). The application must store space for the internal array identifier, not the array data, in the sqldata space. See the following sections to create or modify an array, store its array ID in the sqldata space, and then associate the actual array data with the column.

The following code example illustrates the assignments for one TEXT column and one array column, where the column types are known at compile time.

#define NUMLEN 4

char dept_no[NUMLEN + 1];

ISC_QUAD array_id;

in_sqlda->sqlvar[0].sqldata = &array_id;

in_sqlda->sqlvar[0].sqltype = SQL_ARRAY + 1;

in_sqlda->sqlvar[0].sqllen = sizeof(ISC_QUAD);

in_sqlda->sqlvar[1].sqldata = dept_no;

in_sqlda->sqlvar[1].sqltype = SQL_TEXT;

in_sqlda->sqlvar[1].sqllen = 4;

The dept_no variable should be assigned a value at run time (unless the value is known at compile time). The array_id variable should be set to refer to the newly created array, as described in the following sections.

Calling isc_array_put_slice()

The following steps are required to store the data into an array or array slice:

1.	Declare an array ID:

ISC_QUAD array_id; 	/* Declare an array ID. */

2.	Initialize the array ID. If you are creating a new array to be inserted into a new row, or to replace an existing array, then simply initialize the array ID to NULL:

array_id = NULL;	/* Set handle to NULL before using it */

If you are modifying an existing array, then follow the steps listed under "Read-ing Data from an Array" to read the existing array ID into array_id.

3.	Call isc_array_put_slice(). In your call you pass the array ID (either the array ID of an existing array, or NULL for a new array) in the array_id variable. You also pass the buffer of data to be written and a descriptor specifying the array slice to which the data belongs.

When isc_array_put_slice() is called with an array ID of an existing array, it creates a new array with the same characteristics as the specified array, and copies the existing array data to the new array. Then isc_array_put_slice() writes the data from the array buffer to the new array (or slice of the array), per the bounds specified in the array descriptor, and returns in the same array_id variable the array ID of the new array.

When isc_array_put_slice() is called with a NULL array ID, it creates a new empty array with characteristics as declared for the array column whose name and table name are specified in the array descriptor passed to isc_array_put_slice(). It then writes the data from the array buffer to the new array (or slice of the array), and returns in the array_id variable the array ID of the new array.

Note that in both cases, a new array is created, and its array ID is returned in the array_id variable. The array is temporary until an UPDATE or INSERT statement is executed to associate the array with a particular column of a particular row.

You can make a single call to isc_array_put_slice() to write all the data to the array. Or, you may call isc_array_put_slice() multiple times to store data into various slices of the array. In this case, each call to isc_array_put_slice() after the first call should pass the array ID of the temporary array. When isc_array_put_slice() is called with the array ID of a temporary array, it copies the specified data to the specified slice of the temporary array, but does not create a new array.

 	The following is a sample call to isc_array_put_slice():

isc_array_put_slice(

status_vector,

&db_handle,

&trans,

&array_id,	/* array ID (NULL, or existing array's array ID) */

&desc,	/* array descriptor describing where to write data */

hcnt, 	/* array buffer containing data to write to array */

&len 	/* length of array buffer */

);

This call creates a new array, copies the data in hcnt to the new array (or slice of the array), assigns the array an array ID, and sets array_id to point to the array ID.

array_id should be the variable pointed to by the sqldata field of the UPDATE (or INSERT) statement input parameter that specifies the array column to be updated. Thus, when the INSERT or UPDATE statement is executed, this new array's array ID will be used to set or update the array column to refer to the new array.

Associating the New Array With the Array Column

Execute the UPDATE statement to associate the new array with the array column in the row selected by the statement:

isc_dsql_execute_immediate(

status_vector,

&db_handle,

&trans,

0,	/* indicates string to execute is null-terminated */

upd_str,	/* UPDATE statement string to be executed */

1,	/* XSQLDA version number */

in_sqlda	/* XSQLDA supplying parameters to UPDATE statement */

);

This sets the array column in the row specified in the UPDATE statement to contain the array ID of the new array. The array ID comes from the array_id variable pointed to by the in_sqlda parameter corresponding to the array column.

If the array column in the specified row contains the array ID of a different array before the UPDATE statement is executed, then the column is modified to contain the new array ID, and the array referenced by the previously stored array ID will be deleted during the next garbage collection.


Deleting an Array

There are three ways to delete an array:

1.	Delete the row containing the array. You can use DSQL to execute a DELETE statement.

2.	Replace the array with a different one, as described above. If an array column contains an array ID, and you modify the column to refer to a different array, the array referenced by the previously stored array ID will be deleted during the next garbage collection.

3.	Reset to NULL the column referring to the array. For example, use DSQL to execute a statement like the following, where LANGUAGE_REQ is an array column:

"UPDATE JOB SET LANGUAGE_REQ = NULL \

WHERE JOB_CODE = "SA12" AND JOB_GRADE = 10"

The array referenced by the previously stored array ID will be deleted during the next garbage collection.


Working With Conversions

InterBase uses a proprietary format for internal storage of DATE data, but provides the following API calls for translating to and from this format:

isc_decode_date() to convert the InterBase internal date format to the C time structure

isc_encode_date() to convert the C time structure to the internal
InterBase date format

These calls merely translate DATE data between formats; they do not read or write DATE data directly. DATE data is read from and written to the database using standard DSQL syntax processed with the isc_dsql family of API calls.

InterBase also requires that numbers entered in database and transaction parameter buffers be in a generic format, with the least significant byte last. Signed numbers require the sign to be in the last byte. Systems that represent numbers with the most significant byte last must use the isc_vax_integer() API function to reverse the byte order of numbers entered in database parameter buffers (DPBs) and transaction parameter buffers (TPBs). When numeric information is returned by information calls on these systems, isc_vax_integer() must be used once again to reverse the byte ordering.


Converting Dates From InterBase to C Format

To select a date from a table, and convert it to a form usable in a C language program, follow these steps:

1.	Create a host variable for a C time structure. Most C and C++ compilers provide a typedef declaration, tm, for the C time structure in the time.h header file. The following C code includes that header file, and declares a variable of type tm:

#include <time.h>

#include "ibase.h"

. . .

struct tm hire_time;

. . .

Note:	To create host-language time structures in languages other than C and C++, see the host-language reference manual.

2.	Create a host variable of type ISC_QUAD. For example, the host-variable declaration might look like this:

ISC_QUAD hire_date;

The ISC_QUAD structure is declared in ibase.h, but the programmer must declare actual host-language variables of type ISC_QUAD.

3.	Retrieve a date from a table into the ISC_QUAD variable.

4.	Convert the ISC_QUAD variable into a numeric C format with the InterBase function, isc_decode_date(). This function is also declared in ibase.h. isc_decode_date() requires two parameters, the address of the ISC_QUAD host-language variable, and the address of the tm host-language variable. For example, the following code fragment coverts hire_date to hire_time:

isc_decode_date(&hire_date, &hire_time);


Converting Dates From C to InterBase Format

To insert a date in a table, it must be converted from the host-language format into InterBase format, and then stored. To perform the conversion and insertion in a C program, follow these steps:

1.	Create a host variable for a C time structure. Most C and C++ compilers provide a typedef declaration, tm, for the C time structure in the time.h header file. The following C code includes that header file, and declares a tm variable, hire_time:

#include <time.h>;

. . .

struct tm hire_time;

. . .

Note:	To create host-language time structures in languages other than C and C++, see the host-language reference manual.

2.	Create a host variable of type ISC_QUAD, for use by InterBase. For example, the host-variable declaration might look like this:

ISC_QUAD mydate;

The ISC_QUAD structure is declared in ibase.h, but the programmer must declare actual host-language variables of type ISC_QUAD.

3.	Put date and time information into hire_time.

4.	Use the InterBase isc_encode_date() function to convert the information in hire_time into InterBase internal format and store that formatted information in the ISC_QUAD host variable (hire_date in the example). This function is also declared in ibase.h.

isc_encode_date() requires two parameters, the address of the C time structure, and the address of the ISC_QUAD host-language variable. For example, the following code converts hire_time to hire_date:

isc_encode_date(&hire_time, &hire_date);

5.	Insert the date into a table.


Reversing Byte Order of Numbers With Isc_vax_integer( )

InterBase expects that numbers entered in database and transaction parameter buffers be in a generic format, with the least significant byte last. Signed numbers require the sign to be in the last byte. Systems that represent numbers with the most significant byte last must use the isc_vax_integer() API function to reverse the byte order of numbers entered in DPBs and TPBs. When numeric information is returned by information calls on these systems, isc_vax_integer() must be used once again to reverse the byte ordering. The syntax for isc_vax_integer() is:

ISC_LONG isc_vax_integer( char *buffer, short length);

buffer is a char pointer to the integer to convert, and length is the size, in bytes, of the integer. Valid lengths are 1 (short), 2 (int), and 4(long). The following code reverses the 4-byte value in a result buffer.

#include "ibase.h"

. . .

for(p = res_buffer; *p != isc_info_end;)

{

p++;

length = isc_vax_integer(p, 2);

}


Handling Error Conditions

This section of the Help file describes how to set up an error status vector where InterBase can store run-time error information, and how to use API functions to handle and report errors.

The following table summarizes the API functions for handling errors:

Function	Purpose
isc_interprete()	Capture InterBase error messages to a buffer.
isc_print_sqlerror()	Display an SQL error message.
isc_print_status()	Display InterBase error messages.
isc_sqlcode()	Set the value of SQLCODE.
isc_sql_interprete()	Capture an SQL error message to a buffer.


Setting Up an Error Status Vector

Most API functions return status information that indicates success or failure. The information returned is derived from the second array element of the error status vector, where InterBase reports error conditons. The error status vector is declared in applications as an array of 20 long integers, using the following
syntax:

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

ISC_STATUS is a #define in ibase.h provided for programing convenience and platform independence.


Using Information in the Status Vector

Whether or not an error occurs during the execution of an API call, InterBase loads the error status vector with status information. Information consists of one or more InterBase error codes, and error information that can be used to build an error message honed to a specific error.

An application can check the status vector after the execution of most API calls to determine their success or failure. If an error condition is reported, applications can:

Display InterBase error messages using isc_print_status().

Set an SQLCODE value corresponding to an InterBase error using isc_sqlcode(), and display the SQLCODE and an SQL error message using isc_print_sqlerror().

Build individual InterBase error messages in a buffer with isc_interprete(). The buffer must be provided by the application. Using a buffer enables an application to perform additional message processing (for example, storing messages in an error log file). This ability is especially useful on windowing systems that do not permit direct screen writes.

Capture an SQL error message in a buffer with isc_sql_interprete(). The buffer must be provided by the application.

Parse for and react to specific InterBase error codes in the status vector.


Checking the Status Vector For Errors

API functions that return information in the status vector are declared in ibase.h as returning an ISC_STATUS pointer. For example, the function prototype for isc_prepare_transaction() is declared as:

ISC_STATUS ISC_EXPORT isc_prepare_transaction(
ISC_STATUS ISC_FAR *,
isc_tr_handle ISC_FAR *);

To check the status vector for error conditions after the execution of a function, examine the first element of the status vector to see if it is set to 1, and if so, examine the second element to see if it is not 0. A nonzero value in the second element indicates an error condition. The following C code fragment illustrates how to check the status vector for an error condition:

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

. . .

/* Assume an API call returning status information is called here. */

if (status_vector[0] == 1 && status_vector[1] > 0)

{

/* Handle error condition here. */

;

}

If an error condition is detected, you can use API functions in an error-handling routine to display error messages, capture the error messages in a buffer, or parse the status vector for particular error codes.

Display or capture of error messages is only one part of an error-handling routine. Usually, these routines also roll back transactions, and sometimes they can retry failed operations.


Displaying InterBase Error Messages

Use isc_print_status() to display InterBase error messages on the screen. This function parses the status vector to build all available error messages, then uses the C printf() function to write the messages to the display. isc_print_status() requires one parameter, a pointer to a status vector containing error information. For example, the following code fragment calls isc_print_status() and rolls back a transaction on error:

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

isc_tr_handle trans;

. . .

trans = 0L;

. . .

/* Assume a transaction, trans, is started here. */

/* Assume an API call returning status information is called here. */

if (status_vector[0] == 1 && status_vector[1] > 0)

{

isc_print_status(status_vector);

isc_rollback_transaction(status_vector, &trans);

}

Important:	On windowing systems that do not permit direct screen writes with printf(), use isc_interprete() to capture error messages to a buffer.

Note:	For applications that use the dynamic SQL (DSQL) API functions, errors should be displayed using SQL conventions. Use isc_sqlcode() and isc_print_sqlerror() instead of isc_print_status().


Capturing InterBase Error Messages

Use isc_interprete() to build an error message from information in the status vector and store it in an application-defined buffer where it can be further manipulated. Capturing messages in a buffer is useful when applications:

Run under windowing systems that do not permit direct screen writes.

Require more control over message display than is possible with isc_print_status().

Store a record of all error messages in a log file.

Manipulate or format error messages for display or pass them to a windowing system's display routines.

isc_interprete() retrieves and formats a single error message each time it is called. When an error occurs, the status vector usually contains more than one error message. To retrieve all relevant error messages, you must make repeated calls to isc_interprete().

Given both the location of a buffer, and the address of the status vector, isc_interprete() builds an error message from the information in the status vector, puts the formatted string in the buffer where an application can manipulate it, and advances the status vector pointer to the start of the next cluster of error information. isc_interprete() requires two parameters, the address of an application buffer to hold formatted message output, and a pointer to the status vector array.

Important:	Never pass the status vector array directly to isc_interprete(). Each time it is called, isc_interprete() advances the pointer to the status vector to the next element containing new message information. Before calling isc_interprete(), be sure to set the pointer to the starting address of the status vector.

The following code demonstrates an error-handling routine that makes repeated calls to isc_interprete() to retrieve error messages from the status vector in a buffer, one at a time, so they can be written to a log file:

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

isc_tr_handle trans;

long *pvector;

char msg[512];

FILE *efile; /* Code fragment assumes pointer to an open file. */

trans = 0L;

. . .

/* Error-handling routine starts here. */

/* Always set pvector to point to start of status_vector. */

pvector = status_vector;

/* Retrieve first message. */

isc_interprete(msg, &pvector);

/* Write first message from buffer to log file. */

fprintf(efile, "%s\n", msg);

msg[0] = '-'; /* Append leading hyphen to secondary messages. */

/* Look for more messages and handle in a loop. */

while(isc_interprete(msg + 1, &pvector)) /* More? */

fprintf(efile, "%s\n", msg); /* If so, write it to the log. */

fclose(efile); /* All done, so close the log file. */

isc_rollback(status_vector, &trans);

return(1);

. . .

Note:	This code fragment assumes that the log file is properly declared and opened elsewhere in the application before control is passed to this error handler.

Tip:	For applications that use the dynamic SQL (DSQL) API functions, errors should be buffered using SQL conventions. Use isc_sqlcode() and isc_sql_interprete() instead of isc_interprete().


Setting an SQLCODE Value on Error

For DSQL applications, error conditions should be cast in terms of SQL conventions. SQL applications typically report errors through a variable, SQLCODE, declared by an application. To translate an InterBase error code into SQLCODE format, use isc_sqlcode(). This function searches the error status vector for an InterBase error code that can be translated into an SQL error code, and performs the translation. Once SQLCODE is set, the other API functions for handling SQL errors, isc_print_sqlerror(), and isc_sql_interprete(), can be called.

isc_sqlcode() requires one parameter, a pointer to the status vector. It returns a long value, containing an SQL error code. The following code illustrates the use of this function:

#include <ibase.h>;

. . .

long SQLCODE; /* Declare the SQL error code variable. */

ISC_STATUS status_vector[20];

. . .

if (status_vector[0] == 1 && status_vector[1] > 0)

{

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector)

. . .

}

If successful, isc_sqlcode() returns the first valid SQL error code decoded from the status vector. If no valid SQL error code is found, isc_sqlcode() returns -999.


Displaying SQL Error Messages

API applications that provide a DSQL interface to end users should use isc_print_sqlerror() to display SQL error codes and corresponding error messages on the screen. When passed a variable, conventionally named SQLCODE, containing an SQL error code, and a pointer to the status vector, isc_print_sqlerror() parses the status vector to build an SQL error message, then uses the C printf() function to write the SQLCODE value and message to the display. For example, the following code fragment calls isc_print_sqlerror() and rolls back a transaction on error:

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

isc_tr_handle trans;

long SQLCODE;

. . .

trans = 0L;

. . .

/* Assume a transaction, trans, is started here. */

/* Assume an API call returning status information is called here. */

if (status_vector[0] == 1 && status_vector[1] > 0)

{

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

isc_rollback_transaction(status_vector, &trans);

}

Important:	On windowing systems that do not permit direct screen writes with printf(), use isc_sql_interprete() to capture error messages to a buffer.


Capturing SQL Error Messages

Use isc_sql_interprete() to build an SQL error message based on a specific SQL error code and store it in a buffer defined by an application. Capturing messages in a buffer is useful when applications:

Run under windowing systems that do not permit direct screen writes.

Store a record of all error messages in a log file.

Manipulate or format error messages for display or pass them to a windowing system's display routines.

isc_sql_interprete() requires three parameters: a valid SQL error code, usually passed as a variable named SQLCODE, a buffer where the SQL message should be stored, and the size of the buffer. The following code illustrates how this function might be called to build a message string and store it in a log file:

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

isc_tr_handle trans;

long SQLCODE;

char msg[512];

FILE *efile; /* Code fragment assumes pointer to an open file. */

trans = 0L;

. . .

/* Assume a transaction, trans, is started here. */

/* Assume an API call returning status information is called here. */

. . .

/* Error-handling routine starts here. */

if (status_vector[0] == 1 && status_vector[1] > 0)

{

SQLCODE = isc_sqlcode(status_vector);

isc_sql_interprete(SQLCODE, msg, 512);

fprintf(efile, "%s\n", msg);

isc_rollback_transaction(status_vector, &trans);

return(1);

}

Note:	This code fragment assumes that the log file is properly declared and opened elsewhere in the application before control is passed to this error handler.


Parsing the Status Vector

InterBase stores error information in the status vector in clusters of two or three longs. The first cluster in the status vector always indicates the primary cause of the error. Subsequent clusters may contain supporting information about the error, for example, strings or numbers for display in an associated error message. The actual number of clusters used to report supporting information varies from error to error.

In many cases, additional errors may be reported in the status vector. Additional errors are reported immediately following the first error and its supporting information, if any. The first cluster for each additional error message identifies the error. Subsequent clusters may contain supporting information about the error.

How the Status Vector is Parsed

The InterBase error-handling routines, isc_print_status() and isc_interprete(), use routines which automatically parse error message information in the status vector without requiring you to know about its structure. If you plan to write your own routines to read and react to the contents of the status vector, you need to know how to interpret it.

The key to parsing the status vector is to decipher the meaning of the first long in each cluster, beginning with the first cluster in the vector.

Meaning of the First Long in a Cluster

The first long in any cluster is a numeric descriptor. By examining the numeric descriptor for any cluster, you can always determine the:

Total number of longs in the cluster.

Kind of information reported in the remainder of the cluster.

Starting location of the next cluster in the status vector.

Interpretation of 1st Long in a Cluster

Value	Longs in Cluster	Meaning
0	--	End of error information in the status vector.
1	2	Second long is an InterBase error code.
2	2	Second long is the address of string used as a replaceable parameter in a generic InterBase error message.
3	3	Second long is the length, in bytes, of a variable-length string provided by the operating system (most often this string is a file name). Third long is the address of the string.
4	2	Second long is a number used as a replaceable parameter in a generic InterBase error message.
5	2	Second long is the address of an error message string requiring no further processing before display.
6	2	Second long is a VAX/VMS error code.
7	2	Second long is a Unix error code.
8	2	Second long is an Apollo Domain error code.
9	2	Second long is an MS-DOS or OS/2 error code.
10	2	Second long is an HP MPE/XL error code.
11	2	Second long is an HP MPE/XL IPC error code.
12	2	Second long is a NeXT/Mach error code.
Note:	As InterBase is adapted to run on other hardware and software platforms, additional numeric descriptors for specific platform and operating system error codes may be added to the end of this list.

By including ibase.h at the start of your source code, you can use a series of #defines to substitute for hard-coded numeric descriptors in the status vector parsing routines you write. The advantages of using these #defines over hard-coding the descriptors are:

Your code will be easier to read.

Code maintenance will be easier should the numbering scheme for numeric descriptors change in a future release of InterBase.

The following table lists the #define equivalents of each numeric descriptor:

#defines for Status Vector Numeric Descriptors

Value	#define	Value	#define
0	isc_arg_end	8	isc_arg_domain
1	isc_arg_gds	9	isc_arg_dos
2	isc_arg_string	10	isc_arg_mpexl
3	isc_arg_cstring	11	isc_arg_mpexl_ipc
4	isc_arg_number	15	isc_arg_next_mach
5	isc_arg_interpreted	16	isc_arg_netware
6	isc_arg_vms	17	isc_arg_win32
7	isc_arg_unix
Meaning of the Second Long in a Cluster

The second long in a cluster is always one of five items:

An InterBase error code (1st long = 1).

A string address (1st long = 2 or 5).

A string length (1st long = 3).

A numeric value (1st long = 4).

An operating system error code (1st long > 5).

InterBase Error Codes

InterBase error codes have two uses. First, they are used internally by InterBase functions to build and display descriptive error message strings. For example, isc_interprete() calls another function which uses the InterBase error code to retrieve a base error message from which it builds an error message string you can display or store in a log file.

Secondly, when you write your own error-handling routine, you can examine the status vector directly, trapping for and reacting to specific InterBase error codes.

When the second long of a cluster is an InterBase error code, then subsequent clusters may contain additional parameters for the error message string associated with the error code. For example, a generic InterBase error message may contain a replaceable string parameter for the name of the table where an error occurs, or it may contain a replaceable numeric parameter for the code of the trigger which trapped the error condition.

If you write your own parsing routines, you may need to examine and use these additional clusters of error information.

String Addresses

String addresses point to error message text. When the first long in the cluster is 2 (isc_arg_string), the address pointed to often contains the name of the database, table, or column affected by the error. In these cases, InterBase functions which build error message strings replace a parameter in a generic InterBase error message with the string pointed to by this address. Other times the address points to an error message hard-coded in a database trigger.

When the first long in the cluster is 5 (isc_arg_interpreted), the address points to a text message which requires no further processing before retrieval. Sometimes this message may be hard-coded in InterBase itself, and other times it may be a system-level error message.

In either of these cases, InterBase functions such as isc_print_status() and isc_interprete() can format and display the resulting error message for you.

String Length Indicators

When the first long in a cluster is 3 (isc_arg_cstring), the numeric value in the second long indicates the length, in bytes, of a message string whose address is stored in the third long in the cluster. This string requires translation into a standard, null-terminated C string before display.

Numeric Values

A numeric value has different meaning depending upon the value of the numeric descriptor in the first long of a cluster. If the first long is 4 (isc_arg_number), a numeric value is used by InterBase functions to replace numeric parameters in generic InterBase error messages during message building. For instance, when an integrity error occurs, InterBase stores the code of the trigger which detects the problem as a numeric value in the status vector. When an InterBase function like isc_interprete() builds the error message string for this error, it inserts the numeric value from the status vector into the generic InterBase integrity error message string to make it more specific.

Operating System Error Codes

If the first long in a cluster is greater than 5, the numeric value in the second long is an error code specific to a particular platform or operating system. InterBase functions should not be used to retrieve and display the specific platform or operating system error message. Consult your operating system manual for information on how to handle such errors.

Meaning of the Third Long in a Cluster

If the first long in a cluster is 3 (isc_arg_cstring), the cluster's total length is three longs. The third long always contains the address of a message string requiring translation into a standard, null-terminated C string before display. Such a string is often a file or path name. InterBase functions like isc_interprete() automatically handle this translation for you.

Status Vector Parsing Example

The following C example illustrates a simple, brute force parsing of the status vector. The code forces an error condition. The error-handling block parses the status vector array cluster by cluster, printing the contents of each cluster and interpreting it for you.

#include <ibase.h>

. . .

ISC_STATUS status_vector[20];

main()

{

int done, v;	/* end of args?, index into vector */

int c, extra;	/* cluster count, 3 long cluster flag */

static char *meaning[] = {"End of error information",

"n InterBase error code"," string address"," string length",

" numeric value"," system code"};

/* Assume database is connected and transaction started here. */

if (status_vector[0] == 1 && status_vector[1] > 0)

error_exit();

. . .

}

void error_exit(void)

{

done = v = 0;

c = 1;

while (!done)

{

extra = 0;

printf("Cluster %d:\n", c);

printf("Status vector %d: %ld: ", v, status_vector[v]);

if (status_vector[v] != gds_arg_end)

printf("Next long is a");

switch (status_vector[v++])

{

case gds_arg_end:

printf("%s\n", meaning[0]);

done = 1;

break;

case gds_arg_gds:

printf("%s\n", meaning[1]);

break;

case gds_arg_string:

case gds_arg_interpreted:

printf("%s\n", meaning[2]);

break;

case gds_arg_number:

printf("%s\n", meaning[4]);

break;

case gds_arg_cstring:

printf("%s\n", meaning[3]);

extra = 1;

break;

default:

printf("%s\n", meaning[5]);

break;

}

if (!done)

{

printf("Status vector %d: %ld", v, status_vector[v]);

v++;	/* advance vector pointer */

c++;	/* advance cluster count */

if (extra)

{

printf(": Next long is a %s\n", meaning[2]);

printf("Status vector: %d: %ld\n\n", v,
status_vector[v]);

v++;

}

else

printf("\n\n");

}

}

isc_rollback_transaction(status_vector, &trans);

isc_detach_database(&db1);

return(1);

}

Here is a sample of the output from this program:

Cluster 1:

Status vector 0: 1: Next long is an InterBase error code

Status vector 1: 335544342

Cluster 2:

Status vector 2: 4: Next long is a numeric value

Status vector 3: 1

Cluster 3:

Status vector 4: 1: Next long is an InterBase error code

Status vector 5: 335544382

Cluster 4:

Status vector 6: 2: Next long is a string address

Status vector 7: 156740

Cluster 5:

Status vector 8: 0: End of error information

This output indicates that two InterBase errors occurred. The first error code is 335544342. The error printing routines, isc_print_status() and isc_interprete(), use the InterBase error code to retrieve a corresponding base error message. The base error message contains placeholders for replaceable parameters. For error code 335544342, the base error message string is:

"action cancelled by trigger (%ld) to preserve data integrity"

This error message uses a replaceable numeric parameter, %ld.

In this case, the numeric value to use for replacement, 1, is stored in the second long of the second cluster. When the error printing routine inserts the parameter into the message, it displays the message:

action cancelled by trigger (1) to preserve data integrity

The second error code is 335544382. The base message retrieved for this error code is:

"%s"

In this case, the entire message to be displayed consists of a replaceable string. The second long of the fourth cluster contains the address of the replacement string, 156740. This is an error message defined in the trigger that caused the error. When the error printing routine inserts the message from the trigger into the base message, it displays the resulting message:

-Department name is missing.

Note:	This sample program is only meant to illustrate the structure of the status vector and its contents. While the error-handling routine in this program might serve as a limited debugging tool for a program under development, it does not provide useful information for end users. Ordinarily, error-handling blocks in applications should interpret errors, display explanatory error messages, and take corrective action, if appropriate.

For example, if the error-handling routine in the sample program had called isc_print_status() to display the error messages associated with these codes, the following messages would have been displayed:

action cancelled by trigger (1) to preserve data integrity
-Department name is missing.


Working With Events

This section of the Help files describes how to work with events, a message passed from a trigger or stored procedure to an application to announce the occurrence of a specified condition or action, usually a database change such as an insertion, modification, or deletion of a record. It explains how to set up event buffers, and use the following API functions to make synchronous and asynchronous event calls. In the following table, functions are listed in the order they typically appear in an application:

Function	Purpose
isc_event_block()	Allocate event parameter buffers.
isc_wait_for_event()	Wait for a synchronous event to be posted.
isc_que_events()	Set up an asynchronous event and return to application processing.
isc_event_counts()	Determine the change in values of event counters in the event parameter buffer.
isc_cancel_events()	Cancel interest in an event.
For asynchronous events, this part of the Help file also describes how to create an asynchronous trap (AST), a function that responds to posted events.


Understanding the Event Mechanism

The InterBase event mechanism consists of four parts:

The InterBase engine that maintains an event queue and notifies applications when an event occurs.

Event parameter buffers set up by an application where it can receive notification of events.

An application that registers interest in one or more specified, named events and either waits for notification to occur (synchronous event),
or passes a pointer to an AST function that handles notifications so that application processing can continue in the meantime (asynchronous event).

A trigger or stored procedure that notifies the engine that a specific, named event has occurred. Notification is called posting.

The InterBase event mechanism enables applications to respond to actions and database changes made by other, concurrently running applications without the need for those applications to communicate directly with one another, and without incurring the expense of CPU time required for periodic polling to determine if an event has occurred.

For information about creating triggers and stored procedures that post events, see the Data Definition Guide.


Event Parameter Buffers

If an application is to receive notification about events, it must set up two identically-sized event parameter buffers (EPBs) using isc_event_block(). The first buffer, event_buffer, is used to hold the count of event occurrences before the application registers an interest in the event. The second buffer, result_buffer, is subsequently filled in with an updated count of event occurrences when an event of interest to the application occurs. A second API function, isc_event_counts(), determines the differences between item counts in these buffers to determine which event or events occurred.


Synchronous Event Notification

When an application depends on the occurrence of a specific event for processing, it should use synchronous event notification to suspend its own execution until the event occurs. For example, an automated stock trading application that buys or sells stock when specific price changes occur might start execution, set up EPBs, register interest in a set of stocks, then suspend its own execution until those price changes occur.

The isc_wait_for_event() function provides synchronous event handling for an application.


Asynchronous Event Notification

When an application needs to react to possible database events, but also needs to continue processing whether or not those events occur, it should set up an asynchronous trap (AST) function, and use asynchronous event notification to register interest in events while continuing its own processing. For example, a stock brokering application requires constant access to a database of stocks to allow a broker to buy and sell stock, but, at the same time, may want to use events to alert the broker to particularly significant or volatile stock price changes.

The isc_que_events() function and the AST function provide asynchronous event handling for an application.


Transaction Control of Events

Events occur under transaction control, and can therefore be committed or rolled back. Interested applications do not receive notification of an event until the transaction from which the event is posted is committed. If a posted event is rolled back, notification does not occur.

A transaction can post the same event more than once before committing, but regardless of how many times an event is posted, it is regarded as a single event occurrence for purposes of event notification.


Creating EPBs with Isc_event_block( )

Before an application can register interest in an event, it must establish and populate two event parameter buffers (EPBs), one for holding the initial occurrence count values for each event of interest, and another for holding the changed occurrence count values. These buffers are passed as parameters to several API event functions.

In C, each EPB is declared as a char pointer, as follows:

char *event_buffer, *result_buffer;

Once the buffers are declared, isc_event_block() is called to allocate space for them, and to populate them with starting values.

isc_event_block() also requires at least two additional parameters: the number of events in which an application is registering interest, and, for each event, a string naming the event. A single call to isc_event_block() can pass up to 15 event name strings. Event names must match the names of events posted by stored procedures or triggers.

isc_event_block() allocates the same amount of space for each EPB, enough to handle each named event. It returns a single value, indicating the size, in bytes, of each buffer.

The syntax for isc_event_block() is:

ISC_LONG isc_event_block(
char **event_buffer,
char **result_buffer,
unsigned short id_count,
. . . );

For example, the following code sets up EPBs for three events:

#include <ibase.h>;

. . .

char *event_buffer, *result_buffer;

long blength;

. . .

blength = isc_event_block(&event_buffer, &result_buffer, 3, "BORL", "INTEL", "SUN");

. . .

This code assumes that there are triggers or stored procedures defined for the database that post events named "BORL", "INTEL", and "SUN".

Tip:	Applications that need to respond to more than 15 events can make multiple calls to isc_event_block(), specifying different EPBs and event lists for each call.


Waiting on Events With Isc_wait_for_event( )

After setting up EPBs and specifying events of interest with isc_event_block(), an application can use isc_wait_for_event() to register interest in those events and pause its execution until one of the events occurs.

Important:	 isc_wait_for_event() cannot be used in Microsoft Windows applications or under any other operating system that does not permit processes to pause. Applications on these platforms must use asynchronous event handling.

The syntax for isc_wait_for_event() is:

ISC_STATUS isc_wait_for_event(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
short length,
char *event_buffer,
char *result_buffer);

For example, the following code sets up EPBs for three events, then calls isc_wait_for_event() to suspend its execution until one of the events occurs:

#include <ibase.h>;

. . .

char *event_buffer, *result_buffer;

long blength;

ISC_STATUS status_vector[20];

isc_db_handle db1;

. . .

/* Assume database db1 is attached here and a transaction started. */

blength = isc_event_block(&event_buffer, &result_buffer, 3, "BORL", "INTEL", "SUN");

isc_wait_for_event(status_vector, &db1, (short)blength,
event_buffer, result_buffer);

/* Application processing is suspended here until an event occurs. */

. . .

Once isc_wait_for_event() is called, application processing stops until one of the requested events is posted. When the event occurs, application processing resumes at the next executable statement following the call to isc_wait_for_event(). If an application is waiting on more than one event, it must use isc_event_counts() to determine which event was posted.

Note:	A single call to isc_wait_for_event() can wait on a maximum of 15 events. Applications that need to wait on more than 15 events must wait on one set of 15, then make another call to isc_wait_for_event() to wait on additional events.


Continuous Processing With Isc_que_events( )

isc_que_events() is called to request asynchronous notification of events listed in an event buffer passed as an argument. Upon completion of the call, but before any events are posted, control is returned to the calling application so that it can continue processing.

When a requested event is posted, InterBase calls an asynchronous trap (AST) function, also passed as a parameter to isc_que_events(), to handle the posting. The AST is a function or subroutine in the calling application, the sole purpose of which is to process the event posting for the application.

The syntax for isc_que_events() is:

ISC_STATUS isc_que_events(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
ISC_LONG *event_id,
short length,
char *event_buffer,
isc_callback event_function,
void *event_function_arg);

event_id is a long pointer that is used as a handle in subsequent calls to isc_cancel_events() to terminate event notification. It need not be initialized when passed. The length parameter is the size of event_buffer, which contains the current count of events to be waited upon. event_function is a pointer to the AST function that InterBase should call when an event of interest is posted. It is up to the AST function to notify the application that it has been called, perhaps by setting a global flag of some kind. event_function_arg is a pointer to the first parameter to pass to the AST.


Creating an AST

The event function, event_function, should be written to take three arguments:

The event_function_arg specified in the call to isc_que_events(). This is usually a pointer to the event parameter buffer that should be filled in with updated event counts.

The length of the following events_list buffer.

A pointer to the events_list buffer, a temporary event parameter buffer just like that passed to isc_que_events(), except for having updated event counts.

A result buffer is not automatically updated by the event occurrence; it is up to the event_function to copy the temporary events_list buffer to the more permanent buffer that the application utilizes.

event_function also needs to let the application know that it has been called, for example, by setting a global flag.

A sample event_function appears in the following example:

isc_callback event_function

(char *result, short length, char *updated)

{

/* Set the global event flag. */

event_flag++

/* Copy the temporary updated buffer to the result buffer. */

while (length--)

*result++ = *updated++;

return(0);

};


A Complete Isc_que_events( ) Example

The following program fragment illustrates calling isc_que_events() to wait asynchronously for event occurrences. Within a loop, it performs other processing, and checks the event flag (presumably set by the specified event function) to determine when an event has been posted. If one has, the program resets the event flag, calls isc_event_counts() to determine which events have been posted since the last call to isc_que_events(), and calls isc_que_events() to initiate another asynchronous wait.

#include <ibase.h>

#define number_of_stocks 3;

#define MAX_LOOP 10

char *event_names[] = {"DEC", "HP", "SUN"};

char *event_buffer, *result_buffer;

ISC_STATUS status_vector[20];

short length;

ISC_LONG event_id;

int i, counter;

int event_flag = 0;

length = (short)isc_event_block(

&event_buffer,

&result_buffer,

number_of_stocks,

"DEC", "HP", "SUN");

isc_que_events(

status_vector,

&database_handle, /* Set in previous isc_attach_database(). */

&event_id,

length, /* Returned from isc_event_block(). */

event_buffer,

(isc_callback)event_function,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

};

counter = 0;

while (counter < MAX_LOOP)

{

counter++;

if (!event_flag)

{

/* Do whatever other processing you want. */

;

}

else

{	event_flag = 0;

isc_event_counts(

status_vector,

length,

event_buffer,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

};

for (i=0; i<number_of_stocks; i++)

if (status_vector[i])

{

/* The event has been posted. Do whatever is appropriate,
e.g., initiating a buy or sell order.

Note: event_names[i] tells the name of the event
corresponding to status_vector[i]. */

;

}

isc_que_events(

status_vector,

&database_handle,

&event_id,

length,

event_buffer,

(isc_callback)event_function,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

}

}	/* End of else. */

}	/* End of while. */

/* Let InterBase know you no longer want to wait asynchronously. */

isc_cancel_events(

status_vector,

&database_handle,

&event_id);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

}


Determining Which Events Occurred With Isc_event_counts( )

When an application registers interest in multiple events and receives notification that an event occurred, the application must use isc_event_counts() to determine which event or events occurred. isc_event_counts() subtracts values in the event_buffer array from the values in the result_buffer array to determine the number of times each event has occurred since an application registered interest in a set of events. event_buffer and result_buffer are variables declared within an application, and allocated and initialized by isc_event_block().

The difference of each element is returned in the error status array that is passed to isc_event_counts(). To determine which events occurred, an application must examine each element of the array for nonzero values. A nonzero count indicates the number of times an event is posted between the time isc_event_block() is called and the first time an event is posted after isc_wait_for_event() or isc_que_events() are called. Where multiple applications are accessing the same database, therefore, a particular event count may be 1 or more, and more than one event count element may be nonzero.

Note:	When first setting up an AST to trap events with isc_que_events(), InterBase initializes all count values in the status vector to 1, rather than 0. To clear the values, call isc_event_counts() to reset the values.

In addition to determining which event occurred, isc_event_counts() reinitializes the event_buffer array in anticipation of another call to isc_wait_for_event() or isc_que_events(). Values in event_buffer are set to the same values as corresponding values in result_buffer.

The complete syntax for isc_event_counts() is:

void isc_event_counts(
ISC_STATUS status_vector,
short buffer_length,
char *event_buffer,
char *result_buffer);

For example, the following code declares interest in three events, waits on them, then uses isc_event_counts() to determine which events occurred:

#include <ibase.h>;

. . .

char *event_buffer, *result_buffer;

long blength;

ISC_STATUS status_vector[20];

isc_db_handle db1;

long count_array[3];

int i;

. . .

/* Assume database db1 is attached here and a transaction started. */

blength = isc_event_block(&event_buffer, &result_buffer, 3, "BORL", "INTEL", "SUN");

isc_wait_for_event(status_vector, &db1, (short)blength,
event_buffer, result_buffer);

/* Application processing is suspended here until an event occurs. */

isc_event_counts(status_vector, (short)blength, event_buffer,
result_buffer);

for (i = 0; i < 3; i++)

{

if (status_vector[i])

{

/* Process the event here. */

;

}

}


Canceling Interest in Asynchronous Events With Isc_cancel_events( )

An application that requested asynchronous event notification with isc_que_events() can subsequently cancel the notification request at any time with isc_cancel_events() using the following syntax:

ISC_STATUS isc_cancel_events(
ISC_STATUS *status_vector,
isc_db_handle *db_handle,
ISC_LONG *event_id);

event_id is an event handle set in a previous call to isc_que_events(). For example, the following code cancels interest in the event or events identified by event_id:

include <ibase.h>;

. . .

/* For example code leading up to this call, see the code example

in the topic "Continuous Processing with isc_event_que()" */

isc_cancel_events(status_vector, &db_handle, &event_id);


Data Structures

This appendix documents the data structures, compile-time constants, parameter buffers, and information buffers utilized in InterBase API applications.

This information also appears throughout the rest of this API Guide, but is consolidated here as a convenience. See other sections of this manual for more information.

All the structures and compile-time constants mentioned are defined in the ibase.h header file.

Items are documented alphabetically, as follows:

Array Descriptor

BLOB Descriptor

BLOB Information Item-list Buffer and Result Buffer

BLOB Parameter Buffer

Database Information Request Buffer and Result Buffer

Database Parameter Buffer

SQL Data Type Macro Constants

Status Vector

Transaction Parameter Buffer

XSQLDA and XSQLVAR Structures


Array Descriptor

An array descriptor ISC_ARRAY_DESC is a structure defined in the ibase.h header file as:

typedef struct {

unsigned char array_desc_dtype;

char array_desc_scale;

unsigned short array_desc_length;

char array_desc_field_name [32];

char array_desc_relation_name [32];

short array_desc_dimensions;

short array_desc_flags;

ISC_ARRAY_BOUND array_desc_bounds [16];

} ISC_ARRAY_DESC;

ISC_ARRAY_BOUND is defined as:

typedef struct {

short array_bound_lower; /* lower bound */

short array_bound_upper; /* upper bound */

} ISC_ARRAY_BOUND;

Field	Description
array_desc_dtype	Data type (see below).
array_desc_scale	Scale for numeric data types.
array_desc_length	Length in bytes of each array element.
array_desc_field_name	NULL-terminated column name.
array_desc_relation_name	NULL-terminated relation name.
array_desc_dimensions	Number of array dimensions.
array_desc_flags	Specifies whether array is to be accessed in row-major or column-major order. 0: row-major 1: column-major
array_desc_bounds	Lower and upper bounds for each dimension.


Data Types For Array Descriptors

The array_desc_dtype field of an array descriptor must be expressed as one of the data types in the following table:

array_desc_dtype value	Corresponding InterBase Data Type
blr_text	CHAR
blr_text2	CHAR
blr_short	SMALLINT
blr_long	INTEGER
blr_quad	ISC_QUAD structure
blr_float	FLOAT
blr_double	DOUBLE PRECISION
blr_date	DATE
blr_varying	VARCHAR
blr_varying2	VARCHAR
blr_blob_id	ISC_QUAD structure
blr_cstring	NULL-terminated string
blr_cstring2	NULL-terminated string


BLOB Descriptor

A BLOB descriptor is defined as:

typedef struct {

   short blob_desc_subtype;

   short blob_desc_charset;

   short blob_desc_segment_size;

   unsigned char blob_desc_field_name [32];

   unsigned char blob_desc_relation_name [32];

} ISC_BLOB_DESC;

Field	Description
blob_desc_subtype	Type of BLOB data. 0: unstructured 1: TEXT negative integer between -1 and -32678: user-defined subtype
blob_desc_charset	Character set (see below).
blob_desc_segment_size	Segment size.
blob_desc_field_name	NULL-terminated column name.
blob_desc_relation_name	NULL-terminated table name.


Character Sets

InterBase supports a multitude of character sets. For a list of the character sets supported, and the character set value that must be entered in the
blob_desc_charset field of a BLOB descriptor, see the Data Definition Guide.


BLOB Information Item-list Buffer and Result Buffer

The isc_blob_info() call enables an application to query for BLOB information such as the total number of segments in the BLOB, or the length, in bytes, of the longest segment.

isc_blob_info() requires two application-provided buffers, an item-list buffer, where the application specifies the information it needs, and a result buffer, where InterBase returns the requested information. An application populates the item-list buffer with information prior to calling isc_blob_info(). InterBase returns information in the result buffer. If InterBase attempts to pass back more information than can fit in the result buffer, it puts the value, isc_info_truncated, defined in ibase.h, in the final byte of the result buffer.


Item-list Buffer

The item-list buffer is a byte vector into which is placed a sequence of byte values, one per requested item of information. Each byte is an item type, specifying the kind of information desired. Compile-time constants for all item types are defined in ibase.h:

#define isc_info_blob_num_segments        4

#define isc_info_blob_max_segment         5

#define isc_info_blob_total_length        6

#define isc_info_blob_type                7


Result Buffer (BLOB information)
Result Buffer

The result buffer is a byte vector in which InterBase returns a series of clusters of information, one per item requested. Each cluster consists of three parts:

A one-byte item type. Each is the same as one of the item types in the item-list buffer.

A 2-byte number specifying the number of bytes that follow in the remainder of the cluster.

A value, stored in a variable number of bytes, whose interpretation depends on the item type.

A calling program is responsible for interpreting the contents of the result buffer and for deciphering each cluster as appropriate.

Notes:	The clusters returned to the result buffer are not aligned. Furthermore, all numbers are represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers have the sign in the last byte. Convert the numbers to a data type native to your system, if necessary, before interpreting them. The API call, isc_vax_integer(), can be used to perform the conversion.


BLOB Parameter Buffer

A BLOB Parameter Buffer (BPB) is an application-defined byte vector, passed as an argument to isc_open_blob2() or isc_create_blob2(), that specifies BLOB attributes required for filtering BLOB data.

A BPB consists of the following parts:

A byte specifying the version of the parameter buffer, always the compile-time constant, isc_bpb_version1.

A contiguous series of one or more clusters of bytes, each describing a single parameter.

Each cluster consists of the following parts:

A one-byte parameter type. There are compile-time constants defined for all the parameter types (e.g., isc_bpb_target_type).

A one-byte number specifying the number of bytes that follow in the remainder of the cluster.

A variable number of bytes, whose interpretation depends on the parameter type.

Note:	All numbers in the BLOB parameter buffer must be represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers should have the sign in the last byte. The API function isc_vax_integer() can be used to reverse the byte order of a number.

The following table lists the parameter types and their meaning.

Parameter Type	Description
isc_bpb_target_type	Target subtype.
isc_bpb_source_type	Source subtype.
isc_bpb_target_interp	Target character set.
isc_bpb_source_interp	Source character set.


Database Information Request Buffer and Result Buffer

The isc_database_info() call enables an application to query for information about an attached database.

isc_database_info() requires two application-provided buffers, a request buffer, where the application specifies the information it needs, and a result buffer, where InterBase returns the requested information. An application populates the request buffer with information prior to calling isc_database_info(). InterBase returns information in the result buffer. If InterBase attempts to pass back more information than can fit in the result buffer, it puts the value, isc_info_truncated, defined in ibase.h, in the final byte of the result buffer.


Request Buffer

The request buffer is a byte vector into which is placed a sequence of byte values, one per requested item of information. Each byte is an item type, specifying the kind of information desired. Compile-time constants for all item types are defined in ibase.h and shown below.


Result Buffer (database information)

The result buffer is a byte vector in which InterBase returns a series of clusters of information, one per item requested. Each cluster consists of three parts:

A one-byte item return type. These are the same as the item types specified in the request buffer.

A two-byte number specifying the number of bytes that follow in the remainder of the cluster.

A value, stored in a variable number of bytes, whose interpretation (e.g., as a number or as a string of characters) depends on the item return type.

A calling program is responsible for interpreting the contents of the result buffer and for deciphering each cluster as appropriate. In many cases, the value simply contains a number or a string (sequence of characters). But in other cases, the value is a number of bytes whose interpretation depends on the item return type.

Note:	The clusters returned to the result buffer are not aligned. Furthermore, all numbers are represented in a generic format, with the least significant byte first, and the most significant byte last. Signed numbers have the sign in the last byte. Convert the numbers to a data type native to your system, if necessary, before interpreting them. The API call, isc_vax_integer(), can be used to perform the conversion.

In addition to the information InterBase returns in response to a request, InterBase can also return one or more of the following status messages to the result buffer. Each status message is one unsigned byte in length:

Item	Description
isc_info_end	End of the messages.
isc_info_truncated	Result buffer is too small to hold any more requested information.
isc_info_error	Requested information is unavailable. Check the status vector for an error code and message.


Database Parameter Buffer

A Database Parameter Buffer (DPB) is an application-defined byte vector, passed as an argument to isc_attach_database(), that specifies desired database characteristics.

A DPB consists of the following parts:

A byte specifying the version of the parameter buffer, always the compile-time constant, isc_dpb_version1.

A contiguous series of one or more clusters of bytes, each describing a single parameter.

Each cluster consists of the following parts:

A one-byte parameter type. There are compile-time constants defined for all the parameter types (e.g., isc_dpb_num_buffers).

A one-byte number specifying the number of bytes that follow in the remainder of the cluster.

A variable number of bytes, whose interpretation (e.g., as a number or as a string of characters) depends on the parameter type.

The following table lists DPB items by purpose:

User Validation	Item
User name.	isc_dpb_user_name
Password.	isc_dpb_password
Encrypted password.	isc_dpb_password_enc
System database administrator's user name.	isc_dpb_sys_user_name
Authorization key for a software license.	isc_dpb_license
Database encryption key.	isc_dpb_encrypt_key
Environmental Control	Item
Number of cache buffers.	isc_dpb_num_buffers
dbkey context scope.	isc_dpb_dbkey_scope
System Management	Item
Force writes to the database to be done asynchronously or synchronously.	isc_dpb_force_write
Specify whether or not to reserve a small amount of space on each database page for holding backup versions of records when modifications are made.	isc_dpb_no_reserve
Specify whether or not the database should be marked as damaged.	isc_dpb_damaged
Perform consistency checking of internal structures.	isc_dpb_verify
Shadow Control	Item
Activate the database shadow, an optional, duplicate, in-sync copy of the database.	isc_dpb_activate_shadow
Delete the database shadow.	isc_dpb_delete_shadow
Replay Logging System Control	Item
Activate a replay logging system to keep track of all database calls.	isc_dpb_begin_log
Deactivate the replay logging system.	isc_dpb_quit_log
Character Set and Message File Specification	Item
Language-specific message file.	isc_dpb_lc_messages
Character set to be utilized.	isc_dpb_lc_ctype
The following table lists DPB parameters in alphabetical order. For each parameter, it lists its purpose, the length, in bytes, of any values passed with the parameter, and the value to pass:

Parameter	Purpose	Length	Value
isc_dpb_activate_shadow	Directive to activate the database shadow, which is an optional, duplicate, in-sync copy of the database.	1 (Ignored)	0 (Ignored)
isc_dpb_damaged	Number signifying whether or not the database should be marked as damaged.  1 = mark as damaged,  0 = do not mark as damaged.	1	0 or 1
isc_dpb_dbkey_scope	Scope of dbkey context. 0 limits scope to the current transaction, 1 extends scope to the database session.	1	0 or 1
isc_dpb_delete_shadow	Directive to delete a database shadow that is no longer needed.	1 (Ignored)	0 (Ignored)
isc_dpb_encrypt_key	String encryption key, up to 255 characters.	Number of bytes in string	String containing key
isc_dpb_force_write	Specifies whether database writes are synchronous or asynchronous. 0 = asynchronous, 1 = synchronous.	1	0 or 1
isc_dpb_lc_ctype	String specifying the character set to be utilized.	Number of bytes in string	String containing character set name
isc_dpb_lc_messages	String specifying a language-specific message file.	Number of bytes in string	String containing message file name
isc_dpb_license	String authorization key for a software license.	Number of bytes in string	String containing key
isc_dpb_no_reserve	Specifies whether or not a small amount of space on each database page is reserved for holding backup versions of records when modifications are made. Keeping backup versions on the same page as the primary record optimizes update activity.	0 (default) = reserve space.	1= do not reserve space.10 or 1
isc_dpb_num_buffers	Number of database cache buffers to allocate for use with the database. Default: 75.	1	Number of buffers to allocate
isc_dpb_password	String password, up to 255 characters.	Number of bytes in string	String containing password
isc_dpb_password_enc	String encrypted password, up to 255 characters.	Number of bytes in string	String containing password
isc_dpb_sys_user_name	String system DBA name, up to 255 characters.	Number of bytes in string	String containing SYSDBA name
isc_dpb_user_name	String user name, up to 255 characters.	Number of bytes in string	String containing user name
Note:	Some parameters, such as isc_dpb_delete_shadow, are directives that do not require additional parameters. Even so, you must still provide length and value bytes for these parameters. Set length to 1, and value to 0. InterBase ignores these parameter values, but they are required to maintain the format of the DPB.


Status Vector

Most API functions return status information that indicates success or failure. Status information is reported in an error status vector, declared in applications as an array of twenty long integers, using the following syntax:

#include "ibase.h"

. . .

ISC_STATUS status_vector[20];

If you plan to write your own routines instead of the InterBase error-handling routines to read and react to the contents of the status vector, you need to know how to interpret it.

InterBase stores error information in the status vector in clusters of two or three longs. The first cluster in the status vector always indicates the primary cause of the error. Subsequent clusters may contain supporting information about the error, for example, strings or numbers for display in an associated error message. The actual number of clusters used to report supporting information varies from error to error.

In many cases, additional errors may be reported in the status vector. Additional errors are reported immediately following the first error and its supporting information, if any. The first cluster for each additional error message identifies the error. Subsequent clusters may contain supporting information about the error.

Meaning of the First Long in a Cluster

The first long in any cluster is a numeric descriptor. By examining the numeric descriptor for a cluster, you can always determine the:

Total number of longs in the cluster.

Kind of information reported in the remainder of the cluster.

Starting location of the next cluster in the status vector.

The following table lists possible values for the first long in any cluster in the status vector. Note that the first cluster in the status vector can only contain values of 0, 1, or greater than 4:

Interpretation of 1st Longword in a Cluster

Value	Longwords  in Cluster	Meaning
0	--	End of error information in the status vector.
1	2	Second long is an InterBase error code.
2	2	Second long is the address of string used as a replaceable parameter in a generic InterBase error message.
3	3	Second long is the length, in bytes, of a variable length string provided by the operating system (most often this string is a file name). Third long is the address of the string.
4	2	Second long is a number used as a replaceable parameter in a generic InterBase error message.
5	2	Second long is the address of an error message string requiring no further processing before display.
6	2	Second long is a VAX/VMS error code.
7	2	Second long is a Unix error code.
8	2	Second long is an Apollo Domain error code.
9	2	Second long is an MS-DOS or OS/2 error code.
10	2	Second long is an HP MPE/XL error code.
11	2	Second long is an HP MPE/XL IPC error code.
12	2	Second long is a NeXT/Mach error code.
Note:	As InterBase is adapted to run on other hardware and software platforms, additional numeric descriptors for specific platform and operating system error codes may be added to the end of this list.

The following table lists the ibase.h #define equivalents of each numeric descriptor:

Value	#define
0	isc_arg_end
1	isc_arg_gds
2	isc_arg_string
3	isc_arg_cstring
4	isc_arg_number
5	isc_arg_interpreted
6	isc_arg_vms
7	isc_arg_domain
8	isc_arg_unix
9	isc_arg_dos
10	isc_arg_mpexl
11	isc_arg_mpexl_ipc
15	isc_arg_next_mach
16	isc_arg_netware
17	isc_arg_win32


Transaction Parameter Buffer

The transaction parameter buffer (TPB) is an optional, application-defined byte vector, passed as an argument to isc_start_transaction(), that sets up a transaction's attributes, its operating characteristics, such as whether the transaction has read and write access to tables, or read-only access, and whether or not other simultaneously active transactions can share table access with the transaction. Each transaction may have its own TPB, or transactions that share operating characteristics can use the same TPB.

Note:	If a TPB is not created for a transaction, a NULL pointer must be passed to isc_start_transaction() in its place. A default set of attributes is automatically assigned to such transactions.

A TPB is declared in a C program as a char array of one-byte elements. Each element is a parameter that describes a single transaction attribute. The first element in every TPB must be the isc_tpb_version3 constant. The following table lists available TPB constants, describes their purposes, and indicates which constants are assigned as a default set of attributes when a NULL TPB pointer is passed to isc_start_transaction():

Parameter	Description
isc_tpb_version3	InterBase version 3 transaction.
isc_tpb_consistency	Table-locking transaction model.
isc_tpb_concurrency	High throughput, high concurrency transaction with acceptable consistency. Use of this parameter takes full advantage of the InterBase multi-generational transaction model. (Default.)
isc_tpb_shared	Concurrent, shared access of a specified table among all transactions. Use in conjunction with isc_tpb_lock_read and isc_tpb_lock_write to establish the lock option. (Default.)
isc_tpb_protected	Concurrent, restricted access of a specified table. Use in conjunction with isc_tpb_lock_read and isc_tpb_lock_write to establish the lock option.
isc_tpb_wait	Lock resolution specifies that the transaction is to wait until locked resources are released before retrying an operation. (Default.)
isc_tpb_nowait	Lock resolution specifies that the transaction is not to wait for locks to be released, but instead, a lock conflict error should be returned immediately.
isc_tpb_read	Access mode of read-only that allows a transaction only to select data from tables.
isc_tpb_write	Access mode of read-write that allows a transaction to select, insert, update, and delete table data. (Default.)
isc_tpb_lock_read	Read-only access of a specified table. Use in conjunction with isc_tpb_shared, isc_tpb_protected, and isc_tpb_exclusive to establish the lock option.
isc_tpb_lock_write	Read-write access of a specified table. Use in conjunction with isc_tpb_shared, isc_tpb_protected, and isc_tpb_exclusive to establish the lock option. (Default.)
isc_tpb_read_committed	High throughput, high concurrency transaction that can read changes committed by other concurrent transactions. Use of this parameter takes full advantage of the InterBase multi-generational transaction model.
isc_tpb_rec_version	Enables an isc_tpb_read_committed transaction to read the most recently committed version of a record even if other, uncommitted versions are pending.
isc_tpb_no_rec_version	Enables an isc_tpb_read_committed transaction to read only the latest committed version of a record. If an uncommitted version of a record is pending and isc_tpb_wait is also specified, then the transaction waits for the pending record to be committed or rolled back before proceeding. Otherwise, a lock conflict error is reported at once.
TPB parameters specify the following classes of information:

Transaction version number is used internally by the InterBase engine. It is always be the first attribute specified in the TPB, and must always be set to isc_tpb_version3.

Access mode describes the actions that can be performed by the functions associated with the transaction. Valid access modes are:

isc_tpb_read

isc_tpb_write

Isolation level describes the view of the database given a transaction as it relates to actions performed by other simultaneously occurring transactions. Valid isolation levels are:

isc_tpb_concurrency

isc_tpb_consistency

isc_tpb_read_committed, isc_tpb_rec_version

isc_tpb_read_committed, isc_tpb_no_rec_version

Lock resolution describes how a transaction should react if a lock conflict occurs. Valid lock resolutions are:

isc_tpb_wait

isc_tpb_nowait

Table reservation optionally describes an access method and lock resolution for a specified table that the transaction accesses. When table reservation is used, tables are reserved for the specified access when the transaction is started, rather than when the transaction actually accesses the table. Valid reservations are:

isc_tpb_shared, isc_tpb_lock_write

isc_tpb_shared, isc_tpb_lock_read

isc_tpb_protected, isc_tpb_lock_write

isc_tpb_protected, isc_tpb_lock_read