Enterprise Edition Home | Express Edition Home | Previous Page | Next Page   Creating User-Defined Routines > Writing a User-Defined Routine > Performing Input and Output >

Access to a Stream (Server)

The DataBlade API provides a stream I/O interface, which enables you to use the same function calls to access different objects. Stream is a generic term for an object that can be written to or read from. A stream has the following information associated with it:

To provide access to a stream from within a C UDR, the DataBlade API has the MI_STREAM data type structure for stream descriptors. An MI_STREAM structure contains information about a stream on a particular object. The following table summarizes the memory operations for a stream descriptor.

Memory Duration Memory Operation Function Name
Current memory duration Constructor mi_stream_open_fio( ),
mi_stream_open_mi_lvarchar( ),
mi_stream_open_str( )

Other, user-defined stream-open functions

Destructor mi_stream_close( )
To access a stream in your UDR
  1. Open the stream with the appropriate type-specific stream-open function.

    The stream-open function is the stream I/O function that opens the stream, making the data available for a read or write operation. It returns a pointer to a stream descriptor, which the C UDR uses to access the stream. For more information, see The Stream-Open Function.

  2. Access the opened stream with the appropriate generic stream I/O function.

    Once a particular stream is open, a UDR can use the generic functions of the stream I/O interface to access the associated I/O object. Each of the generic stream I/O functions requires a stream descriptor for the stream on which the function is to operate. The usual sequence of access is to seek to the desired location in the stream, read or write the desired number of bytes, and close the stream.

Table 87 shows the generic stream I/O functions of the DataBlade API. You can use these generic stream I/O functions on any stream (as long as the stream class implements them).

Table 87. Generic Stream I/O Functions
Stream-I/O Function Description
mi_stream_close( ) Close the stream.
mi_stream_eof( ) Check the stream for the end-of-stream condition.
mi_stream_get_error( ) Obtain the last error that occurred on the specified stream.
mi_stream_getpos( ) Obtain the current stream seek position, returning it in a function parameter.
mi_stream_length( ) Obtain the length of the stream data.
mi_stream_read( ) Read a specified number of bytes from the stream.
mi_stream_seek( ) Move the stream seek position to the desired location.
mi_stream_set_error( ) Sets the last error status on the specified stream.
mi_stream_setpos( ) Set the stream seek position.
mi_stream_tell( ) Obtain the current stream seek position, returning it from the function.
mi_stream_write( ) Write a specified number of bytes to the stream.

The advantage of accessing data through a stream is that the call to the generic stream I/O function is the same, regardless of the format of the underlying data. With these generic stream-I/O functions, the DataBlade API provides a common interface for the transportation and access of data independent of the data type or destination.

For example, the following call to mi_stream_read( ) reads 164 bytes of data from a stream into a user-defined buffer named buf:

nbytes_read = mi_stream_read(strm_desc, buf, 164);

The calling code does not need to concern itself about the format of the underlying data. Whether mi_stream_read( ) reads the data from a file, character array, varying-length structure, or user-defined stream depends on which stream-open function has obtained the pointer to the stream descriptor (MI_STREAM structure).

In addition to the generic stream I/O functions in Table 87, the stream I/O interface contains the following functions for different stream classes.

Classes of Stream I/O Function Stream I/O Function More Information
Stream-open functions for the
predefined stream classes:
Using Predefined Stream Classes
  • File stream
  • String stream
  • Varying-length-data stream
  • mi_stream_open_fio( )
  • mi_stream_open_mi_lvarchar( )
  • mi_stream_open_str( )
Abstract stream I/O functions for user-defined streams
  • mi_stream_init( )

Type-specific stream-open function

Creating a User-Defined Stream Class

Using Predefined Stream Classes

The DataBlade API provides several predefined stream classes that you can access with the stream I/O interface.

To use a predefined stream class in your UDR
  1. Open a stream with the appropriate type-specific stream-open function.

    The following table shows the predefined stream classes that the DataBlade API provides and their associated stream-open functions.

    Predefined Stream Class Stream-Open Function
    File stream mi_stream_open_fio( )
    String stream mi_stream_open_str( )
    Varying-length-data stream mi_stream_open_mi_lvarchar( )

    The mistrmtype.h header file declares these predefined stream-open functions.

  2. Access the open stream with the appropriate stream I/O function.

    Table 87 lists the stream I/O functions that the DataBlade API provides.

For example, the following code fragment reads 26 bytes of data from a string stream into a user-defined buffer named buf:

