/* ** File name: fcur.c */ #include "postgres.h" #include "fmgr.h" typedef struct { char fcur_name[4]; /* Currency name */ float4 fcur_units; /* Units of currency */ float4 fcur_xrate; /* Exchange rate */ } fcur; static char * baseCurrencyName = "US$"; static char * unknownCurrencyName = "???"; /* ** Name: fcur_in() ** ** Converts an fcur value from external form ** to internal form. */ PG_FUNCTION_INFO_V1(fcur_in); Datum fcur_in(PG_FUNCTION_ARGS) { char * src = PG_GETARG_CSTRING(0); char * workStr = (char *)palloc( strlen( src )); char * units = NULL; char * name = NULL; char * xrate = NULL; fcur * result = NULL; char * endPtr = NULL; strcpy( workStr, src ); units = strtok( workStr, "(" ); xrate = strtok( NULL, "/)" ); name = strtok( NULL, ")" ); result = (fcur *)palloc( sizeof( fcur )); memset( result, 0x00, sizeof( fcur )); result->fcur_units = strtod( units, &endPtr ); if( xrate ) { result->fcur_xrate = strtod( xrate, &endPtr ); } else { result->fcur_xrate = 1.0; } if( name ) { strncpy( result->fcur_name, name, sizeof( result->fcur_name )); } else { strncpy( result->fcur_name, unknownCurrencyName, sizeof( result->fcur_name )); } PG_RETURN_POINTER( result ); } /* ** Name: fcur_out() ** ** Converts an fcur value from internal form ** to external form. */ PG_FUNCTION_INFO_V1(fcur_out); Datum fcur_out(PG_FUNCTION_ARGS) { fcur * src = (fcur *)PG_GETARG_POINTER( 0 ); char * result; char work[16+1+sizeof(src->fcur_name)+16]; sprintf( work, "%g(%g/%s)", src->fcur_units, src->fcur_xrate, src->fcur_name ); result = (char *)palloc( strlen( work ) + 1 ); strcpy( result, work ); PG_RETURN_CSTRING( result ); } /* ** Name: normalize() ** ** Converts an fcur value into a normalized ** double by applying the exchange rate. */ static double normalize( fcur * src ) { return( src->fcur_units / src->fcur_xrate ); } /* ** Name: fcur_eq() ** ** Returns true if the two fcur values ** are equal (after normalization), otherwise ** returns false. */ PG_FUNCTION_INFO_V1(fcur_eq); Datum fcur_eq(PG_FUNCTION_ARGS) { fcur * left = (fcur *)PG_GETARG_POINTER(0); fcur * right = (fcur *)PG_GETARG_POINTER(1); PG_RETURN_BOOL( normalize( left ) == normalize( right )); } /* ** Name: fcur_ne() ** ** Returns true if the two fcur values ** are not equal (after normalization), ** otherwise returns false. */ PG_FUNCTION_INFO_V1(fcur_ne); Datum fcur_ne(PG_FUNCTION_ARGS) { fcur * left = (fcur *)PG_GETARG_POINTER(0); fcur * right = (fcur *)PG_GETARG_POINTER(1); PG_RETURN_BOOL( normalize( left ) != normalize( right )); } /* ** Name: fcur_lt() ** ** Returns true if the left operand ** is less than the right operand. */ PG_FUNCTION_INFO_V1(fcur_lt); Datum fcur_lt(PG_FUNCTION_ARGS) { fcur * left = (fcur *)PG_GETARG_POINTER(0); fcur * right = (fcur *)PG_GETARG_POINTER(1); PG_RETURN_BOOL( normalize( left ) < normalize( right )); } /* ** Name: fcur_le() ** ** Returns true if the left operand ** is less than or equal to the right ** operand. */ PG_FUNCTION_INFO_V1(fcur_le); Datum fcur_le(PG_FUNCTION_ARGS) { fcur * left = (fcur *)PG_GETARG_POINTER(0); fcur * right = (fcur *)PG_GETARG_POINTER(1); PG_RETURN_BOOL( normalize( left ) <= normalize( right )); } /* ** Name: fcur_gt() ** ** Returns true if the left operand ** is greater than the right operand. */ PG_FUNCTION_INFO_V1(fcur_gt); Datum fcur_gt(PG_FUNCTION_ARGS) { fcur * left = (fcur *)PG_GETARG_POINTER(0); fcur * right = (fcur *)PG_GETARG_POINTER(1); PG_RETURN_BOOL( normalize( left ) > normalize( right )); } /* ** Name: fcur_ge() ** ** Returns true if the left operand ** is greater than or equal to the right operand. */ PG_FUNCTION_INFO_V1(fcur_ge); Datum fcur_ge(PG_FUNCTION_ARGS) { fcur * left = (fcur *)PG_GETARG_POINTER(0); fcur * right = (fcur *)PG_GETARG_POINTER(1); PG_RETURN_BOOL( normalize( left ) >= normalize( right )); } /* ** Name: fcur_to_float4() ** ** Converts the given fcur value into a ** normalized float4. */ PG_FUNCTION_INFO_V1(fcur_to_float4); Datum fcur_to_float4(PG_FUNCTION_ARGS) { fcur * src = (fcur *)PG_GETARG_POINTER(0); PG_RETURN_FLOAT4( normalize( src )); } /* ** Name: float4_to_fcur() ** ** Converts the given float4 value into an ** fcur value */ PG_FUNCTION_INFO_V1(float4_to_fcur); Datum float4_to_fcur(PG_FUNCTION_ARGS) { float4 src = PG_GETARG_FLOAT4(0); fcur * result = (fcur *)palloc( sizeof( fcur )); result->fcur_units = src; result->fcur_xrate = 1.0; strcpy( result->fcur_name, baseCurrencyName ); PG_RETURN_POINTER( result ); } /* ** Name: fcur_add() ** ** Adds two fcur values, returning the result ** If the operands are expressed in the same ** currency (and exchange rate), the result ** will be expressed in the common currency, ** otherwise, the result will be in normalized ** form. */ PG_FUNCTION_INFO_V1(fcur_add); Datum fcur_add(PG_FUNCTION_ARGS) { fcur * left = (fcur *)PG_GETARG_POINTER(0); fcur * right = (fcur *)PG_GETARG_POINTER(1); fcur * result; result = (fcur *)palloc( sizeof( fcur )); if( left->fcur_xrate == right->fcur_xrate ) { if( strcmp( left->fcur_name, right->fcur_name )) { /* ** The two operands have a common currency - preserve ** that currency by constructing a new fcur with the ** same currency type. */ result->fcur_xrate = left->fcur_xrate; result->fcur_units = left->fcur_units + right->fcur_units; strcpy( result->fcur_name, left->fcur_name ); PG_RETURN_POINTER( result ); } } result->fcur_xrate = 1.0; result->fcur_units = normalize( left ) + normalize( right ); strcpy( result->fcur_name, baseCurrencyName ); PG_RETURN_POINTER( result );; }