1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 2002-2012 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21
22 static const char stats_usage[] =
23 "[+PLUGIN?\findex\f]"
24 "[+DESCRIPTION?The stats query lists the sum, average, unbiased standard"
25 " deviation, and minimum and maximum range values of the numeric"
26 " \afield\a operands. If no operands are specified then all numeric"
27 " fields are assumed. If \b-\b is specified then only records or"
28 " groups are counted. If any of \b--average\b, \b--count\b,"
29 " \b--deviation\b, \b--range\b or \b--sum\b are specified then only"
30 " those values are listed.]"
31 "[a:average?List the average.]"
32 "[c:count?List the record count.]"
33 "[d:deviation?List the unbiased standard deviation.]"
34 "[g:group?Group by values in \afield\a. More than one \b--group\b may be "
35 "specified; grouping is done by the tuple of all \afield\a "
36 "values.]:[field]"
37 "[l:label?Label the output with \alabel\a.]:[label]"
38 "[m:maxgroups?Maximum number of groupings. Values beyond \amax\a are"
39 " listed as \bOVERFLOW\b.]#[max:=100]"
40 "[p:print?Print summary data according to \aformat\a. The format fields"
41 " are:]:[format]{\ffields\f}"
42 "[r:range?List the minimum and maximum values.]"
43 "[s:sum?List the sum.]"
44 "\n"
45 "\n[ field ... ]\n"
46 "\n"
47 ;
48
49 #include <dsslib.h>
50 #include <ast_float.h>
51
52 #define FW 15
53
54 #define STATS_AVERAGE 0x0001
55 #define STATS_COUNT 0x0002
56 #define STATS_DEVIATION 0x0004
57 #define STATS_FIELD 0x0008
58 #define STATS_GROUP 0x0010
59 #define STATS_MAX 0x0020
60 #define STATS_MIN 0x0040
61 #define STATS_SUM 0x0080
62
63 #define STATS_RANGE (STATS_MAX|STATS_MIN)
64
65 struct Bucket_s; typedef struct Bucket_s Bucket_t;
66 struct Field_s; typedef struct Field_s Field_t;
67 struct Group_s; typedef struct Group_s Group_t;
68 struct Print_s; typedef struct Print_s Print_t;
69 struct State_s; typedef struct State_s State_t;
70 struct Total_s; typedef struct Total_s Total_t;
71
72 struct Total_s
73 {
74 Cxnumber_t value;
75 Cxnumber_t square;
76 Cxnumber_t min;
77 Cxnumber_t max;
78 Cxunsigned_t count;
79 };
80
81 struct Bucket_s
82 {
83 Dtlink_t link;
84 Cxoperand_t* key;
85 Total_t total[1];
86 };
87
88 struct Group_s
89 {
90 Group_t* next;
91 Cxvariable_t* variable;
92 int string;
93 int width;
94 };
95
96 struct Field_s
97 {
98 Field_t* next;
99 Cxvariable_t* variable;
100 };
101
102 struct Print_s
103 {
104 Field_t* field;
105 Group_t* group;
106 const char* label;
107 Total_t* total;
108 Cxoperand_t* key;
109 };
110
111 struct State_s
112 {
113 Dtdisc_t bucketdisc;
114 Field_t* field;
115 char* format;
116 Cx_t* print;
117 Group_t* group;
118 char* label;
119 Cxoperand_t* key;
120 Dt_t* buckets;
121 Total_t* total;
122 Vmalloc_t* vm;
123 int fields;
124 int fw;
125 int groups;
126 int maxgroups;
127 int op;
128 };
129
130 static Cxvariable_t variables[] =
131 {
132 CXV("AVERAGE", "number", STATS_AVERAGE, "Average value.")
133 CXV("COUNT", "number", STATS_COUNT, "Number of values.")
134 CXV("DEVIATION","number", STATS_DEVIATION, "Unbiased standard deviation.")
135 CXV("FIELD", "string", STATS_FIELD, "Field name.")
136 CXV("GROUP", "string", STATS_GROUP, "Group (key) values.")
137 CXV("MAX", "number", STATS_MAX, "Maximum value.")
138 CXV("MIN", "number", STATS_MIN, "Minimum value.")
139 CXV("SUM", "number", STATS_SUM, "Sum of values.")
140 };
141
142 extern Dsslib_t dss_lib_stats;
143
144 static int
getop(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)145 getop(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
146 {
147 register State_t* state = (State_t*)((Dssrecord_t*)data)->data;
148 Cxvariable_t* variable = pc->data.variable;
149 Group_t* group;
150 char* s;
151 char* v;
152
153 if (variable->data == (void*)&variables[0])
154 {
155 state->op |= variable->index;
156 if (variable->format.delimiter <= 0)
157 variable->format.delimiter = ':';
158 if (a && (s = a->value.string.data))
159 do
160 {
161 while (*s == ':')
162 s++;
163 for (v = s; *s && *s != ':'; s++);
164 if (strneq(v, "delimiter=", 10))
165 variable->format.delimiter = v[10];
166 else if (strneq(v, "nodelimiter", 11))
167 variable->format.delimiter = 0;
168 } while (*s);
169 }
170 else
171 {
172 group = state->group;
173 for (;;)
174 {
175 if (!group)
176 {
177 if (disc->errorf)
178 (*disc->errorf)(cx, disc, 2, "%s: unknown print variable", variable->name);
179 return -1;
180 }
181 if (group->variable == variable)
182 break;
183 group = group->next;
184 }
185 }
186 r->value.string.data = "";
187 r->value.string.size = 0;
188 return 0;
189 }
190
191 static int
getvalue(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)192 getvalue(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
193 {
194 register Print_t* print = (Print_t*)((Dssrecord_t*)data)->data;
195 Cxvariable_t* variable = pc->data.variable;
196 char* s;
197 Group_t* group;
198 Cxoperand_t* key;
199 Cxoperand_t arg;
200 Cxnumber_t u;
201 int sep;
202
203 if (variable->data == (void*)&variables[0])
204 switch (variable->index)
205 {
206 case STATS_AVERAGE:
207 r->value.number = print->total->count ? print->total->value / (Cxinteger_t)print->total->count : 0;
208 break;
209 case STATS_COUNT:
210 r->value.number = (Cxinteger_t)print->total->count;
211 break;
212 case STATS_DEVIATION:
213 if (print->total->count)
214 {
215 u = print->total->value / (Cxinteger_t)print->total->count;
216 if ((u = print->total->square + u * (u - 2 * print->total->value)) < 0)
217 u = -u;
218 if (print->total->count > 1)
219 u /= (Cxinteger_t)(print->total->count - 1);
220 r->value.number = sqrt(u);
221 }
222 else
223 r->value.number = 0;
224 break;
225 case STATS_FIELD:
226 if (!print->field || !(r->value.string.data = (char*)print->field->variable->name))
227 r->value.string.data = "-";
228 r->value.string.size = strlen(r->value.string.data);
229 break;
230 case STATS_GROUP:
231 sfstrseek(cx->buf, SEEK_SET, 0);
232 if (key = print->key)
233 for (sep = 0, group = print->group; group; group = group->next, key++)
234 {
235 if (sep)
236 sfputc(cx->buf, sep);
237 else
238 sep = variable->format.delimiter;
239 if (group->string)
240 s = key->value.string.data;
241 else
242 {
243 arg = *key;
244 if (cxcast(cx, &arg, 0, cx->state->type_string, NiL, group->variable->format.details))
245 return -1;
246 s = arg.value.string.data;
247 }
248 sfprintf(cx->buf, "%s", s);
249 }
250 else if (print->label)
251 sfprintf(cx->buf, "%s", print->label);
252 r->value.string.size = sfstrtell(cx->buf);
253 r->value.string.data = sfstruse(cx->buf);
254 break;
255 case STATS_MAX:
256 r->value.number = print->total->max;
257 break;
258 case STATS_MIN:
259 r->value.number = print->total->min;
260 break;
261 case STATS_SUM:
262 r->value.number = print->total->value;
263 break;
264 }
265 else if (key = print->key)
266 {
267 for (group = print->group; group->variable != variable; group = group->next, key++);
268 r->value = key->value;
269 }
270 else
271 memset(&r->value, 0, sizeof(r->value));
272 return 0;
273 }
274
275 static int
bucketcmp(Dt_t * dt,void * a,void * b,Dtdisc_t * disc)276 bucketcmp(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
277 {
278 register State_t* state = (State_t*)disc;
279 register Cxoperand_t* ka = (Cxoperand_t*)a;
280 register Cxoperand_t* kb = (Cxoperand_t*)b;
281 register Group_t* group;
282 register int n;
283
284 for (group = state->group; group; group = group->next, ka++, kb++)
285 {
286 if (group->string)
287 {
288 n = ka->value.string.size < kb->value.string.size ? ka->value.string.size : kb->value.string.size;
289 if (n = memcmp(ka->value.string.data, kb->value.string.data, n))
290 return n;
291 if (ka->value.string.size < kb->value.string.size)
292 return -1;
293 if (ka->value.string.size > kb->value.string.size)
294 return 1;
295 }
296 else if (ka->value.number < kb->value.number)
297 return -1;
298 else if (ka->value.number > kb->value.number)
299 return 1;
300 }
301 return 0;
302 }
303
304 static int
stats_beg(Cx_t * cx,Cxexpr_t * expr,void * data,Cxdisc_t * disc)305 stats_beg(Cx_t* cx, Cxexpr_t* expr, void* data, Cxdisc_t* disc)
306 {
307 char** argv = (char**)data;
308 int errors = error_info.errors;
309 int all;
310 int i;
311 char* s;
312 State_t* state;
313 Cxvariable_t* variable;
314 Field_t* field;
315 Field_t* lastfield;
316 Group_t* group;
317 Group_t* lastgroup;
318 Vmalloc_t* vm;
319 Dssrecord_t record;
320 Sfio_t* tmp;
321
322 if (!(vm = vmopen(Vmdcheap, Vmlast, 0)) || !(state = vmnewof(vm, 0, State_t, 1, 0)))
323 {
324 if (vm)
325 vmclose(vm);
326 if (disc->errorf)
327 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
328 return -1;
329 }
330 state->vm = vm;
331 state->maxgroups = 100;
332 if (!(state->print = cxopen(0, 0, disc)))
333 {
334 if (disc->errorf)
335 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
336 goto bad;
337 }
338 if (!cxscope(state->print, cx, 0, 0, disc))
339 {
340 cxclose(state->print);
341 goto bad;
342 }
343 for (i = 0; i < elementsof(variables); i++)
344 {
345 if (cxaddvariable(state->print, &variables[i], disc))
346 goto bad;
347 variables[i].data = &variables[0];
348 }
349 sfprintf(cx->buf, "%s%s", strchr(dss_lib_stats.description, '['), stats_usage);
350 s = sfstruse(cx->buf);
351 for (;;)
352 {
353 switch (optget(argv, s))
354 {
355 case 'a':
356 state->op |= STATS_AVERAGE;
357 continue;
358 case 'c':
359 state->op |= STATS_COUNT;
360 continue;
361 case 'd':
362 state->op |= STATS_DEVIATION;
363 continue;
364 case 'g':
365 if (!(variable = cxvariable(cx, opt_info.arg, NiL, disc)))
366 goto bad;
367 if (!(group = vmnewof(vm, 0, Group_t, 1, 0)))
368 {
369 if (disc->errorf)
370 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
371 goto bad;
372 }
373 group->variable = variable;
374 group->string = cxisstring(variable->type) || cxisbuffer(variable->type);
375 if (state->group)
376 lastgroup = lastgroup->next = group;
377 else
378 lastgroup = state->group = group;
379 state->groups++;
380 continue;
381 case 'l':
382 if (!(state->label = vmstrdup(vm, opt_info.arg)))
383 {
384 if (disc->errorf)
385 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
386 goto bad;
387 }
388 continue;
389 case 'm':
390 state->maxgroups = opt_info.num;
391 continue;
392 case 'p':
393 if (!(state->format = vmstrdup(vm, opt_info.arg)))
394 {
395 if (disc->errorf)
396 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
397 goto bad;
398 }
399 continue;
400 case 'r':
401 state->op |= STATS_RANGE;
402 continue;
403 case 's':
404 state->op |= STATS_SUM;
405 continue;
406 case '?':
407 if (disc->errorf)
408 {
409 (*disc->errorf)(cx, disc, ERROR_USAGE|4, "%s", opt_info.arg);
410 }
411 else
412 goto bad;
413 continue;
414 case ':':
415 if (disc->errorf)
416 (*disc->errorf)(cx, disc, 2, "%s", opt_info.arg);
417 else
418 goto bad;
419 continue;
420 }
421 break;
422 }
423 if (error_info.errors > errors)
424 goto bad;
425 argv += opt_info.index;
426 if (!state->op)
427 state->op = ~0;
428 if (all = !*argv)
429 variable = 0;
430 state->fw = 5;
431 do
432 {
433 if (all)
434 {
435 do
436 {
437 variable = (Cxvariable_t*)(variable ? dtnext(cx->fields, variable) : dtfirst(cx->fields));
438 } while (variable && (variable->data == (void*)&variables[0] || !cxisnumber(variable->type)));
439 if (!variable)
440 break;
441 }
442 else if (streq(*argv, "-"))
443 continue;
444 else if (!(variable = cxvariable(cx, *argv, NiL, disc)))
445 goto bad;
446 else if (!cxisnumber(variable->type))
447 {
448 if (disc->errorf)
449 (*disc->errorf)(cx, disc, 2, "%s: not a numeric field", variable->name);
450 goto bad;
451 }
452 if (!(field = vmnewof(vm, 0, Field_t, 1, 0)))
453 {
454 if (disc->errorf)
455 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
456 goto bad;
457 }
458 field->variable = variable;
459 if (state->field)
460 lastfield = lastfield->next = field;
461 else
462 lastfield = state->field = field;
463 state->fields++;
464 if (state->fw < (i = (int)strlen(variable->name)))
465 state->fw = i;
466 } while (all || *++argv);
467 if (!state->fields)
468 {
469 if (all)
470 {
471 if (disc->errorf)
472 (*disc->errorf)(cx, disc, 2, "no numeric fields");
473 goto bad;
474 }
475 state->fields = 1;
476 }
477 if (!(state->total = vmnewof(vm, 0, Total_t, state->fields, 0)))
478 {
479 if (disc->errorf)
480 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
481 goto bad;
482 }
483 if (state->group)
484 {
485 state->bucketdisc.comparf = bucketcmp;
486 state->bucketdisc.link = offsetof(Bucket_t, link);
487 state->bucketdisc.size = -1;
488 state->bucketdisc.key = offsetof(Bucket_t, key);
489 if (!(state->buckets = dtnew(vm, &state->bucketdisc, Dtoset)))
490 {
491 if (disc->errorf)
492 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
493 goto bad;
494 }
495 }
496 if (state->format)
497 {
498 if (!(tmp = sfstropen()))
499 {
500 if (disc->errorf)
501 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
502 goto bad;
503 }
504 state->print->getf = getop;
505 record.data = state;
506 i = dssprintf(DSS(cx), state->print, tmp, state->format, &record);
507 state->print->getf = getvalue;
508 sfclose(tmp);
509 if (i < 0)
510 goto bad;
511 }
512 expr->data = state;
513 return 0;
514 bad:
515 if (state->print)
516 cxscope(state->print, NiL, 0, 0, disc);
517 vmclose(vm);
518 return -1;
519 }
520
521 static int
stats_act(Cx_t * cx,Cxexpr_t * expr,void * data,Cxdisc_t * disc)522 stats_act(Cx_t* cx, Cxexpr_t* expr, void* data, Cxdisc_t* disc)
523 {
524 register State_t* state = (State_t*)expr->data;
525 register Cxoperand_t* key;
526 register Field_t* field;
527 register Group_t* group;
528 register Total_t* total;
529 Bucket_t* bucket;
530 Cxoperand_t val;
531 char* s;
532 int range;
533 int square;
534
535 total = state->total;
536 if (state->group)
537 {
538 if (!state->key && !(state->key = vmnewof(state->vm, 0, Cxoperand_t, state->groups, 0)))
539 {
540 if (disc->errorf)
541 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
542 return -1;
543 }
544 for (group = state->group, key = state->key; group; group = group->next, key++)
545 if (cxcast(cx, key, group->variable, group->variable->type, data, NiL))
546 return -1;
547 if (bucket = (Bucket_t*)dtmatch(state->buckets, state->key))
548 total = bucket->total;
549 else if (dtsize(state->buckets) < state->maxgroups)
550 {
551 if (!(bucket = vmnewof(state->vm, 0, Bucket_t, 1, (state->fields - 1) * sizeof(Total_t))))
552 {
553 if (disc->errorf)
554 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
555 return -1;
556 }
557 for (group = state->group, key = state->key; group; group = group->next, key++)
558 if (group->string)
559 {
560 s = key->value.string.data;
561 if (!(key->value.string.data = vmnewof(state->vm, 0, char, key->value.string.size, 1)))
562 {
563 if (disc->errorf)
564 (*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
565 return -1;
566 }
567 memcpy(key->value.string.data, s, key->value.string.size);
568 }
569 bucket->key = state->key;
570 state->key = 0;
571 dtinsert(state->buckets, bucket);
572 total = bucket->total;
573 }
574 }
575 if (!state->field)
576 total->count++;
577 else
578 {
579 range = !!(state->op & STATS_RANGE);
580 square = !!(state->op & STATS_DEVIATION);
581 for (field = state->field; field; field = field->next)
582 {
583 if (cxcast(cx, &val, field->variable, cx->state->type_number, data, NiL))
584 return -1;
585 total->count++;
586 total->value += val.value.number;
587 if (range)
588 {
589 if (total->min > val.value.number || total->count == 1)
590 total->min = val.value.number;
591 if (total->max < val.value.number || total->count == 1)
592 total->max = val.value.number;
593 }
594 if (square)
595 total->square += val.value.number * val.value.number;
596 total++;
597 }
598 }
599 return 0;
600 }
601
602 static void
number(Sfio_t * op,Cxnumber_t n,int fw)603 number(Sfio_t* op, Cxnumber_t n, int fw)
604 {
605 if (n == 0 || ((n >= 0) ? n : -n) >= 1 && n >= FLTMAX_INTMAX_MIN && n <= FLTMAX_UINTMAX_MAX && n == (Cxinteger_t)n)
606 sfprintf(op, " %*I*u", fw, sizeof(Cxinteger_t), (Cxinteger_t)n);
607 else
608 {
609 if (n >= 0)
610 sfputc(op, ' ');
611 sfprintf(op, " %1.*I*e", fw - 7, sizeof(n), n);
612 }
613 }
614
615 static int
list(Cx_t * cx,register State_t * state,Sfio_t * op,const char * label,register Field_t * field,register Total_t * total,Cxoperand_t * key)616 list(Cx_t* cx, register State_t* state, Sfio_t* op, const char* label, register Field_t* field, register Total_t* total, Cxoperand_t* key)
617 {
618 Dssrecord_t record;
619 Print_t pr;
620 Cxnumber_t u;
621 Cxoperand_t arg;
622 Group_t* group;
623 char* s;
624
625 do
626 {
627 if (state->format)
628 {
629 record.data = ≺
630 pr.field = field;
631 pr.group = state->group;
632 pr.label = label;
633 pr.total = total;
634 pr.key = key;
635 if (dssprintf(DSS(cx), state->print, op, state->format, &record) < 0)
636 return -1;
637 }
638 else
639 {
640 if (field)
641 sfprintf(op, "%*s", state->fw, field->variable->name);
642 if (state->op & STATS_COUNT)
643 number(op, (Cxinteger_t)total->count, FW);
644 u = total->value / (Cxinteger_t)total->count;
645 if (state->op & STATS_SUM)
646 number(op, total->value, FW);
647 if (state->op & STATS_AVERAGE)
648 number(op, u, FW);
649 if (state->op & STATS_DEVIATION)
650 {
651 if ((u = total->square + u * (u - 2 * total->value)) < 0)
652 u = -u;
653 if (total->count > 1)
654 u /= (Cxinteger_t)(total->count - 1);
655 number(op, (Cxnumber_t)sqrt(u), FW);
656 }
657 if (state->op & STATS_RANGE)
658 {
659 number(op, total->min, FW);
660 number(op, total->max, FW);
661 }
662 if (label)
663 {
664 sfprintf(op, " %s", label);
665 label = 0;
666 }
667 else if (key)
668 {
669 for (group = state->group; group; group = group->next, key++)
670 {
671 sfputc(op, ' ');
672 if (group->string)
673 s = key->value.string.data;
674 else
675 {
676 arg = *key;
677 if (cxcast(cx, &arg, 0, cx->state->type_string, NiL, group->variable->format.details))
678 return -1;
679 s = arg.value.string.data;
680 }
681 sfprintf(op, "%*s", group->width, s);
682 }
683 key = 0;
684 }
685 sfprintf(op, "\n");
686 }
687 total++;
688 } while (field && (field = field->next));
689 return 0;
690 }
691
692 static int
stats_end(Cx_t * cx,Cxexpr_t * expr,void * data,Cxdisc_t * disc)693 stats_end(Cx_t* cx, Cxexpr_t* expr, void* data, Cxdisc_t* disc)
694 {
695 register State_t* state = (State_t*)expr->data;
696 register Bucket_t* bucket;
697 register Group_t* group;
698
699 if (state->label)
700 sfprintf(expr->op, "%s\n", state->label);
701 if (!state->format)
702 {
703 if (state->field)
704 sfprintf(expr->op, "%*s", state->fw, "FIELD");
705 if (state->op & STATS_COUNT)
706 sfprintf(expr->op, " %*s", FW, "COUNT");
707 if (state->op & STATS_SUM)
708 sfprintf(expr->op, " %*s", FW, "SUM");
709 if (state->op & STATS_AVERAGE)
710 sfprintf(expr->op, " %*s", FW, "AVERAGE");
711 if (state->op & STATS_DEVIATION)
712 sfprintf(expr->op, " %*s", FW, "DEVIATION");
713 if (state->op & STATS_RANGE)
714 sfprintf(expr->op, " %*s %*s", FW, "MIN", FW, "MAX");
715 if (state->buckets)
716 for (group = state->group; group; group = group->next)
717 {
718 group->width = group->variable->format.print ? group->variable->format.print : group->variable->type->format.print ? group->variable->type->format.print : FW;
719 sfprintf(expr->op, " %*s", group->width, group->variable->name);
720 }
721 sfprintf(expr->op, "\n");
722 }
723 if (state->buckets)
724 {
725 for (bucket = (Bucket_t*)dtfirst(state->buckets); bucket; bucket = (Bucket_t*)dtnext(state->buckets, bucket))
726 if (list(cx, state, expr->op, NiL, state->field, bucket->total, bucket->key))
727 goto bad;
728 if (state->total->count && list(cx, state, expr->op, "OVERFLOW", state->field, state->total, NiL))
729 goto bad;
730 }
731 else if (list(cx, state, expr->op, NiL, state->field, state->total, NiL))
732 goto bad;
733 vmclose(state->vm);
734 return 0;
735 bad:
736 vmclose(state->vm);
737 return -1;
738 }
739
740 static Cxquery_t queries[] =
741 {
742 {
743 "stats",
744 "collect numeric field value statistics",
745 CXH,
746 stats_beg,
747 0,
748 stats_act,
749 stats_end
750 },
751 {0}
752 };
753
754 Dsslib_t dss_lib_stats =
755 {
756 "stats",
757 "stats query"
758 "[-1lms5P?\n@(#)$Id: dss stats query (AT&T Research) 2011-09-11 $\n]"
759 USAGE_LICENSE,
760 CXH,
761 0,
762 0,
763 0,
764 0,
765 0,
766 0,
767 &queries[0]
768 };
769