#define STRING_SIZE = 80

MI_STREAM *strm_desc;
mi_integer nbytes;
char buf[200];
char string_txt[STRING_SIZE] = 
   "A stream is a generic term for some object that can be\
 written to or read from."

strm_desc = mi_stream_open_str(NULL, string_txt, STRING_SIZE);
   if ( (nbytes = mi_stream_read(strm_desc, buf, 26)) != 26 )
      /* error in read */
mi_stream_close(strm_desc);

After this code fragment completes, the buf user-defined buffer contains the following character string:

A stream is a generic term

The following sections provide additional details on each of the predefined DataBlade API stream classes.

The File Stream

The file stream provides access to an operating-system file through the stream I/O interface. To support a data stream on an operating-system file, the DataBlade API provides the stream I/O functions in Table 88.

Table 88. Stream I/O Functions for a File Stream
Stream I/O Task Stream I/O Function
Initialize and open a file stream. mi_stream_open_fio( )
Move the file seek position to the desired location. mi_stream_seek( )
Read a specified number of bytes from the file stream. mi_stream_read( )
Write a specified number of bytes to the file stream. mi_stream_write( )
Obtain the current file seek position, returning it from the function. mi_stream_tell( )
Obtain the current file seek position, returning it in a function parameter. mi_stream_getpos( )
Set the file seek position. mi_stream_setpos( )
Obtain the length of the operating-system file. mi_stream_length( )
Close the file stream. mi_stream_close( )

Tip:
You can also use the mi_stream_get_error( ) and mi_stream_eof( ) functions on a file stream.

As Table 88 shows, the stream I/O interface for a file stream consists of a type-specific stream-open function, mi_stream_open_fio( ), plus the generic stream I/O functions. The mi_stream_open_fio( ) function opens the file and returns a new file stream.

The other stream I/O functions in Table 88 handle return status differently from DataBlade API file-access functions because the stream I/O functions do not allow you to obtain the errno status value directly. Instead, these functions handle their return status as follows:

The String Stream

The string stream provides access to a character array through the stream I/O interface. The string stream does not handle character data as null-terminated strings. It does not evaluate the contents of the data stream in any way. To support a data stream on a character array, the DataBlade API provides the stream I/O functions in Table 89.

Table 89. Stream I/O Functions for a String Stream
Stream I/O Task Stream I/O Function
Initialize and open a string stream. mi_stream_open_str( )
Move the string seek position to the desired location. mi_stream_seek( )
Read a specified number of bytes from the string stream. mi_stream_read( )
Write a specified number of bytes to the string stream. mi_stream_write( )
Obtain the current string seek position, returning it from the function. mi_stream_tell( )
Obtain the current string seek position, returning it in a function parameter. mi_stream_getpos( )
Set the string seek position. mi_stream_setpos( )
Obtain the length of the character array.

This is the str_len value to pass to mi_stream_open_str( ) when you create the string stream.

mi_stream_length( )
Close the string stream. mi_stream_close( )

Tip:
You can also use the mi_stream_get_error( ) and mi_stream_eof( ) functions on a string stream.

As Table 89 shows, the stream I/O interface for a string stream consists of the generic stream I/O functions plus a type-specific stream-open function, mi_stream_open_str( ).

The Varying-Length-Data Stream

The varying-length-data stream provides access to the data within a varying-length structure (mi_lvarchar) through the stream I/O interface. A varying-length-data stream does not handle varying-length data as null-terminated strings. It also does not evaluate the contents of the data stream in any way. To support a data stream on a varying-length structure, the DataBlade API provides the stream I/O functions in Table 90.

Table 90. Stream I/O Functions for a Varying-Length-Data Stream
Stream I/O Task Stream I/O Function
Initialize and open a varying-length-data stream. mi_stream_open_mi_lvarchar( )
Move the stream seek position to the desired location. mi_stream_seek( )
Read a specified number of bytes from the varying-length-data stream. mi_stream_read( )
Write a specified number of bytes to the varying-length-data stream. mi_stream_write( )
Obtain the current stream seek position, returning it from the function. mi_stream_tell( )
Obtain the current stream seek position, returning it in a function parameter. mi_stream_getpos( )
Set the stream seek position. mi_stream_setpos( )
Obtain the length of the varying-length data. mi_stream_length( )
Close the varying-length-data stream. mi_stream_close( )

Tip:
You can also use the mi_stream_get_error( ) and mi_stream_eof( ) functions on a varying-length-data stream.

