/****************************************************************************
**
** Source File Name = tblsrv.c
**
** Licensed Materials - Property of IBM
**
** (C) COPYRIGHT International Business Machines Corp. 1999
** All Rights Reserved.
**
** US Government Users Restricted Rights - Use, duplication or
** disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**
** PURPOSE :
**
** This Table Function reads an input file which contains
** the Weather data and returns the weather information.
**
** Input File contains City Name followed by Weather info. This
** pattern is repeated for other cities.
** This would be an associated DDL for the following Table Function.
** Note that it does not take any input and returns 7 output columns.
**
** create function tfweather_u()
**    returns table ( CITY VARCHAR(30),
**                    TEMP_IN_F INTEGER,
**                    HUMIDITY INTEGER,
**                    WIND VARCHAR(5),
**                    WIND_VELOCITY INTEGER,
**                    BAROMETER FLOAT,
**                    FORECAST VARCHAR(25) )
**    specific tfweather_u
**    disallow parallel
**    not fenced not variant no sql
**    no external action scratchpad final call
**    language c parameter style sql
**    EXTERNAL NAME 'tblsrv!weather';
**
** To make the library:
**   Drop any instances of the function that previously existed.
**   Compile without linking.
**   Link with the DB2 libraries.
**   Copy the compiled library to the "function" directory of
**   the DB2INSTANCE that the library will used with.
**
**   There is an example program called "tblcli.sqc" which creates and runs
**   the user-defined function tfweather_u from the "tblsrv" library.
**
**    EXTERNAL DEPENDENCIES :
**       - Ensure existence of database for precompile purposes.
**       - Compile and link with the IBM Cset++ compiler (AIX and OS/2)
**         or the Microsoft Visual C++ compiler (Windows) 
**         or the compiler supported on your platform.
**
** For more information about these samples see the README file.
**
** For more information on programming in C, see the:
**   -  "Programming in C and C++" section of the Application Development Guide
** For more information on Building C Applications, see the:
**   -  "Building C Applications" section of the Application Building Guide.
**
** For more information on the SQL language see the SQL Reference.
**
****************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sql.h>
#include <sqludf.h> /* for use in compiling User Defined Function */

#define   SQL_NOTNULL   0   /* Nulls Allowed - Value is not Null */
#define   SQL_ISNULL   -1   /* Nulls Allowed - Value is Null */

/* Short and long city name structure */
typedef struct {
  char * city_short ;
  char * city_long ;
} city_area ;

/* Scratchpad data */
/* Preserve information from one function call to the next call */
typedef struct {
  /* FILE * file_ptr; if you use weather data text file */
  int file_pos ;  /* if you use a weather data buffer */
} scratch_area ;

/* Field descriptor structure */
typedef struct {
  char fld_field[31] ;                     /* Field data */
  int  fld_ind ;            /* Field null indicator data */
  int  fld_type ;                          /* Field type */
  int  fld_length ;  /* Field length in the weather data */
  int  fld_offset ;  /* Field offset in the weather data */
} fld_desc ;
 
/* Short and long city name data */
city_area cities[] = {
  { "alb", "Albany, NY"                  },
  { "atl", "Atlanta, GA"                 },
  { "aus", "Austin, TX"                  },
  { "bgm", "Binghamton, NY"              },
  { "btv", "Burlington, VT"              },
  { "chi", "Chicago, IL"                 },
  { "clt", "Charlotte, NC"               },
  { "den", "Denver, CO"                  },
  { "ftw", "Dallas - Fort Worth, TX"     },
  { "hou", "Houston, TX"                 },
  { "lax", "Los Angeles, CA"             },
  { "lex", "Lexington, KY"               },
  { "mci", "Kansas City, MO"             },
  { "mem", "Memphis, TN"                 },
  { "mia", "Miami - Fort Lauderdale, FL" },
  { "mke", "Milwaukee, WI"               },
  { "msp", "Twin Cities, MN"             },
  { "nyc", "New York City, NY"           },
  { "pbi", "Palm Beach, FL"              },
  { "pit", "Pittsburgh, PA"              },
  { "rdu", "Raleigh - Durham, NC"        },
  { "ric", "Richmond, VA"                },
  { "rst", "Rochester, MN"               },
  { "sea", "Seattle, WA"                 },
  { "sfo", "San Francisco, CA"           },
  { "sjc", "San Jose, CA"                },
  { "slc", "Salt Lake City, UT"          },
  { "stl", "St. Louis, MO"               },
  { "tus", "Tuscon, AZ"                  },
  { "wbc", "Washington DC, DC"           },
  /* You may want to add more cities here */

  /* Do not forget a null termination */
  { ( char * ) 0, ( char * ) 0           }
} ;

