OME README

From Ingres Community Wiki

Jump to: navigation, search

Contents

Introduction

The Object Management Extension (OME) allows the Ingres administrator to provide User Defined Types (UDTs) and User Defined Functions (UDFs) for inclusion in the Ingres DBMS server. This README will concern itself only with UDFs which act on the standard Ingres datatypes.

The first step in OME is to create the necessary 'framework' program in which to install the UDFs. This is by far the hardest step in OME!

To help, I have provided an empty framework which will easily extend to allow more functions to be installed. Simply cut and paste this program and graft into it one or two functions from the community wiki, such as:

A Makefile has also been provided.

Enjoy.

Written by Martin Bowes.

A Frame Work

#include "iiadd.h" /*The Abstract Data type stuff required for OME*/

#include <time.h>         /* Required for time function, as part of error
                          ** processing
                          */
/*
** Bits and Bobs for the callback block
*/
static  II_STATUS   (*Ingres_trace_function)() = 0;
#ifdef __STDC__
GLOBALDEF II_ERROR_WRITER *usc_error = 0;
GLOBALDEF II_INIT_FILTER  *usc_setup_workspace = 0;
GLOBALDEF II_LO_HANDLER   *usc_lo_handler = 0;
GLOBALDEF II_LO_FILTER    *usc_lo_filter = 0;
#else
GLOBALDEF (*usc_error)() = 0;
GLOBALDEF (*usc_setup_workspace)() = 0;
GLOBALDEF (*usc_lo_handler)() = 0;
GLOBALDEF (*usc_lo_filter)() = 0;
#endif

/* us_error(): This ensures that error messages are correctly formatted with a
**             date and time. It bypasses problems in the usc_error callback
**             function.
*/
void us_error(II_SCB *scb, long err_no, char *err_text)
{
    char msg[300];

    /* variables used to get current time
    ** need a buffer for thread-safe version of ctime, see man ctime
    */
    char errtime_str[26];
    char errtimebuf[26];
    time_t errtime_t;

    /* get current time and convert to nice string */
    time(&errtime_t);
    strncpy(errtime_str, ctime_r(&errtime_t, errtimebuf), 26);
    sprintf(msg, "%sE_OME001 %s", errtime_str, err_text);

    usc_error(scb, err_no, msg);
}; /* us_error */

/*
** User defined datatypes: empty in this case, but handy to have for the
** IIudadt_register routine.
*/
static IIADD_DT_DFN *Datatypes;

/* The Function Executor Code, External Lenspec routines and any supporting
** functions can go in here...
*/

/* Need arrays of 2-byte integers to hold the function argument datatypes
** Here are some examples...
*/
static II_DT_ID  UD_2_INT[]        = {II_INTEGER,  II_INTEGER};
static II_DT_ID  UD_FLOAT_INT[]    = {II_FLOAT,    II_INTEGER};
static II_DT_ID  UD_VC_N_VB[]      = {II_VARCHAR,  II_VBYTE};
static II_DT_ID  UD_VC_N_LVC[]     = {II_VARCHAR,  II_LVCH};
static II_DT_ID  UD_VC_N_LB[]      = {II_VARCHAR,  II_LBYTE};
static II_DT_ID  UD_2_VC[]         = {II_VARCHAR,  II_VARCHAR};
static II_DT_ID  UD_LVC_N_VC[]     = {II_LVCH,     II_VARCHAR};
static II_DT_ID  UD_2_VARBYTE[]    = {II_VBYTE,    II_VBYTE};


/* The fod_id enum set:
** Use enum to start counting the fod_id's from II_OPSTART
*/
enum fod_id {
   UDF_FIRST_FUNCTION = II_OPSTART,
   UDF_NEXT_FUNCTION
}; /* fod_id's */

static IIADD_FO_DFN Function_Definitions[] ={
    /* II_AGGREGATE functions:
    **     These are only available from Ingres2006 rls2
    */
    /* II_NORMAL functions: */
    }; /*Function_Definitions, sort by fod_id */

/*
** For each function defined we need a set of Function Instance Definitions.
**
** The fid_id enum set:
** Use enum to start counting the fid_id's from II_FISTART
*/
enum fid_id {
   UDF_FI_FIRST_FID = II_FISTART,
   UDF_FI_NEXT_FID
}; /* fid_id's */
 
static IIADD_FI_DFN Function_Instances[] = {
    /* The Aggregate Functions, go here. */
    /* The Normal Functions, go here.  */
}; /*Function_Instances, sorted by fid_opid */

/* And now the NASTY BIT...
** If provided, IIADD_DEFINITION exists as a compiler-generated data area.
** Do not dynamically allocate this data area or place it on the stack.
*/
static  IIADD_DEFINITION    register_block =
{
    (IIADD_DEFINITION *) 0,    /* add_next */
    (IIADD_DEFINITION *) 0,    /* add_prev */
    sizeof(IIADD_DEFINITION),  /* add_length */
    IIADD_DFN2_TYPE,           /* add_type */
    0,                         /* add_s_reserved */
    0,                         /* add_1_reserved*/
    0,                         /* add_owner */
    0,                         /* add_ascii_id */
    IIADD_CONSISTENT,          /* add_risk_consistency */
    2,       /* Major id */    /* add_major_id  */
    0,       /* minor id */    /* add_minor_id */
    50,                        /* add_l_user_string */
    "UDT demo",                /* add_user_string */
    IIADD_T_FAIL_MASK | IIADD_T_LOG_MASK,    /* add_trace */
    0,                /* add_count
                      ** This field will be filled in by the iiudadt_register.
                      ** This is not a requirement of the function,
                      ** just a convenient place to do it.
                      */
    0,                                                   /* add_dt_cnt */
    (IIADD_DT_DFN *) 0,                                  /* add_dt_dfn */

    (sizeof(Function_Definitions)/sizeof(IIADD_FO_DFN)), /* add_fo_cnt */
    Function_Definitions,                                /* add_fo_dfn */

    (sizeof(Function_Instances)/sizeof(IIADD_FI_DFN)),   /* add_fi_cnt */
    Function_Instances                                   /* add_fi_dfn */
}; /*register_block*/