As Table 90 shows, the stream I/O interface for a varying-length-data stream consists of the generic stream I/O functions plus a type-specific stream-open function, mi_stream_open_mi_lvarchar( ). This function returns a new varying-length-data stream.

Creating a User-Defined Stream Class

You can provide a stream I/O interface to create your own protocol for reciprocal reading and writing of SQL data and other data streams. The DataBlade API stream I/O interface provides a consistent interface for accessing data; that is, each stream I/O function has a fixed function name and argument list, regardless of the actual kind of stream that it accesses. This fixed syntax provides the main benefits of stream access:

Important:
Enterprise Replication does not support user-defined stream classes.

To create a user-defined stream class, you need to write the following stream I/O functions:

The mi_stream_init( ) function initializes the stream descriptor with the arguments it receives. The following code fragment of a stream-open function calls mi_stream_init( ) with the stream-operations structure in Figure 81, the internal structure for the mytype opaque type, and a NULL-valued pointer:

MI_STREAM *mi_stream_open_mytype(void *mydata)
{
   MI_STREAM *strm_desc; /* could be passed in as input to open( )
                        * also.
                        */
/* Code to process any stream-open arguments */
...
/* Call to mi_stream_init( ) to allocate and initialize
 * the stream descriptor 
 */
   strm_desc = mi_stream_init(stream_ops_mytype, mydata, NULL);

/* Return pointer to newly allocated stream descriptor */
   return strm_desc;
}

Because mi_stream_init( ) receives a NULL-valued pointer as its stream descriptor, it allocates the stream descriptor in the current memory duration. The mi_stream_init( ) function then returns a pointer to this newly allocated structure, which the mi_stream_open_mytype( ) function also returns.

The Stream-Open Function

Your stream-open function must take the following steps:

  1. Accept as its arguments the type-specific initialization information and use them to open the data.
  2. Call mi_stream_init( ) with appropriate information to initialize an MI_STREAM structure (see Table 7).

The stream-open function must prepare the arguments for the call to the mi_stream_init( ) function, which initializes and optionally allocates an MI_STREAM structure. The mi_stream_init( ) function takes the following arguments:

The Stream-Operations Structure

The stream-operations structure contains pointers to the C functions that implement the generic stream I/O functions for the particular stream. A valid stream-operations structure must exist for the DataBlade API to locate at runtime your type-specific implementations of these generic stream I/O functions. Therefore, it must be initialized before the call to mi_stream_init( ).

Figure 80 shows the declaration of the stream-operations structure, mi_st_ops. For the most current definition, see the mistream.h header file.

Figure 80. The Stream-Operations Structure
#define OPS_NAME_LENGTH 40

struct mi_stream_operations {
   /* the pointers to the functions */
   mi_integer (*close)(MI_STREAM *strm_desc);
   mi_integer (*read)(MI_STREAM *strm_desc, void *buf, 
      mi_integer nbytes);
   mi_integer (*write)(MI_STREAM *strm_desc, void *buf,
      mi_integer nbytes);
   mi_integer (*seek)(MI_STREAM *strm_desc, 
      mi_int8 *offset, mi_integer whence);
   mi_int8 *  (*tell)(MI_STREAM *strm_desc);
   mi_integer (*setpos)(MI_STREAM *strm_desc, 
      mi_int8 *pos);
   mi_integer (*getpos)(MI_STREAM *strm_desc, 
      mi_int8 *pos);
   mi_integer (*length)(MI_STREAM *strm_desc, 
      mi_int8 *length);
   /* names of the functions above */
   char close_name [OPS_NAME_LENGTH];
   char read_name  [OPS_NAME_LENGTH];
   char write_name [OPS_NAME_LENGTH];
   char seek_name  [OPS_NAME_LENGTH];
   char tell_name  [OPS_NAME_LENGTH];
   char setpos_name[OPS_NAME_LENGTH];
   char getpos_name[OPS_NAME_LENGTH];
   char length_name[OPS_NAME_LENGTH];
   /* the function handles for the functions above */
   void *close_fhandle;
   void *read_fhandle;
   void *write_fhandle;
   void *seek_fhandle;
   void *tell_fhandle;
   void *setpos_fhandle;
   void *getpos_fhandle;
   void *length_fhandle;
} mi_st_ops;

As Figure 80 shows, the stream-operations structure consists of the following parts:

You should initialize the pointers and names and set the handles to NULL.

