tsmov.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. * Licensed Materials - Property of HCL
  3. *
  4. * IBM Informix DataBlade Module
  5. * (C) Copyright International Business Machines Corporation 2002.
  6. * (c) Copyright HCL Technologies Ltd. 2017. All Rights Reserved.
  7. *
  8. * COPYRIGHT LICENSE:
  9. * This information contains sample application programs in source language,
  10. * which illustrate programming techniques on various operating platforms.
  11. * You may copy, modify, and distribute these sample programs in any form
  12. * without payment to IBM, for the purposes of developing, using, marketing
  13. * or distributing application programs conforming to the application
  14. * programming interface for the operating platform for which the sample
  15. * programs are written. These examples have not been thoroughly tested under
  16. * all conditions. IBM, therefore, cannot guarantee or imply reliability,
  17. * serviceability, or function of these programs. You may copy, modify, and
  18. * distribute these sample programs in any form without payment to IBM for
  19. * the purposes of developing, using, marketing, or distributing application
  20. * programs conforming to IBM's application programming interfaces.
  21. * Each copy or any portion of these sample programs or any derivative work,
  22. * must include a copyright notice as follows:
  23. * © (your company name) (year). Portions of this code are derived from
  24. * IBM Corp. Sample Programs. © Copyright IBM Corp. (enter the year or
  25. * years). All rights reserved.
  26. *
  27. */
  28. # include "tseries.h"
  29. /*
  30. * Sample moving average code
  31. */
  32. /* base types we can handle */
  33. typedef enum
  34. {
  35. ACC_INT2,
  36. ACC_INT4,
  37. ACC_INT8,
  38. ACC_SMALLFLOAT,
  39. ACC_FLOAT,
  40. ACC_MONEY,
  41. ACC_DECIMAL
  42. } ts_acc_types;
  43. /* values for accum_flags */
  44. # define ACC_READY 1
  45. /*& accumulator structure */
  46. typedef struct _ts_accum {
  47. MI_DATUM *accum_buffer; /* buffer to hold values */
  48. mi_integer accum_pos; /* index into buffer, put data here then inc */
  49. mi_integer accum_winsize; /* number of intervals in window */
  50. mi_integer accum_colnum; /* column number in element being accumulated */
  51. mi_integer accum_flags; /* currently says if we have had a full window*/
  52. MI_DATUM accum_accum; /* accumulator value */
  53. MI_DATUM accum_ret; /* return value */
  54. ts_acc_types accum_type; /* which base type this column is */
  55. struct _ts_accum *accum_next; /* next column accumulator pointer */
  56. } ts_accum;
  57. /*
  58. * funciton for doing a moving average. This might be better split into
  59. * separate functions for each base type.
  60. */
  61. static MI_DATUM
  62. ts_mov_avg(ts_accum *accum,
  63. MI_DATUM val,
  64. mi_boolean nullval,
  65. mi_boolean *isNull)
  66. {
  67. mi_integer pos;
  68. MI_DATUM oldval;
  69. mi_smallint int2_tmp;
  70. mi_integer int4_tmp;
  71. mi_int8 int8_tmp;
  72. mi_decimal dec_tmp;
  73. mi_real real_tmp;
  74. mi_double_precision dbl_tmp;
  75. /* get current index into buffer */
  76. pos = accum->accum_pos;
  77. switch (accum->accum_type) {
  78. case ACC_INT2:
  79. if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
  80. /* get old value if there was one */
  81. int2_tmp = *((mi_smallint *) &accum->accum_buffer[pos]);
  82. /* put new value into buffer */
  83. if (nullval)
  84. *(mi_smallint *) &val = 0;
  85. *((mi_smallint *) &accum->accum_buffer[pos]) = (mi_smallint) val;
  86. /* add new value to accumulator */
  87. *(mi_smallint *) &accum->accum_accum += (mi_smallint) val;
  88. if (++accum->accum_pos == accum->accum_winsize) {
  89. /* we have filled our window for the first time */
  90. accum->accum_flags |= ACC_READY;
  91. accum->accum_pos = 0;
  92. }
  93. if (accum->accum_flags & ACC_READY) {
  94. /* remove oldest value from accum if there was one */
  95. *(mi_smallint *) &accum->accum_accum -= int2_tmp;
  96. *(mi_smallint *) &accum->accum_ret = *(mi_smallint *) &accum->accum_accum / accum->accum_winsize;
  97. }
  98. break;
  99. case ACC_INT4:
  100. if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
  101. /* get old value if there was one */
  102. int4_tmp = *((mi_integer *) &accum->accum_buffer[pos]);
  103. /* put new value into buffer */
  104. if (nullval)
  105. *(mi_integer *) &val = 0;
  106. *((mi_integer *) &accum->accum_buffer[pos]) = (mi_integer) val;
  107. /* add new value to accumulator */
  108. *(mi_integer *) &accum->accum_accum += (mi_integer) val;
  109. if (++accum->accum_pos == accum->accum_winsize) {
  110. /* we have filled our window for the first time */
  111. accum->accum_flags |= ACC_READY;
  112. accum->accum_pos = 0;
  113. }
  114. if (accum->accum_flags & ACC_READY) {
  115. /* remove oldest value from accum if there was one */
  116. *(mi_integer *) &accum->accum_accum -= (mi_integer) int4_tmp;
  117. *(mi_integer *) &accum->accum_ret = *(mi_integer *) &accum->accum_accum / accum->accum_winsize;
  118. }
  119. break;
  120. case ACC_INT8:
  121. if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
  122. /* get old value if there was one */
  123. ifx_int8copy((mi_int8 *) accum->accum_buffer[pos], &int8_tmp);
  124. /* put new value into buffer */
  125. if (nullval)
  126. ifx_int8cvint(0, (mi_int8 *) accum->accum_buffer[pos]);
  127. else {
  128. ifx_int8copy((mi_int8 *) val, (mi_int8 *) accum->accum_buffer[pos]);
  129. /* add new value to accumulator */
  130. ifx_int8add((mi_int8 *) accum->accum_accum, (mi_int8 *) val, (mi_int8 *) accum->accum_accum);
  131. }
  132. if (++accum->accum_pos == accum->accum_winsize) {
  133. /* we have filled our window for the first time */
  134. accum->accum_flags |= ACC_READY;
  135. accum->accum_pos = 0;
  136. }
  137. if (accum->accum_flags & ACC_READY) {
  138. /* remove oldest value from accum if there was one */
  139. ifx_int8sub((mi_int8 *) &accum->accum_accum, &int8_tmp, (mi_int8 *) accum->accum_accum);
  140. ifx_int8cvint(accum->accum_winsize, &int8_tmp);
  141. ifx_int8div((mi_int8 *) &accum->accum_accum, &int8_tmp, (mi_int8 *) accum->accum_ret);
  142. }
  143. break;
  144. case ACC_SMALLFLOAT:
  145. if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
  146. /* get old value if there was one */
  147. real_tmp = *((mi_real *) accum->accum_buffer[pos]);
  148. /* put new value into buffer */
  149. if (nullval)
  150. *((mi_real *) accum->accum_buffer[pos]) = (mi_real) 0;
  151. else {
  152. *((mi_real *) accum->accum_buffer[pos]) = *(mi_real *) val;
  153. /* add new value to accumulator */
  154. *(mi_real *) accum->accum_accum += *(mi_real *) val;
  155. }
  156. if (++accum->accum_pos == accum->accum_winsize) {
  157. /* we have filled our window for the first time */
  158. accum->accum_flags |= ACC_READY;
  159. accum->accum_pos = 0;
  160. }
  161. if (accum->accum_flags & ACC_READY) {
  162. /* remove oldest value from accum if there was one */
  163. *(mi_real *) accum->accum_accum -= real_tmp;
  164. *(mi_real *) accum->accum_ret = *(mi_real *) accum->accum_accum / accum->accum_winsize;
  165. }
  166. break;
  167. case ACC_FLOAT:
  168. if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
  169. /* get old value if there was one */
  170. dbl_tmp = *((mi_double_precision *) accum->accum_buffer[pos]);
  171. /* put new value into buffer */
  172. if (nullval)
  173. *((mi_double_precision *) accum->accum_buffer[pos]) = (mi_double_precision) 0;
  174. else {
  175. *((mi_double_precision *) accum->accum_buffer[pos]) = *(mi_double_precision *) val;
  176. /* add new value to accumulator */
  177. *(mi_double_precision *) accum->accum_accum += *(mi_double_precision *) val;
  178. }
  179. if (++accum->accum_pos == accum->accum_winsize) {
  180. /* we have filled our window for the first time */
  181. accum->accum_flags |= ACC_READY;
  182. accum->accum_pos = 0;
  183. }
  184. if (accum->accum_flags & ACC_READY) {
  185. /* remove oldest value from accum if there was one */
  186. *(mi_double_precision *) accum->accum_accum -= dbl_tmp;
  187. *(mi_double_precision *) accum->accum_ret = *(mi_double_precision *) accum->accum_accum / accum->accum_winsize;
  188. }
  189. break;
  190. case ACC_DECIMAL:
  191. case ACC_MONEY:
  192. if ((accum->accum_flags & ACC_READY) || pos + 1 == accum->accum_winsize)
  193. /* get old value if there was one */
  194. deccopy((mi_decimal *) accum->accum_buffer[pos], &dec_tmp);
  195. /* put new value into buffer */
  196. if (nullval)
  197. deccvint(0, (mi_decimal *) accum->accum_buffer[pos]);
  198. else {
  199. deccopy((mi_decimal *) val, (mi_decimal *) accum->accum_buffer[pos]);
  200. /* add new value to accumulator */
  201. decadd((mi_decimal *) accum->accum_accum, (mi_decimal *) val, (mi_decimal *) accum->accum_accum);
  202. }
  203. if (++accum->accum_pos == accum->accum_winsize) {
  204. /* we have filled our window for the first time */
  205. accum->accum_flags |= ACC_READY;
  206. accum->accum_pos = 0;
  207. }
  208. if (accum->accum_flags & ACC_READY) {
  209. /* remove oldest value from accum if there was one */
  210. decsub((mi_decimal *) &accum->accum_accum, &dec_tmp, (mi_decimal *) accum->accum_accum);
  211. deccvint(accum->accum_winsize, &dec_tmp);
  212. decdiv((mi_decimal *) accum->accum_accum, &dec_tmp, (mi_decimal *) accum->accum_ret);
  213. }
  214. break;
  215. }
  216. /* return null until we have seen winsize number of values */
  217. *isNull = !(accum->accum_flags & ACC_READY);
  218. /* return result */
  219. return(accum->accum_ret);
  220. }
  221. /*
  222. * call a function over a moving window. Currently only avg is implemented
  223. */
  224. ts_timeseries *
  225. ts_moving_func(MI_CONNECTION *conn,
  226. ts_tsdesc *tsdesc, /* source timeseries */
  227. mi_integer window_size, /* number of intervals in window */
  228. mi_integer colnum, /* col to agg, -1 mean all cols */
  229. mi_datetime *tstart, /* start date, can be null */
  230. mi_datetime *tend, /* end date, can be null */
  231. mi_string *func_name) /* name of function to run */
  232. {
  233. ts_accum *accum, *head;
  234. MI_DATUM (*func)(ts_accum *, MI_DATUM, mi_boolean, mi_boolean *);
  235. mi_string *typename;
  236. mi_integer start, end, i, j;
  237. ts_typeinfo *tinfo;
  238. ts_tscan *scan;
  239. ts_timeseries *newts, *ts;
  240. ts_tselem elem, newelem;
  241. ts_tsdesc *newtsdesc;
  242. MI_DATUM newval;
  243. mi_boolean isNull;
  244. mi_boolean *nulls;
  245. MI_DATUM *values;
  246. /* only avg is currently implemented */
  247. if (strcmp(func_name, "avg") == 0) {
  248. func = ts_mov_avg;
  249. } else {
  250. mi_db_error_raise(NULL, MI_FATAL, "bad function passed in");
  251. }
  252. /* setup min and max column numbers */
  253. if (colnum == -1) {
  254. /* doing all columns */
  255. start = 1;
  256. end = ts_col_cnt(tsdesc);
  257. } else {
  258. /* doing just one column */
  259. start = colnum;
  260. end = colnum + 1;
  261. }
  262. /* make a linked list of all columns involved */
  263. head = NULL;
  264. for (i = start; i < end; i++) {
  265. /*
  266. * note: code further on depends on accum
  267. * to point to columns in decending order
  268. */
  269. accum = mi_zalloc(sizeof(ts_accum));
  270. if (head == NULL) {
  271. /* first in list */
  272. head = accum;
  273. accum->accum_next = NULL;
  274. } else {
  275. /* add to head of list */
  276. accum->accum_next = head;
  277. head = accum;
  278. }
  279. /* setup window buffer for this column */
  280. accum->accum_buffer = mi_zalloc(window_size * sizeof(MI_DATUM));
  281. accum->accum_flags = 0;
  282. accum->accum_pos = 0;
  283. accum->accum_winsize = window_size;
  284. accum->accum_colnum = i;
  285. tinfo = ts_colinfo_number(tsdesc, i);
  286. /* setup type specific info */
  287. if (strcmp(tinfo->ti_typename, "smallint") == 0) {
  288. accum->accum_type = ACC_INT2;
  289. } else if (strcmp(tinfo->ti_typename, "integer") == 0) {
  290. accum->accum_type = ACC_INT4;
  291. } else if (strcmp(tinfo->ti_typename, "int8") == 0) {
  292. /* int8's are accessed as pointers, so we must allocate space */
  293. accum->accum_type = ACC_INT8;
  294. accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_int8));
  295. accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_int8));
  296. for (j = 0; j < accum->accum_winsize; j++)
  297. accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_int8));
  298. } else if (strcmp(tinfo->ti_typename, "smallfloat") == 0) {
  299. /* floats are accessed as pointers, so we must allocate space */
  300. accum->accum_type = ACC_SMALLFLOAT;
  301. accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_real));
  302. accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_real));
  303. for (j = 0; j < accum->accum_winsize; j++)
  304. accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_real));
  305. } else if (strcmp(tinfo->ti_typename, "float") == 0) {
  306. /* floats are accessed as pointers, so we must allocate space */
  307. accum->accum_type = ACC_FLOAT;
  308. accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_double_precision));
  309. accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_double_precision));
  310. for (j = 0; j < accum->accum_winsize; j++)
  311. accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_double_precision));
  312. } else if (strcmp(tinfo->ti_typename, "money") == 0) {
  313. /* money is accessed as pointers, so we must allocate space */
  314. accum->accum_type = ACC_MONEY;
  315. accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
  316. accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
  317. for (j = 0; j < accum->accum_winsize; j++)
  318. accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
  319. } else if (strcmp(tinfo->ti_typename, "decimal") == 0) {
  320. /* decimals are accessed as pointers, so we must allocate space */
  321. accum->accum_type = ACC_DECIMAL;
  322. accum->accum_accum = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
  323. accum->accum_ret = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
  324. for (j = 0; j < accum->accum_winsize; j++)
  325. accum->accum_buffer[j] = (MI_DATUM) mi_zalloc(sizeof(mi_decimal));
  326. } else
  327. /* we only handle the above column types */
  328. mi_db_error_raise(NULL, MI_FATAL, "type not supported");
  329. }
  330. /* get the source timeseries */
  331. ts = ts_get_ts(tsdesc);
  332. /* create the return timeseriers */
  333. newts = ts_create(conn,
  334. ts_get_calname(ts),
  335. tstart == NULL ? ts_get_origin(ts) : tstart,
  336. ts_get_threshold(ts),
  337. TS_IS_IRREGULAR(ts) ? TSFLAGS_IRR : 0,
  338. ts_get_typeid(conn, ts),
  339. (tstart != NULL && tend != NULL)
  340. ? ts_cal_index(conn, ts_get_calname(ts), tstart, tend)
  341. : 0,
  342. ts_get_containername(ts));
  343. /* open the return timeseries */
  344. newtsdesc = ts_open(conn, newts, ts_get_typeid(conn, ts), 0);
  345. /* begin the scan of the source timeseries */
  346. scan = ts_begin_scan(tsdesc, 0, tstart, tend);
  347. /* allocate the arrays for creating elements */
  348. values = (MI_DATUM *) mi_zalloc(sizeof(MI_DATUM) * ts_col_cnt(tsdesc));
  349. nulls = (mi_boolean *) mi_zalloc(sizeof(mi_boolean) * ts_col_cnt(tsdesc));
  350. newelem = NULL;
  351. while (ts_next(scan, &elem) != TS_SCAN_EOS) {
  352. accum = head;
  353. nulls[0] = MI_TRUE; /* the datetime column is always null */
  354. /* loop through each column of each element last col to first */
  355. for (i = ts_col_cnt(tsdesc) - 1; i >= 1; i--) {
  356. /* get column "i" value */
  357. if (elem == NULL)
  358. nulls[i] = MI_TRUE;
  359. else
  360. values[i] = ts_get_col_by_number(tsdesc, elem, i, &nulls[i],-1);
  361. if (accum && accum->accum_colnum == i) {
  362. /* this is a column we are accumulating */
  363. isNull = MI_FALSE;
  364. /* call accumulator function */
  365. values[i] = func(accum, values[i], nulls[i], &isNull);
  366. nulls[i] = isNull;
  367. /* on to next column, if there is one */
  368. accum = accum->accum_next;
  369. }
  370. }
  371. /* make the element to put in the return timeseries */
  372. newelem = ts_make_elem_with_buf(newtsdesc, values, nulls, NULL, newelem);
  373. /* put it into the return timeseries at the given stamp */
  374. (void) ts_put_elem(newtsdesc, newelem, ts_current_timestamp(scan));
  375. }
  376. /* clean up */
  377. ts_end_scan(scan);
  378. ts_close(newtsdesc);
  379. /* all done */
  380. return(newts);
  381. }
  382. /*
  383. * SQL entry point
  384. * create function TsMovAvg(TimeSeries,
  385. * integer,
  386. * integer,
  387. * datetime year to fraction(5) default NULL,
  388. * datetime year to fraction(5) default NULL)
  389. * returns timeseries with (handlesnulls)
  390. * external name
  391. * '$INFORMIXDIR/functions/tsmov.bld(ts_moving_avg_sql)'
  392. * language c not variant;
  393. */
  394. #ifdef NT
  395. __declspec(dllexport)
  396. #endif
  397. ts_timeseries *
  398. ts_moving_avg_sql(ts_timeseries *ts,
  399. mi_integer window_size, /* number of intervals in window */
  400. mi_integer colnum, /* col to agg, -1 means all */
  401. mi_datetime *tstart, /* start time, can be null */
  402. mi_datetime *tend, /* end time, can be null */
  403. MI_FPARAM *fp)
  404. {
  405. MI_CONNECTION *conn;
  406. ts_timeseries *ret;
  407. ts_tsdesc *tsdesc;
  408. /* get a connections */
  409. conn = mi_open(NULL, NULL, NULL);
  410. /* see if start and end were passed as null values */
  411. if (mi_fp_argisnull(fp, 3))
  412. tstart = 0;
  413. if (mi_fp_argisnull(fp, 4))
  414. tend = 0;
  415. /* open the source timeseries */
  416. tsdesc = ts_open(conn, ts, mi_fp_argtype(fp, 0), 0);
  417. /* go do the work! */
  418. ret = ts_moving_func(conn, tsdesc, window_size, colnum, tstart, tend, "avg");
  419. /* clean up */
  420. ts_close(tsdesc);
  421. mi_close(conn);
  422. return(ret);
  423. }