/* Field descriptor data */
fld_desc fields[] = {
  { "", SQL_ISNULL, SQL_TYP_VARCHAR, 30,  0 }, /* city          */
  { "", SQL_ISNULL, SQL_TYP_INTEGER,  3,  2 }, /* temp_in_f     */
  { "", SQL_ISNULL, SQL_TYP_INTEGER,  3,  7 }, /* humidity      */
  { "", SQL_ISNULL, SQL_TYP_VARCHAR,  5, 13 }, /* wind          */
  { "", SQL_ISNULL, SQL_TYP_INTEGER,  3, 19 }, /* wind_velocity */
  { "", SQL_ISNULL, SQL_TYP_FLOAT,    5, 24 }, /* barometer     */
  { "", SQL_ISNULL, SQL_TYP_VARCHAR, 25, 30 }, /* forecast      */
  /* You may want to add more fields here */

  /* Do not forget a null termination */
  { ( char ) 0, 0, 0, 0, 0 }
} ;

/* Following is the weather data buffer for this example. You */
/* may want to keep the weather data in a separate text file. */
/* Uncomment the following fopen() statement.  Note that you  */
/* have to specify the full path name for this file.          */
char * weather_data[] = {
   "alb.forecast",
   "   34   28%    wnw   3  30.53 clear",
   "atl.forecast",
   "   46   89%   east  11  30.03 fog",
   "aus.forecast",
   "   59   57%  south   5  30.05 clear",
   "bgm.forecast",
   "   36   32%  south  10  30.34 overcast",
   "btv.forecast",
   "   38   30%  south   5  30.36 clear",
   "chi.forecast",
   "   41   55%    nne  12  30.15 partly cloudy",
   "clt.forecast",
   "   37  100%    nne  10  30.18 light rain",
   "den.forecast",
   "   58   24%    nne   8  29.67 clear",
   "ftw.forecast",
   "   49   73%    nne   4  30.05 overcast",
   "hou.forecast",
   "   65   46%   west  10  30.03 clear",
   "lax.forecast",
   "   60   86%   east   6  29.96 light drizzle",
   "lex.forecast",
   "   46   92%   east   8  29.99 fog",
   "mci.forecast",
   "   46   51%     ne   9  30.06 overcast",
   "mem.forecast",
   "   53   96%    wnw   8  29.97 moderate rain",
   "mia.forecast",
   "   82   64%     se  17  30.08 mostly cloudy",
   "mke.forecast",
   "   37   59%     ne  12  30.18 clear",
   "msp.forecast",
   "   41   39%     se   6  30.19 n/a",
   "nyc.forecast",
   "   37   56%    sse   6  30.44 overcast",
   "pbi.forecast",
   "   81   69%  south  17  30.09 partly cloudy",
   "pit.forecast",
   "   39   82%   east  11  30.21 light drizzle",
   "rdu.forecast",
   "   39   96%  north   8  30.10 light rain",
   "ric.forecast",
   "   39   75%    ene  12  30.20 light rain",
   "rst.forecast",
   "   40   50%     se  10  30.17 n/a",
   "sea.forecast",
   "   51   37%    ssw   8  29.93 clear",
   "sfo.forecast",
   "   53   54%   west  19  29.98 partly cloudy",
   "sjc.forecast",
   "   62   44%    wnw  17    n/a partly cloudy",
   "slc.forecast",
   "   63   26%  south  25  29.45 mostly cloudy",
   "stl.forecast",
   "   43   55%  north   9  30.06 overcast",
   "tus.forecast",
   "   73   18%  south   8  29.80 clear",
   "wbc.forecast",
   "   38   96%    ene  16  30.31 light rain",
   /* You may want to add more weather data here */

   /* Do not forget a null termination */
   ( char * ) 0
} ;

#ifdef __cplusplus
extern "C"
#endif
/* Find a full city name using a short name */
int get_name( char * short_name, char * long_name ) {

    int name_pos = 0 ;

    while ( cities[name_pos].city_short != ( char * ) 0 ) {
       if (strcmp(short_name, cities[name_pos].city_short) == 0) {
          strcpy( long_name, cities[name_pos].city_long ) ;
          /* A full city name found */
          return( 0 ) ;
       }
       name_pos++ ;
    }
    /* Could not find such city in the city data */
    strcpy( long_name, "Unknown City" ) ;
    return( -1 ) ;

}