Figure 81 shows a sample stream-operations structure that provides function pointers for the type-specific implementations of the mi_stream_close( ), mi_stream_read( ), and mi_stream_write( ) functions for a stream on a user-defined type named newstream.

Figure 81. A Sample Stream-Operations Structure
static struct mi_st_ops stream_ops_newstream =
{
   stream_close_newstream,
   stream_read_newstream,
   stream_write_newstream,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   "stream_close_newstream",
   "stream_read_newstream",
   "stream_write_newstream",
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL   
};

The code fragment in Figure 81 statically initializes the stream-operations structure. If you initialize this structure dynamically, do so in the stream-open function.

The Stream Data

The second argument to mi_stream_init( ) is an uninterpreted data pointer that is stored in the MI_STREAM structure initialized by the call to mi_stream_init( ). The stream interface does not interpret this pointer, which is for the benefit of the stream implementer. You can retrieve the value of this pointer through a call to mi_stream_get_dataptr( ).

The Stream Descriptor

A stream descriptor holds information about the stream that all stream I/O functions need to access. The mi_stream_init( ) function accepts as its stream-descriptor argument either of the following values:

When you pass the mi_stream_init( ) function a NULL-valued pointer for its stream-descriptor argument, the function allocates a new stream descriptor in the current memory duration. If your application requires a specific memory duration for the stream descriptor, your stream-open function can perform one of the following tasks:

Initialization of the Stream Descriptor

After your type-specific stream-open function has prepared the arguments for the mi_stream_init( ) function, it must call mi_stream_init( ) to initialize the stream descriptor.

Tip:
Whether the mi_stream_init( ) function actually allocates the stream descriptor depends on the value of its third argument. For more information, see The Stream Descriptor.

The stream descriptor, MI_STREAM, holds information about the data stream such as the data and its seek position. For the most current definition of the MI_STREAM structure, see the mistream.h header file.

Support for Stream Access

To provide access to the data in your user-defined stream, you must implement the appropriate generic stream I/O functions. The following table shows which stream I/O functions to implement for the stream characteristics that your stream supports.

Stream Characteristic Description Stream I/O Function
Stream seek position The location within the data at which the next read or write operation begins mi_stream_seek( ), mi_stream_tell( ),
mi_stream_getpos( ), mi_stream_setpos( )
Stream length The size of the data This length can be the size of the data when the stream is initialized or the current size of the data. mi_stream_length( )
Stream mode Which operations are valid: read-only, read/write, or write-only mi_stream_read( ), mi_stream_write( )

Tip:
You do not have to implement the stream I/O functions mi_stream_get_error( ) and mi_stream_eof( ) for your user-defined stream. The implementation of these functions is generic for any stream.

Consider the following information when deciding which stream I/O functions to implement:

The following general rules apply to values that the generic stream I/O functions return:

Registering a UDR That Accesses a Stream

To declare a stream as an argument or return value of a C UDR, use the MI_STREAM data type. When you register this UDR in the database, use the opaque data type stream to represent the stream descriptor.

The database server represents a stream with the stream opaque type. As for other opaque types, the database server stores information on stream in the sysxtdtypes system catalog table.

For example, suppose you have a C declaration for a UDR named get_data( ):

mi_lvarchar *get_data(strm_desc, nbytes)
   MI_STREAM *strm_desc;
   mi_integer nbytes;

The following CREATE FUNCTION statement registers the get_data( ) UDR, using the stream data type as its first argument:

CREATE FUNCTION get_data(data_source stream, nbytes INTEGER)
RETURNS VARCHAR
EXTERNAL NAME '/usr/local/udrs/stream/stream.so(get_data)'
LANGUAGE C;

Releasing Stream Resources

When your DataBlade API module no longer needs a stream, you need to assess whether you can release resources that the stream is using. A stream descriptor that the mi_stream_init( ) function allocated has the current memory duration, so it remains valid until one of the following events occurs:

To conserve resources, use the mi_stream_close( ) function to deallocate the stream descriptor explicitly when your DataBlade API module no longer needs it. The mi_stream_close( ) function is the destructor function for a stream descriptor. This function frees a stream descriptor that mi_stream_init( ) allocated and any associated resources, including the stream-data buffer.

The mi_stream_close( ) function does not automatically free a stream descriptor allocated by your stream-open function. If the mi_stream_init( ) function does not allocate a stream descriptor, your type-specific implementation of mi_stream_close( ) must handle the deallocation.

Enterprise Edition Home | Express Edition Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]