/*
** Name: IIUDADT_REGISTER    - Add the datatype to the server
**
** Description:
** This routine is called by the DBMS server to add obtain information to
** add the datatype to the server.  It simply fills in the provided
** structure, sets the provided pointer to point to a pointer to this structure
** and returns.
**
*/
II_STATUS
IIudadt_register(
    IIADD_DEFINITION  **ui_block_ptr,
    II_CALLBACKS      *callback_block
    )
{
    /* This uses the global variable register_block, which is of type
    ** II_ADD_DEFINITION.
    */
    register_block.add_count = register_block.add_dt_cnt +
        register_block.add_fo_cnt +
        register_block.add_fi_cnt;
    *ui_block_ptr = &register_block;  /* Point to the first block in the list */

    /*
    ** Note that after this call, the call back block address will
    ** no longer be valid (the function addresses will, but the block
    ** address itself will not).  Therefore, it is necessary to
    ** copy the values in which we are interested before returning.
    */
    if (callback_block && callback_block->ii_cb_version >= II_CB_V1) {
        Ingres_trace_function = callback_block->ii_cb_trace;
        if (callback_block->ii_cb_version >= II_CB_V2) {
            usc_lo_handler      = callback_block->ii_lo_handler_fcn;
            usc_lo_filter       = callback_block->ii_filter_fcn;
            usc_setup_workspace = callback_block->ii_init_filter_fcn;
            usc_error           = callback_block->ii_error_fcn;
            }
        }
    else {
        Ingres_trace_function = 0;
        };
    return(II_OK);
} /*IIudadt_register*/

A Makefile

The following Makefile will make a shared object library named libOME.so from a program file named OME_functions.c.

# Make OME Stuff: libOME.so
CCFLAGS=-c -fPIC -O0 -o$@ -pthread
LDFLAGS=-shared
CC=cc
INC=-I${II_SYSTEM}/ingres/files
# In all Ingres...
LIBPATH=-L${II_SYSTEM}/ingres/lib
LIBS=-lcrypto -lssl #Extra libs for some special functions
#
all: OME_functions.o
   $(CC) $(LDFLAGS) -olibOME.so OME_functions.o

OME_functions.o:OME_functions.c
   $(CC) OME_functions.c $(CCFLAGS) $(LIBPATH) $(INC)

iilink options

To merge the shared object library (libOME.so) with the current iimerge you need to shutdown Ingres and execute iilink. Note that iilink is an interactive program and that it will ask you to provide the answers to a few questions. Over different versions of ingres the questions have changed, but essentially you will be asked to provide:

  • The full path to the shared object library containing your OME functions.
  • Optionally, provide a file extension for the new server. It is highly recommended to do this. I would suggest an extension like 'OME'.

An iilink response file

A quasi-response file can be made to execute with iilink. For example the file 'iilink.rsp' may contain the following responses to the questions asked by the iilink in Ingres 9.0.4.

/user/ingres/scripts/C/OME/libOME.so
OME

This can be execute with iilink by using: cat iilink.rsp | iilink

Installing the new server

I suggest the following tactic be used to install the new server created with iilink.

  • cd $II_SYSTEM/ingres/bin
  • mv iimerge iimerge.patch# eg. mv iimerge iimerge.12743
  • ln -s ./iimerge.OME iimerge

The advantage of this symlink is that subsequent attempts to install a patch to Ingres will fail when the symlink is noticed. This will (hopefully) jog the memory of whoever is installing the patch and remind them to rerun iilink after the patch is installed.

The patch can be installed by removing the symlink and copying the saved iimerge.patch# over iimerge.

How to tell if OME functions are installed on your system

Other than a symlink pointing to iimerge.OME...

The $II_SYSTEM/ingres/files/errlog.log will have details written into it on system startup to indicate the number of function definitions and instances that have been loaded. For example:

E_SC024D_SCA_ADDING User Defined Datatype Initialization:
       Adding 34. new datatype entries,
              0. new datatypes,
              12. new operators,
         and  22. new function instances.

External Lenspec Routines

Error Processing

The only thing an external lenspec routine is suppossed to do is set the db_length field of the result data vector. However on occassion it is not possible to do so, in which case the lenspec routine may raise an error.

BUT...

It may only raise error 0x22022 (E_AD2005). If it attempts to raise any other error code then the session may be crashed out of the server. This is Bug 120198.

Furthermore, the message raised with the error is ignored. That is bug 120197. A work-around is to simply print the desired message using the Ingres_trace_function and then generate the correct error.

As an example...

if (...some error condition...)
{
     /* This will print the warning for the user */
     sprintf(msg,"A warning message\n");
     Ingres_trace_function(II_TRACE_FE_MASK, strlen(msg), msg);

     /* And this will generate the required error */
     us_error(scb, 0x22022, msg); /* E_AD2005 (msg is ignored: Bug 120197) */
     return(II_ERROR);
};
Personal tools
Developing With