#ifdef __cplusplus
extern "C"
#endif
/* Clean all field data and field null indicator data */
int clean_fields( int field_pos ) {

    while ( fields[field_pos].fld_length != 0 ) {
       memset( fields[field_pos].fld_field, '\0', 31 ) ;
       fields[field_pos].fld_ind = SQL_ISNULL ;
       field_pos++ ;
    }
    return( 0 ) ;

}

#ifdef __cplusplus
extern "C"
#endif
/* Fills all field data and field null indicator data ... */
/* ... from text weather data */
int get_value( char * value, int field_pos ) {

    fld_desc * field ;
    char field_buf[31] ;
    double * double_ptr ;
    int * int_ptr, buf_pos ;

    while ( fields[field_pos].fld_length != 0 ) {
       field = &fields[field_pos] ;
       memset( field_buf, '\0', 31 ) ;
       memcpy( field_buf,
               ( value + field->fld_offset ),
               field->fld_length ) ;
       buf_pos = field->fld_length ;
       while ( ( buf_pos > 0 ) &&
               ( field_buf[buf_pos] == ' ' ) )
          field_buf[buf_pos--] = '\0' ;
       buf_pos = 0 ;
       while ( ( buf_pos < field->fld_length ) &&
               ( field_buf[buf_pos] == ' ' ) )
          buf_pos++ ;
       if ( strlen( ( char * ) ( field_buf + buf_pos ) ) > 0 ||
            strcmp( ( char * ) ( field_buf + buf_pos ), "n/a") != 0 ) {
          field->fld_ind = SQL_NOTNULL ;

          /* Text to SQL type conversion */
          switch( field->fld_type ) {
            case SQL_TYP_VARCHAR:
                 strcpy( field->fld_field,
                         ( char * ) ( field_buf + buf_pos ) ) ;
                 break ;
            case SQL_TYP_INTEGER:
                 int_ptr = ( int * ) field->fld_field ;
                 *int_ptr = atoi( ( char * ) ( field_buf + buf_pos ) ) ;
                 break ;
            case SQL_TYP_FLOAT:
                 double_ptr = ( double * ) field->fld_field ;
                 *double_ptr = atof( ( char * ) ( field_buf + buf_pos ) ) ;
                 break ;
            /* You may want to add more text to SQL type conversion here */
          }

       }
       field_pos++ ;
    }
    return( 0 ) ;

}

