123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- /*
- * Licensed Materials - Property of HCL
- *
- * IBM Informix DataBlade Module
- * (C) Copyright International Business Machines Corporation 2002.
- * (c) Copyright HCL Technologies Ltd. 2017. All Rights Reserved.
- *
- * COPYRIGHT LICENSE:
- * This information contains sample application programs in source language,
- * which illustrate programming techniques on various operating platforms.
- * You may copy, modify, and distribute these sample programs in any form
- * without payment to IBM, for the purposes of developing, using, marketing
- * or distributing application programs conforming to the application
- * programming interface for the operating platform for which the sample
- * programs are written. These examples have not been thoroughly tested under
- * all conditions. IBM, therefore, cannot guarantee or imply reliability,
- * serviceability, or function of these programs. You may copy, modify, and
- * distribute these sample programs in any form without payment to IBM for
- * the purposes of developing, using, marketing, or distributing application
- * programs conforming to IBM's application programming interfaces.
- * Each copy or any portion of these sample programs or any derivative work,
- * must include a copyright notice as follows:
- * © (your company name) (year). Portions of this code are derived from
- * IBM Corp. Sample Programs. © Copyright IBM Corp. (enter the year or
- * years). All rights reserved.
- *
- */
- # include "tseries.h"
- /*
- * Sample moving average code
- */
- /* base types we can handle */
- typedef enum
- {
- ACC_INT2,
- ACC_INT4,
- ACC_INT8,
- ACC_SMALLFLOAT,
- ACC_FLOAT,
- ACC_MONEY,
- ACC_DECIMAL
- } ts_acc_types;
- /* values for accum_flags */
- # define ACC_READY 1
- /*& accumulator structure */
- typedef struct _ts_accum {
- MI_DATUM *accum_buffer; /* buffer to hold values */
- mi_integer accum_pos; /* index into buffer, put data here then inc */
- mi_integer accum_winsize; /* number of intervals in window */
- mi_integer accum_colnum; /* column number in element being accumulated */
- mi_integer accum_flags; /* currently says if we have had a full window*/
- MI_DATUM accum_accum; /* accumulator value */
- MI_DATUM accum_ret; /* return value */
- ts_acc_types accum_type; /* which base type this column is */
- struct _ts_accum *accum_next; /* next column accumulator pointer */
- } ts_accum;
- /*
- * funciton for doing a moving average. This might be better split into
- * separate functions for each base type.
- */
- static MI_DATUM
- ts_mov_avg(ts_accum *accum,
- MI_DATUM val,
- mi_boolean nullval,
- mi_boolean *isNull)
- {
- mi_integer pos;
- MI_DATUM oldval;
- mi_smallint int2_tmp;
- mi_integer int4_tmp;
- mi_int8 int8_tmp;
- mi_decimal dec_tmp;
- mi_real real_tmp;
- mi_double_precision dbl_tmp;
- /* get current index into buffer */
- pos = accum->accum_pos;
- switch (accum->accum_type) {
- case ACC_INT2:
- if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
- /* get old value if there was one */
- int2_tmp = *((mi_smallint *) &accum->accum_buffer[pos]);
- /* put new value into buffer */
- if (nullval)
- *(mi_smallint *) &val = 0;
- *((mi_smallint *) &accum->accum_buffer[pos]) = (mi_smallint) val;
- /* add new value to accumulator */
- *(mi_smallint *) &accum->accum_accum += (mi_smallint) val;
- if (++accum->accum_pos == accum->accum_winsize) {
- /* we have filled our window for the first time */
- accum->accum_flags |= ACC_READY;
- accum->accum_pos = 0;
- }
- if (accum->accum_flags & ACC_READY) {
- /* remove oldest value from accum if there was one */
- *(mi_smallint *) &accum->accum_accum -= int2_tmp;
- *(mi_smallint *) &accum->accum_ret = *(mi_smallint *) &accum->accum_accum / accum->accum_winsize;
- }
- break;
- case ACC_INT4:
- if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
- /* get old value if there was one */
- int4_tmp = *((mi_integer *) &accum->accum_buffer[pos]);
- /* put new value into buffer */
- if (nullval)
- *(mi_integer *) &val = 0;
- *((mi_integer *) &accum->accum_buffer[pos]) = (mi_integer) val;
- /* add new value to accumulator */
- *(mi_integer *) &accum->accum_accum += (mi_integer) val;
- if (++accum->accum_pos == accum->accum_winsize) {
- /* we have filled our window for the first time */
- accum->accum_flags |= ACC_READY;
- accum->accum_pos = 0;
- }
- if (accum->accum_flags & ACC_READY) {
- /* remove oldest value from accum if there was one */
- *(mi_integer *) &accum->accum_accum -= (mi_integer) int4_tmp;
- *(mi_integer *) &accum->accum_ret = *(mi_integer *) &accum->accum_accum / accum->accum_winsize;
- }
- break;
- case ACC_INT8:
- if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
- /* get old value if there was one */
- ifx_int8copy((mi_int8 *) accum->accum_buffer[pos], &int8_tmp);
- /* put new value into buffer */
- if (nullval)
- ifx_int8cvint(0, (mi_int8 *) accum->accum_buffer[pos]);
- else {
- ifx_int8copy((mi_int8 *) val, (mi_int8 *) accum->accum_buffer[pos]);
- /* add new value to accumulator */
- ifx_int8add((mi_int8 *) accum->accum_accum, (mi_int8 *) val, (mi_int8 *) accum->accum_accum);
- }
- if (++accum->accum_pos == accum->accum_winsize) {
- /* we have filled our window for the first time */
- accum->accum_flags |= ACC_READY;
- accum->accum_pos = 0;
- }
- if (accum->accum_flags & ACC_READY) {
- /* remove oldest value from accum if there was one */
- ifx_int8sub((mi_int8 *) &accum->accum_accum, &int8_tmp, (mi_int8 *) accum->accum_accum);
- ifx_int8cvint(accum->accum_winsize, &int8_tmp);
- ifx_int8div((mi_int8 *) &accum->accum_accum, &int8_tmp, (mi_int8 *) accum->accum_ret);
- }
- break;
- case ACC_SMALLFLOAT:
- if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
- /* get old value if there was one */
- real_tmp = *((mi_real *) accum->accum_buffer[pos]);
- /* put new value into buffer */
- if (nullval)
- *((mi_real *) accum->accum_buffer[pos]) = (mi_real) 0;
- else {
- *((mi_real *) accum->accum_buffer[pos]) = *(mi_real *) val;
- /* add new value to accumulator */
- *(mi_real *) accum->accum_accum += *(mi_real *) val;
- }
- if (++accum->accum_pos == accum->accum_winsize) {
- /* we have filled our window for the first time */
- accum->accum_flags |= ACC_READY;
- accum->accum_pos = 0;
- }
- if (accum->accum_flags & ACC_READY) {
- /* remove oldest value from accum if there was one */
- *(mi_real *) accum->accum_accum -= real_tmp;
- *(mi_real *) accum->accum_ret = *(mi_real *) accum->accum_accum / accum->accum_winsize;
- }
- break;
- case ACC_FLOAT:
- if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
- /* get old value if there was one */
- dbl_tmp = *((mi_double_precision *) accum->accum_buffer[pos]);
- /* put new value into buffer */
- if (nullval)
- *((mi_double_precision *) accum->accum_buffer[pos]) = (mi_double_precision) 0;
- else {
- *((mi_double_precision *) accum->accum_buffer[pos]) = *(mi_double_precision *) val;
- /* add new value to accumulator */
- *(mi_double_precision *) accum->accum_accum += *(mi_double_precision *) val;
- }
- if (++accum->accum_pos == accum->accum_winsize) {
- /* we have filled our window for the first time */
- accum->accum_flags |= ACC_READY;
- accum->accum_pos = 0;
- }
- if (accum->accum_flags & ACC_READY) {
- /* remove oldest value from accum if there was one */
- *(mi_double_precision *) accum->accum_accum -= dbl_tmp;
- *(mi_double_precision *) accum->accum_ret = *(mi_double_precision *) accum->accum_accum / accum->accum_winsize;
- }
- break;
- case ACC_DECIMAL:
- case ACC_MONEY:
- if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
- /* get old value if there was one */
- deccopy((mi_decimal *) accum->accum_buffer[pos], &dec_tmp);
- /* put new value into buffer */
- if (nullval)
- deccvint(0, (mi_decimal *) accum->accum_buffer[pos]);
- else {
- deccopy((mi_decimal *) val, (mi_decimal *) accum->accum_buffer[pos]);
- /* add new value to accumulator */
- decadd((mi_decimal *) accum->accum_accum, (mi_decimal *) val, (mi_decimal *) accum->accum_accum);
- }
- if (++accum->accum_pos == accum->accum_winsize) {
- /* we have filled our window for the first time */
- accum->accum_flags |= ACC_READY;
- accum->accum_pos = 0;
- }
- if (accum->accum_flags & ACC_READY) {
- /* remove oldest value from accum if there was one */
- decsub((mi_decimal *) &accum->accum_accum, &dec_tmp, (mi_decimal *) accum->accum_accum);
- deccvint(accum->accum_winsize, &dec_tmp);
- decdiv((mi_decimal *) accum->accum_accum, &dec_tmp, (mi_decimal *) accum->accum_ret);
- }
- break;
- }
- /* return null until we have seen winsize number of values */
- *isNull = !(accum->accum_flags & ACC_READY);
- /* return result */
- return(accum->accum_ret);
- }
- /*
- * call a function over a moving window. Currently only avg is implemented
- */
- ts_timeseries *
- ts_moving_func(MI_CONNECTION *conn,
- ts_tsdesc *tsdesc, /* source timeseries */
- mi_integer window_size, /* number of intervals in window */
- mi_integer colnum, /* col to agg, -1 mean all cols */
- mi_datetime *tstart, /* start date, can be null */
- mi_datetime *tend, /* end date, can be null */
- mi_string *func_name) /* name of function to run */
- {
- ts_accum *accum, *head;
- MI_DATUM (*func)(ts_accum *, MI_DATUM, mi_boolean, mi_boolean *);
- mi_string *typename;
- mi_integer start, end, i, j;
- ts_typeinfo *tinfo;
- ts_tscan *scan;
- ts_timeseries *newts, *ts;
- ts_tselem elem, newelem;
- ts_tsdesc *newtsdesc;
- MI_DATUM newval;
- mi_boolean isNull;
- mi_boolean *nulls;
- MI_DATUM *values;
- /* only avg is currently implemented */
- if (strcmp(func_name, "avg") == 0) {
- func = ts_mov_avg;
- } else {
- mi_db_error_raise(NULL, MI_FATAL, "bad function passed in");
- }
- /* setup min and max column numbers */
- if (colnum == -1) {
- /* doing all columns */
- start = 1;
- end = ts_col_cnt(tsdesc);
- } else {
- /* doing just one column */
- start = colnum;
- end = colnum + 1;
- }
- /* make a linked list of all columns involved */
- head = NULL;
- for (i = start; i < end; i++) {
- /*
- * note: code further on depends on accum
- * to point to columns in decending order
- */
- accum = mi_zalloc(sizeof(ts_accum));
- if (head == NULL) {
- /* first in list */
- head = accum;
- accum->accum_next = NULL;
- } else {
- /* add to head of list */
- accum->accum_next = head;
- head = accum;
- }
- /* setup window buffer for this column */
- accum->accum_buffer = mi_zalloc(window_size * sizeof(MI_DATUM));
- accum->accum_flags = 0;
- accum->accum_pos = 0;
- accum->accum_winsize = window_size;
- accum->accum_colnum = i;
- tinfo = ts_colinfo_number(tsdesc, i);
- /* setup type specific info */
- if (strcmp(tinfo->ti_typename, "smallint") == 0) {
- accum->accum_type = ACC_INT2;
- } else if (strcmp(tinfo->ti_typename, "integer") == 0) {
- accum->accum_type = ACC_INT4;
- } else if (strcmp(tinfo->ti_typename, "int8") == 0) {
- /* int8's are accessed as pointers, so we must allocate space */
- accum->accum_type = ACC_INT8;
- accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_int8));
- accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_int8));
- for (j = 0; j < accum->accum_winsize; j++)
- accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_int8));
- } else if (strcmp(tinfo->ti_typename, "smallfloat") == 0) {
- /* floats are accessed as pointers, so we must allocate space */
- accum->accum_type = ACC_SMALLFLOAT;
- accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_real));
- accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_real));
- for (j = 0; j < accum->accum_winsize; j++)
- accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_real));
- } else if (strcmp(tinfo->ti_typename, "float") == 0) {
- /* floats are accessed as pointers, so we must allocate space */
- accum->accum_type = ACC_FLOAT;
- accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_double_precision));
- accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_double_precision));
- for (j = 0; j < accum->accum_winsize; j++)
- accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_double_precision));
- } else if (strcmp(tinfo->ti_typename, "money") == 0) {
- /* money is accessed as pointers, so we must allocate space */
- accum->accum_type = ACC_MONEY;
- accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
- accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
- for (j = 0; j < accum->accum_winsize; j++)
- accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
- } else if (strcmp(tinfo->ti_typename, "decimal") == 0) {
- /* decimals are accessed as pointers, so we must allocate space */
- accum->accum_type = ACC_DECIMAL;
- accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
- accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
- for (j = 0; j < accum->accum_winsize; j++)
- accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
- } else
- /* we only handle the above column types */
- mi_db_error_raise(NULL, MI_FATAL, "type not supported");
- }
- /* get the source timeseries */
- ts = ts_get_ts(tsdesc);
- /* create the return timeseriers */
- newts = ts_create(conn,
- ts_get_calname(ts),
- tstart == NULL ? ts_get_origin(ts) : tstart,
- ts_get_threshold(ts),
- TS_IS_IRREGULAR(ts) ? TSFLAGS_IRR : 0,
- ts_get_typeid(conn, ts),
- (tstart != NULL && tend != NULL)
- ? ts_cal_index(conn, ts_get_calname(ts), tstart, tend)
- : 0,
- ts_get_containername(ts));
- /* open the return timeseries */
- newtsdesc = ts_open(conn, newts, ts_get_typeid(conn, ts), 0);
- /* begin the scan of the source timeseries */
- scan = ts_begin_scan(tsdesc, 0, tstart, tend);
- /* allocate the arrays for creating elements */
- values = (MI_DATUM *) mi_zalloc(sizeof(MI_DATUM) * ts_col_cnt(tsdesc));
- nulls = (mi_boolean *) mi_zalloc(sizeof(mi_boolean) * ts_col_cnt(tsdesc));
- newelem = NULL;
- while (ts_next(scan, &elem) != TS_SCAN_EOS) {
- accum = head;
- nulls[0] = MI_TRUE; /* the datetime column is always null */
- /* loop through each column of each element last col to first */
- for (i = ts_col_cnt(tsdesc) - 1; i >= 1; i--) {
- /* get column "i" value */
- if (elem == NULL)
- nulls[i] = MI_TRUE;
- else
- values[i] = ts_get_col_by_number(tsdesc, elem, i, &nulls[i],-1);
- if (accum && accum->accum_colnum == i) {
- /* this is a column we are accumulating */
- isNull = MI_FALSE;
- /* call accumulator function */
- values[i] = func(accum, values[i], nulls[i], &isNull);
- nulls[i] = isNull;
- /* on to next column, if there is one */
- accum = accum->accum_next;
- }
- }
- /* make the element to put in the return timeseries */
- newelem = ts_make_elem_with_buf(newtsdesc, values, nulls, NULL, newelem);
- /* put it into the return timeseries at the given stamp */
- (void) ts_put_elem(newtsdesc, newelem, ts_current_timestamp(scan));
- }
- /* clean up */
- ts_end_scan(scan);
- ts_close(newtsdesc);
- /* all done */
- return(newts);
- }
- /*
- * SQL entry point
- * create function TsMovAvg(TimeSeries,
- * integer,
- * integer,
- * datetime year to fraction(5) default NULL,
- * datetime year to fraction(5) default NULL)
- * returns timeseries with (handlesnulls)
- * external name
- * '$INFORMIXDIR/functions/tsmov.bld(ts_moving_avg_sql)'
- * language c not variant;
- */
- #ifdef NT
- __declspec(dllexport)
- #endif
- ts_timeseries *
- ts_moving_avg_sql(ts_timeseries *ts,
- mi_integer window_size, /* number of intervals in window */
- mi_integer colnum, /* col to agg, -1 means all */
- mi_datetime *tstart, /* start time, can be null */
- mi_datetime *tend, /* end time, can be null */
- MI_FPARAM *fp)
- {
- MI_CONNECTION *conn;
- ts_timeseries *ret;
- ts_tsdesc *tsdesc;
- /* get a connections */
- conn = mi_open(NULL, NULL, NULL);
- /* see if start and end were passed as null values */
- if (mi_fp_argisnull(fp, 3))
- tstart = 0;
- if (mi_fp_argisnull(fp, 4))
- tend = 0;
- /* open the source timeseries */
- tsdesc = ts_open(conn, ts, mi_fp_argtype(fp, 0), 0);
- /* go do the work! */
- ret = ts_moving_func(conn, tsdesc, window_size, colnum, tstart, tend, "avg");
- /* clean up */
- ts_close(tsdesc);
- mi_close(conn);
- return(ret);
- }
|