Creating a user defined function for atan2 in Open Ingres
Some RDBMSs have a CREATE FUNCTION statement to allow the creation of user defined functions. Ingres does not have
this statement (at least not yet), but does allow the creation of user defined functions using C. Its a bit more work
but not that hard really. I needed an atan2 function for some route planning code, so I set about creating a UDF in Ingres.
I started by creating an empty .c file and copying in one of the functions from $II_SYSTEM/ingres/demo/udadts/op.c, I used
midpoint.c as it also took 2 parameters. I changed the name to arctan2:
#ifdef __STDC__
int arctan2(II_SCB *scb ,
II_DATA_VALUE *dv_1 ,
II_DATA_VALUE *dv_2 ,
II_DATA_VALUE *dv_result )
#else
int arctan2(scb, dv_1, dv_2, dv_result )
II_SCB *scb;
II_DATA_VALUE *dv_1;
II_DATA_VALUE *dv_2;
II_DATA_VALUE *dv_result;
#endif
Then I declared the local variables I would need to perform the calculation:
double dx;
double dy;
double res;
Then modify the error checking to check all parameters are floats:
if ( (dv_1->db_datatype == II_FLOAT)
&& (dv_2->db_datatype == II_FLOAT)
&& (dv_result->db_datatype == II_FLOAT)
&& (dv_result->db_length == 8)
&& (dv_result->db_data)
)
{
Then copy the parameters from the function to the local variables where they will be strongly typed. I copied the
macros from op.c for this:
F8ASSIGN_MACRO(*dv_1->db_data, dy);
F8ASSIGN_MACRO(*dv_2->db_data, dx);
The actual calculation is trivial as its already in libm:
res = atan2(dy, dx);
Then change the line which copies the result to the dv_result return structure:
F8ASSIGN_MACRO(res, *dv_result->db_data);
Finally return and leave the error checking as is:
if (result)
us_error(scb, 0x200023, "atan2: Invalid input");
return(result);
I copied into in my c file the code for us_error and byte_copy. Next you'll need a few structures and macros
to declare your function and its definition:
#define FI_ATAN2 II_FISTART
static II_DT_ID atan2_parms[] = { II_FLOAT, II_FLOAT};
#define min(a,b) ((a) <= (b) ? (a) : (b))
#define F8ASSIGN_MACRO(a,b) ((*(double *)&(b)) = (*(double *)&(a)))
#define OP_ATAN2 II_OPSTART
static IIADD_FO_DFN operators[] =
{
{ II_O_OPERATION, { "atan2"},
OP_ATAN2, II_NORMAL }
};
static IIADD_FI_DFN function_instances[] =
{
{ II_O_FUNCTION_INSTANCE , FI_ATAN2, II_NO_FI,
OP_ATAN2, II_NORMAL, II_FID_F0_NOFLAGS, 0,
2, atan2_parms, II_FLOAT, II_RES_FIXED, 8, II_PSVALID,
arctan2, 0 }
};
static IIADD_DEFINITION register_block =
{
(IIADD_DEFINITION *) 0,
(IIADD_DEFINITION *) 0,
sizeof(IIADD_DEFINITION),
IIADD_DFN2_TYPE,
0,
0,
0,
0,
IIADD_INCONSISTENT, /* Ignore the current datatypes known by the recovery
system */
2, /* Major id */
0, /* minor id */
50,
"UDT atan2",
IIADD_T_FAIL_MASK | IIADD_T_LOG_MASK,
0,
0,
0,
(sizeof(operators)/sizeof(IIADD_FO_DFN)),
operators,
(sizeof(function_instances)/sizeof(IIADD_FI_DFN)),
function_instances
};
Finally, the missing link (no Paul, that's not you) is to provide the hook so the dbms can register your
function;
II_STATUS
#ifdef __STDC__
IIudadt_register(IIADD_DEFINITION **ui_block_ptr ,
II_CALLBACKS *callback_block )
#else
IIudadt_register(ui_block_ptr, callback_block)
IIADD_DEFINITION **ui_block_ptr;
II_CALLBACKS *callback_block;
#endif
{
register_block.add_count = register_block.add_dt_cnt +
register_block.add_fo_cnt +
register_block.add_fi_cnt;
*ui_block_ptr = ®ister_block;
return(II_OK);
}
Now you are ready to build it. A Makefile section like this should do:
atan2.o : atan2.c
${CC} atan2.c -c -fPIC
And finally stop ingres, run iilink and when prompted enter the full path name of the atan2.o, and restart Ingres.
If all went will then you should be able to run
select atan2(1,0);
select atan2(0,1);
select atan2(-1,0);
select atan2(0,-1);
and get the radians for these vectors. Y is the first parameter, so atan2(1, 0) is an upwards vertical vector, so has
an atan2 value of pi/2 or about 1.571.
The full atan2.c file.
This is just a small part of what can be done using the Object Management Extension in Ingres (OME). You can also
create user defined types and operators. See the demos/udadts directory for more stuff.