#ifdef __cplusplus
extern "C"
#endif
void SQL_API_FN weather( /* Return row fields */
              SQLUDF_VARCHAR * city,
              SQLUDF_INTEGER * temp_in_f,
              SQLUDF_INTEGER * humidity,
              SQLUDF_VARCHAR * wind,
              SQLUDF_INTEGER * wind_velocity,
              SQLUDF_DOUBLE  * barometer,
              SQLUDF_VARCHAR * forecast,
              /* You may want to add more fields here */

              /* Return row field null indicators */
              SQLUDF_NULLIND * city_ind,
              SQLUDF_NULLIND * temp_in_f_ind,
              SQLUDF_NULLIND * humidity_ind,
              SQLUDF_NULLIND * wind_ind,
              SQLUDF_NULLIND * wind_velocity_ind,
              SQLUDF_NULLIND * barometer_ind,
              SQLUDF_NULLIND * forecast_ind,
              /* You may want to add more field indicators here */

              /* UDF always-present (trailing) input arguments */
              SQLUDF_TRAIL_ARGS_ALL
            ) {

  scratch_area * save_area ;
  char line_buf[81] ;
  int line_buf_pos ;

  /* SQLUDF_SCRAT is part of SQLUDF_TRAIL_ARGS_ALL */
  /* Preserve information from one function call to the next call */
  save_area = ( scratch_area * ) ( SQLUDF_SCRAT->data ) ;

  /* SQLUDF_CALLT is part of SQLUDF_TRAIL_ARGS_ALL */
  switch( SQLUDF_CALLT ) {

    /* First call UDF: Open table and fetch first row */
    case SQL_TF_OPEN:
         /* If you use a weather data text file specify full path */
         /* save_area->file_ptr = fopen("/sqllib/samples/c/tblsrv.dat",
                                        "r"); */
         save_area->file_pos = 0 ;
         break ;

    /* Normal call UDF: Fetch next row */
    case SQL_TF_FETCH:
         /* If you use a weather data text file */
         /* memset(line_buf, '\0', 81); */
         /* if (fgets(line_buf, 80, save_area->file_ptr) == NULL) { */
         if ( weather_data[save_area->file_pos] == ( char * ) 0 ) {

            /* SQLUDF_STATE is part of SQLUDF_TRAIL_ARGS_ALL */
            strcpy( SQLUDF_STATE, "02000" ) ;

            break ;
         }
         memset( line_buf, '\0', 81 ) ;
         strcpy( line_buf, weather_data[save_area->file_pos] ) ;
         line_buf[3] = '\0' ;

         /* Clean all field data and field null indicator data */
         clean_fields( 0 ) ;

         /* Fills city field null indicator data */
         fields[0].fld_ind = SQL_NOTNULL ;

         /* Find a full city name using a short name */
         /* Fills city field data */
         if ( get_name( line_buf, fields[0].fld_field ) == 0 ) {
            save_area->file_pos++ ;
            /* If you use a weather data text file */
            /* memset(line_buf, '\0', 81); */
            /* if (fgets(line_buf, 80, save_area->file_ptr) == NULL) { */
            if ( weather_data[save_area->file_pos] == ( char * ) 0 ) {
               /* SQLUDF_STATE is part of SQLUDF_TRAIL_ARGS_ALL */
               strcpy( SQLUDF_STATE, "02000" ) ;
               break ;
            }
            memset( line_buf, '\0', 81 ) ;
            strcpy( line_buf, weather_data[save_area->file_pos] ) ;
            line_buf_pos = strlen( line_buf ) ;
            while ( line_buf_pos > 0 ) {
               if ( line_buf[line_buf_pos] >= ' ' )
                  line_buf_pos = 0 ;
               else {
                  line_buf[line_buf_pos] = '\0' ;
                  line_buf_pos-- ;
               }
            }
         }

         /* Fills field data and field null indicator data ... */
         /* ... for selected city from text weather data */
         get_value( line_buf, 1 ) ;  /* Skips city field */

         /* Builds return row fields */
         strcpy( city, fields[0].fld_field ) ;
         memcpy( (void *) temp_in_f,
                 fields[1].fld_field,
                 sizeof( SQLUDF_INTEGER ) ) ;
         memcpy( (void *) humidity,
                 fields[2].fld_field,
                 sizeof( SQLUDF_INTEGER ) ) ;
         strcpy( wind, fields[3].fld_field ) ;
         memcpy( (void *) wind_velocity,
                 fields[4].fld_field,
                 sizeof( SQLUDF_INTEGER ) ) ;
         memcpy( (void *) barometer,
                 fields[5].fld_field,
                 sizeof( SQLUDF_DOUBLE ) ) ;
         strcpy( forecast, fields[6].fld_field ) ;

         /* Builds return row field null indicators */
         memcpy( (void *) city_ind,
                 &(fields[0].fld_ind),
                 sizeof( SQLUDF_NULLIND ) ) ;
         memcpy( (void *) temp_in_f_ind,
                 &(fields[1].fld_ind),
                 sizeof( SQLUDF_NULLIND ) ) ;
         memcpy( (void *) humidity_ind,
                 &(fields[2].fld_ind),
                 sizeof( SQLUDF_NULLIND ) ) ;
         memcpy( (void *) wind_ind,
                 &(fields[3].fld_ind),
                 sizeof( SQLUDF_NULLIND ) ) ;
         memcpy( (void *) wind_velocity_ind,
                 &(fields[4].fld_ind),
                 sizeof( SQLUDF_NULLIND ) ) ;
         memcpy( (void *) barometer_ind,
                 &(fields[5].fld_ind),
                 sizeof( SQLUDF_NULLIND ) ) ;
         memcpy( (void *) forecast_ind,
                 &(fields[6].fld_ind),
                 sizeof( SQLUDF_NULLIND ) ) ;

         /* Next city weather data */
         save_area->file_pos++ ;

         break ;

    /* Special last call UDF for cleanup (no real args!): Close table */
    case SQL_TF_CLOSE:
         /* If you use a weather data text file */
         /* fclose(save_area->file_ptr); */
         /* save_area->file_ptr = NULL; */
         save_area->file_pos = 0 ;
         break ;
 
  }

}