Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 2011 James K. Lowden
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Library General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Library General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Library General Public
15 : * License along with this library; if not, write to the
16 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 : * Boston, MA 02111-1307, USA.
18 : */
19 :
20 : #include <config.h>
21 :
22 : #include <stdarg.h>
23 :
24 : #include <freetds/time.h>
25 :
26 : #include <assert.h>
27 : #include <stdio.h>
28 :
29 : #if HAVE_STDLIB_H
30 : #include <stdlib.h>
31 : #endif /* HAVE_STDLIB_H */
32 :
33 : #if HAVE_STRING_H
34 : #include <string.h>
35 : #endif /* HAVE_STRING_H */
36 :
37 : #if HAVE_UNISTD_H
38 : #include <unistd.h>
39 : #endif /* HAVE_UNISTD_H */
40 :
41 : #if HAVE_ERRNO_H
42 : # include <errno.h>
43 : #endif /* HAVE_ERRNO_H */
44 :
45 :
46 : #include <freetds/tds.h>
47 : #include <freetds/thread.h>
48 : #include <freetds/convert.h>
49 : #include <freetds/utils/string.h>
50 : #include <freetds/replacements.h>
51 : #include <sybfront.h>
52 : #include <sybdb.h>
53 : #include <syberror.h>
54 : #include <dblib.h>
55 :
56 : #define TDS_FIND(k,b,c) tds_find(k, b, TDS_VECTOR_SIZE(b), sizeof(b[0]), c)
57 :
58 : typedef bool (*compare_func)(const void *, const void *);
59 :
60 : static void *
61 : tds_find(const void *key, const void *base, size_t nelem, size_t width,
62 : compare_func compar)
63 : {
64 : size_t n;
65 175566 : char *p = (char *) base;
66 :
67 175566 : for (n = nelem; n != 0; --n) {
68 0 : if (compar(key, p))
69 : return p;
70 0 : p += width;
71 : }
72 : return NULL;
73 : }
74 :
75 :
76 : struct col_t
77 : {
78 : size_t len;
79 : TDS_SERVER_TYPE type;
80 : int null_indicator;
81 : char *s;
82 : union {
83 : DBTINYINT ti;
84 : DBSMALLINT si;
85 : DBINT i;
86 : DBREAL r;
87 : DBFLT8 f;
88 : } data;
89 : };
90 :
91 : static TDS_SERVER_TYPE infer_col_type(int sybtype);
92 :
93 : static struct col_t *
94 0 : col_init(struct col_t *pcol, int sybtype, int collen)
95 : {
96 0 : assert(pcol);
97 :
98 0 : pcol->type = infer_col_type(sybtype);
99 0 : if (pcol->type == TDS_INVALID_TYPE)
100 : return NULL;
101 0 : pcol->len = collen;
102 :
103 0 : switch (sybtype) {
104 0 : case 0:
105 0 : pcol->len = 0;
106 0 : return NULL;
107 0 : case SYBDATETIME:
108 : case SYBDATETIME4:
109 : case SYBDATETIMN:
110 0 : collen = 30;
111 : /* fall through */
112 0 : case SYBCHAR:
113 : case SYBVARCHAR:
114 : case SYBTEXT:
115 : case SYBNTEXT:
116 0 : pcol->len = collen;
117 0 : if ((pcol->s = tds_new(char, 1+collen)) == NULL) {
118 : return NULL;
119 : }
120 : break;
121 : }
122 : return pcol;
123 : }
124 :
125 : static void
126 0 : col_free(struct col_t *p)
127 : {
128 0 : free(p->s);
129 0 : memset(p, 0, sizeof(*p));
130 0 : }
131 :
132 : static bool
133 0 : col_equal(const struct col_t *pc1, const struct col_t *pc2)
134 : {
135 0 : assert( pc1 && pc2 );
136 0 : assert( pc1->type == pc2->type );
137 :
138 0 : switch (pc1->type) {
139 :
140 0 : case SYBCHAR:
141 : case SYBVARCHAR:
142 0 : if( pc1->len != pc2->len)
143 : return false;
144 0 : return strncmp(pc1->s, pc2->s, pc1->len) == 0;
145 0 : case SYBINT1:
146 0 : return pc1->data.ti == pc2->data.ti;
147 0 : case SYBINT2:
148 0 : return pc1->data.si == pc2->data.si;
149 0 : case SYBINT4:
150 0 : return pc1->data.i == pc2->data.i;
151 0 : case SYBFLT8:
152 0 : return pc1->data.f == pc2->data.f;
153 0 : case SYBREAL:
154 0 : return pc1->data.r == pc2->data.r;
155 :
156 : case SYBINTN:
157 : case SYBDATETIME:
158 : case SYBBIT:
159 : case SYBTEXT:
160 : case SYBNTEXT:
161 : case SYBIMAGE:
162 : case SYBMONEY4:
163 : case SYBMONEY:
164 : case SYBDATETIME4:
165 : case SYBBINARY:
166 : case SYBVOID:
167 : case SYBVARBINARY:
168 : case SYBBITN:
169 : case SYBNUMERIC:
170 : case SYBDECIMAL:
171 : case SYBFLTN:
172 : case SYBMONEYN:
173 : case SYBDATETIMN:
174 : case SYBMSTABLE:
175 0 : assert( false && pc1->type );
176 : break;
177 : }
178 : return false;
179 : }
180 :
181 : static void *
182 0 : col_buffer(struct col_t *pcol)
183 : {
184 0 : switch (pcol->type) {
185 :
186 0 : case SYBCHAR:
187 : case SYBVARCHAR:
188 0 : return pcol->s;
189 0 : case SYBINT1:
190 0 : return &pcol->data.ti;
191 0 : case SYBINT2:
192 0 : return &pcol->data.si;
193 0 : case SYBINT4:
194 0 : return &pcol->data.i;
195 0 : case SYBFLT8:
196 0 : return &pcol->data.f;
197 0 : case SYBREAL:
198 0 : return &pcol->data.r;
199 :
200 : case SYBINTN:
201 : case SYBDATETIME:
202 : case SYBBIT:
203 : case SYBTEXT:
204 : case SYBNTEXT:
205 : case SYBIMAGE:
206 : case SYBMONEY4:
207 : case SYBMONEY:
208 : case SYBDATETIME4:
209 : case SYBBINARY:
210 : case SYBVOID:
211 : case SYBVARBINARY:
212 : case SYBBITN:
213 : case SYBNUMERIC:
214 : case SYBDECIMAL:
215 : case SYBFLTN:
216 : case SYBMONEYN:
217 : case SYBDATETIMN:
218 : case SYBMSTABLE:
219 0 : assert( false && pcol->type );
220 : break;
221 : }
222 : return NULL;
223 :
224 : }
225 :
226 : #if 0
227 : static int
228 : col_print(FILE* out, const struct col_t *pcol)
229 : {
230 : char *fmt;
231 :
232 : switch (pcol->type) {
233 :
234 : case SYBCHAR:
235 : case SYBVARCHAR:
236 : return (int) fwrite(pcol->s, pcol->len, 1, out);
237 : case SYBINT1:
238 : return fprintf(out, "%d", (int)pcol->ti);
239 : case SYBINT2:
240 : return fprintf(out, "%d", (int)pcol->si);
241 : case SYBINT4:
242 : return fprintf(out, "%d", (int)pcol->i);
243 : case SYBFLT8:
244 : return fprintf(out, "%f", pcol->f);
245 : case SYBREAL:
246 : return fprintf(out, "%f", (double)pcol->r);
247 :
248 : case SYBINTN:
249 : case SYBDATETIME:
250 : case SYBBIT:
251 : case SYBTEXT:
252 : case SYBNTEXT:
253 : case SYBIMAGE:
254 : case SYBMONEY4:
255 : case SYBMONEY:
256 : case SYBDATETIME4:
257 : case SYBBINARY:
258 : case SYBVOID:
259 : case SYBVARBINARY:
260 : case SYBBITN:
261 : case SYBNUMERIC:
262 : case SYBDECIMAL:
263 : case SYBFLTN:
264 : case SYBMONEYN:
265 : case SYBDATETIMN:
266 : assert( false && pcol->type );
267 : break;
268 : }
269 : return false;
270 : }
271 : #endif
272 : static struct col_t *
273 0 : col_cpy(struct col_t *pdest, const struct col_t *psrc)
274 : {
275 0 : assert( pdest && psrc );
276 0 : assert( psrc->len > 0 || psrc->null_indicator == -1);
277 :
278 0 : memcpy(pdest, psrc, sizeof(*pdest));
279 :
280 0 : if (psrc->s) {
281 : assert(psrc->len >= 0);
282 0 : if ((pdest->s = tds_new(char, psrc->len)) == NULL)
283 : return NULL;
284 0 : memcpy(pdest->s, psrc->s, psrc->len);
285 : }
286 :
287 0 : assert( pdest->len > 0 || pdest->null_indicator == -1);
288 : return pdest;
289 : }
290 :
291 : static bool
292 : col_null( const struct col_t *pcol )
293 : {
294 : assert(pcol);
295 0 : return pcol->null_indicator == -1;
296 : }
297 :
298 : static char *
299 0 : string_value(const struct col_t *pcol)
300 : {
301 0 : char *output = NULL;
302 0 : int len = -1;
303 :
304 0 : switch (pcol->type) {
305 0 : case SYBCHAR:
306 : case SYBVARCHAR:
307 0 : if ((output = tds_new0(char, 1 + pcol->len)) == NULL)
308 : return NULL;
309 0 : strncpy(output, pcol->s, pcol->len);
310 0 : return output;
311 : break;
312 0 : case SYBINT1:
313 0 : len = asprintf(&output, "%d", (int)pcol->data.ti);
314 0 : break;
315 0 : case SYBINT2:
316 0 : len = asprintf(&output, "%d", (int)pcol->data.si);
317 0 : break;
318 0 : case SYBINT4:
319 0 : len = asprintf(&output, "%d", (int)pcol->data.i);
320 0 : break;
321 0 : case SYBFLT8:
322 0 : len = asprintf(&output, "%f", pcol->data.f);
323 0 : break;
324 0 : case SYBREAL:
325 0 : len = asprintf(&output, "%f", (double)pcol->data.r);
326 0 : break;
327 :
328 : default:
329 : case SYBINTN:
330 : case SYBDATETIME:
331 : case SYBBIT:
332 : case SYBTEXT:
333 : case SYBNTEXT:
334 : case SYBIMAGE:
335 : case SYBMONEY4:
336 : case SYBMONEY:
337 : case SYBDATETIME4:
338 : case SYBBINARY:
339 : case SYBVOID:
340 : case SYBVARBINARY:
341 : case SYBBITN:
342 : case SYBNUMERIC:
343 : case SYBDECIMAL:
344 : case SYBFLTN:
345 : case SYBMONEYN:
346 : case SYBDATETIMN:
347 0 : assert( false && pcol->type );
348 : return NULL;
349 : break;
350 : }
351 :
352 0 : return len >= 0? output : NULL;
353 : }
354 :
355 : static char *
356 0 : join(int argc, char *argv[], const char sep[])
357 : {
358 0 : size_t len = 0;
359 : char **p, *output;
360 :
361 0 : for (p=argv; p < argv + argc; p++) {
362 0 : len += strlen(*p);
363 : }
364 :
365 0 : len += 1 + argc * strlen(sep); /* allows one too many */
366 :
367 0 : output = tds_new0(char, len);
368 0 : if (!output)
369 : return NULL;
370 :
371 0 : for (p=argv; p < argv + argc; p++) {
372 0 : if (p != argv)
373 0 : strcat(output, sep);
374 0 : strcat(output, *p);
375 : }
376 : return output;
377 : }
378 :
379 : static TDS_SERVER_TYPE
380 0 : infer_col_type(int sybtype)
381 : {
382 0 : switch (sybtype) {
383 : case SYBCHAR:
384 : case SYBVARCHAR:
385 : case SYBTEXT:
386 : case SYBNTEXT:
387 : return SYBCHAR;
388 : case SYBDATETIME:
389 : case SYBDATETIME4:
390 : case SYBDATETIMN:
391 : return SYBCHAR;
392 0 : case SYBINT1:
393 : case SYBBIT:
394 : case SYBBITN:
395 0 : return SYBINT1;
396 0 : case SYBINT2:
397 0 : return SYBINT2;
398 0 : case SYBINT4:
399 : case SYBINTN:
400 0 : return SYBINT4;
401 0 : case SYBFLT8:
402 : case SYBMONEY4:
403 : case SYBMONEY:
404 : case SYBFLTN:
405 : case SYBMONEYN:
406 : case SYBNUMERIC:
407 : case SYBDECIMAL:
408 0 : return SYBFLT8;
409 0 : case SYBREAL:
410 0 : return SYBREAL;
411 :
412 : case SYBIMAGE:
413 : case SYBBINARY:
414 : case SYBVOID:
415 : case SYBVARBINARY:
416 0 : assert( false && sybtype );
417 : break;
418 : }
419 0 : return TDS_INVALID_TYPE;
420 : }
421 :
422 : static int
423 0 : bind_type(int sybtype)
424 : {
425 0 : switch (sybtype) {
426 : case SYBCHAR:
427 : case SYBVARCHAR:
428 : case SYBTEXT:
429 : case SYBNTEXT:
430 : case SYBDATETIME:
431 : case SYBDATETIME4:
432 : case SYBDATETIMN:
433 : return NTBSTRINGBIND;
434 0 : case SYBINT1:
435 : case SYBBIT:
436 : case SYBBITN:
437 0 : return TINYBIND;
438 0 : case SYBINT2:
439 0 : return SMALLBIND;
440 0 : case SYBINT4:
441 : case SYBINTN:
442 0 : return INTBIND;
443 0 : case SYBFLT8:
444 : case SYBMONEY4:
445 : case SYBMONEY:
446 : case SYBFLTN:
447 : case SYBMONEYN:
448 : case SYBNUMERIC:
449 : case SYBDECIMAL:
450 0 : return FLT8BIND;
451 0 : case SYBREAL:
452 0 : return REALBIND;
453 :
454 : case SYBIMAGE:
455 : case SYBBINARY:
456 : case SYBVOID:
457 : case SYBVARBINARY:
458 0 : assert( false && sybtype );
459 : break;
460 : }
461 0 : return 0;
462 : }
463 :
464 : typedef struct KEY_T
465 : {
466 : int nkeys;
467 : struct col_t *keys;
468 : } KEY_T;
469 :
470 : static bool
471 0 : key_equal(const KEY_T *a, const KEY_T *b)
472 : {
473 : int i;
474 :
475 0 : assert(a && b);
476 0 : assert(a->keys && b->keys);
477 0 : assert(a->nkeys == b->nkeys);
478 :
479 0 : for (i=0; i < a->nkeys; i++) {
480 0 : if (! col_equal(a->keys+i, b->keys+i))
481 : return false;
482 : }
483 : return true;
484 : }
485 :
486 :
487 : static void
488 0 : key_free(KEY_T *p)
489 : {
490 0 : col_free(p->keys);
491 0 : free(p->keys);
492 0 : memset(p, 0, sizeof(*p));
493 0 : }
494 :
495 : static KEY_T *
496 0 : key_cpy(KEY_T *pdest, const KEY_T *psrc)
497 : {
498 : int i;
499 :
500 0 : assert( pdest && psrc );
501 :
502 0 : if ((pdest->keys = tds_new0(struct col_t, psrc->nkeys)) == NULL)
503 : return NULL;
504 :
505 0 : pdest->nkeys = psrc->nkeys;
506 :
507 0 : for( i=0; i < psrc->nkeys; i++) {
508 0 : if (NULL == col_cpy(pdest->keys+i, psrc->keys+i))
509 : return NULL;
510 : }
511 :
512 : return pdest;
513 : }
514 :
515 :
516 : static char *
517 0 : make_col_name(DBPROCESS *dbproc, const KEY_T *k)
518 : {
519 : const struct col_t *pc;
520 : char **names, **s, *output;
521 :
522 0 : assert(k);
523 0 : assert(k->nkeys);
524 0 : assert(k->keys);
525 :
526 0 : s = names = tds_new0(char *, k->nkeys);
527 0 : if (!s) {
528 0 : dbperror(dbproc, SYBEMEM, errno);
529 0 : return NULL;
530 : }
531 0 : for(pc=k->keys; pc < k->keys + k->nkeys; pc++) {
532 0 : *s++ = strdup(string_value(pc));
533 : }
534 :
535 0 : output = join(k->nkeys, names, "/");
536 :
537 0 : for(s=names; s < names + k->nkeys; s++) {
538 0 : free(*s);
539 : }
540 0 : free(names);
541 :
542 0 : return output;
543 : }
544 :
545 :
546 : typedef struct agg_t
547 : {
548 : KEY_T row_key, col_key;
549 : struct col_t value;
550 : } AGG_T;
551 :
552 : #if 0
553 : static bool
554 : agg_key_equal(const void *a, const void *b)
555 : {
556 : int i;
557 : const AGG_T *p1 = a, *p2 = b;
558 :
559 : assert(p1 && p2);
560 : assert(p1->row_key.keys && p2->row_key.keys);
561 : assert(p1->row_key.nkeys == p2->row_key.nkeys);
562 :
563 : for( i=0; i < p1->row_key.nkeys; i++ ) {
564 : if (! col_equal(p1->row_key.keys+i, p2->row_key.keys+i))
565 : return false;
566 : }
567 :
568 : return true;
569 : }
570 : #endif
571 :
572 : static bool
573 0 : agg_next(const AGG_T *p1, const AGG_T *p2)
574 : {
575 : int i;
576 :
577 0 : assert(p1 && p2);
578 :
579 0 : if (p1->row_key.keys == NULL || p2->row_key.keys == NULL)
580 : return false;
581 :
582 : assert(p1->row_key.keys && p2->row_key.keys);
583 0 : assert(p1->row_key.nkeys == p2->row_key.nkeys);
584 :
585 0 : assert(p1->col_key.keys && p2->col_key.keys);
586 0 : assert(p1->col_key.nkeys == p2->col_key.nkeys);
587 :
588 0 : for( i=0; i < p1->row_key.nkeys; i++ ) {
589 0 : assert(p1->row_key.keys[i].type);
590 0 : assert(p2->row_key.keys[i].type);
591 0 : if (p1->row_key.keys[i].type != p2->row_key.keys[i].type)
592 : return false;
593 : }
594 :
595 0 : for( i=0; i < p1->row_key.nkeys; i++ ) {
596 0 : if (! col_equal(p1->row_key.keys+i, p2->row_key.keys+i))
597 : return false;
598 : }
599 :
600 0 : for( i=0; i < p1->col_key.nkeys; i++ ) {
601 0 : if (p1->col_key.keys[i].type != p2->col_key.keys[i].type)
602 : return false;
603 : }
604 :
605 0 : for( i=0; i < p1->col_key.nkeys; i++ ) {
606 0 : if (! col_equal(p1->col_key.keys+i, p2->col_key.keys+i))
607 : return false;
608 : }
609 :
610 : return true;
611 : }
612 :
613 : static void
614 0 : agg_free(AGG_T *p)
615 : {
616 0 : key_free(&p->row_key);
617 0 : key_free(&p->col_key);
618 0 : col_free(&p->value);
619 0 : }
620 :
621 : static bool
622 0 : agg_equal(const AGG_T *p1, const AGG_T *p2)
623 : {
624 : int i;
625 :
626 0 : assert(p1 && p2);
627 0 : assert(p1->row_key.keys && p1->col_key.keys);
628 0 : assert(p2->row_key.keys && p2->col_key.keys);
629 :
630 0 : assert(p1->row_key.nkeys == p2->row_key.nkeys);
631 0 : assert(p1->col_key.nkeys == p2->col_key.nkeys);
632 :
633 : /* todo: use key_equal */
634 0 : for( i=0; i < p1->row_key.nkeys; i++ ) {
635 0 : if (! col_equal(p1->row_key.keys+i, p2->row_key.keys+i))
636 : return false;
637 : }
638 0 : for( i=0; i < p1->col_key.nkeys; i++ ) {
639 0 : if (! col_equal(p1->col_key.keys+i, p2->col_key.keys+i))
640 : return false;
641 : }
642 : return true;
643 : }
644 :
645 : #undef TEST_MALLOC
646 : #define TEST_MALLOC(dest,type) \
647 : {if (!(dest = (type*)calloc(1, sizeof(type)))) goto Cleanup;}
648 :
649 : #undef TEST_CALLOC
650 : #define TEST_CALLOC(dest,type,n) \
651 : {if (!(dest = (type*)calloc((n), sizeof(type)))) goto Cleanup;}
652 :
653 : #define tds_alloc_column() ((TDSCOLUMN*) calloc(1, sizeof(TDSCOLUMN)))
654 :
655 : static TDSRESULTINFO *
656 0 : alloc_results(size_t num_cols)
657 : {
658 : TDSRESULTINFO *res_info;
659 : TDSCOLUMN **ppcol;
660 :
661 0 : TEST_MALLOC(res_info, TDSRESULTINFO);
662 0 : res_info->ref_count = 1;
663 0 : TEST_CALLOC(res_info->columns, TDSCOLUMN *, num_cols);
664 :
665 0 : for (ppcol = res_info->columns; ppcol < res_info->columns + num_cols; ppcol++)
666 0 : if ((*ppcol = tds_alloc_column()) == NULL)
667 : goto Cleanup;
668 0 : res_info->num_cols = num_cols;
669 0 : res_info->row_size = 0;
670 0 : return res_info;
671 :
672 0 : Cleanup:
673 0 : tds_free_results(res_info);
674 0 : return NULL;
675 : }
676 :
677 : static TDSRET
678 0 : set_result_column(TDSSOCKET * tds, TDSCOLUMN * curcol, const char name[], const struct col_t *pvalue)
679 : {
680 0 : assert(curcol && pvalue);
681 0 : assert(name);
682 :
683 0 : curcol->column_usertype = pvalue->type;
684 0 : curcol->column_nullable = true;
685 0 : curcol->column_writeable = false;
686 0 : curcol->column_identity = false;
687 :
688 0 : tds_set_column_type(tds->conn, curcol, pvalue->type); /* sets "cardinal" type */
689 :
690 0 : curcol->column_timestamp = (curcol->column_type == SYBBINARY && curcol->column_usertype == TDS_UT_TIMESTAMP);
691 :
692 : #if 0
693 : curcol->funcs->get_info(tds, curcol);
694 : #endif
695 0 : curcol->on_server.column_size = curcol->column_size;
696 :
697 0 : if (!tds_dstr_copy(&curcol->column_name, name))
698 : return TDS_FAIL;
699 :
700 0 : tdsdump_log(TDS_DBG_INFO1, "tds7_get_data_info: \n"
701 : "\tcolname = %s\n"
702 : "\ttype = %d (%s)\n"
703 : "\tserver's type = %d (%s)\n"
704 : "\tcolumn_varint_size = %d\n"
705 : "\tcolumn_size = %d (%d on server)\n",
706 0 : tds_dstr_cstr(&curcol->column_name),
707 0 : curcol->column_type, tds_prtype(curcol->column_type),
708 0 : curcol->on_server.column_type, tds_prtype(curcol->on_server.column_type),
709 0 : curcol->column_varint_size,
710 : curcol->column_size, curcol->on_server.column_size);
711 :
712 : return TDS_SUCCESS;
713 : }
714 :
715 : struct metadata_t { KEY_T *pacross; char *name; struct col_t col; };
716 :
717 :
718 : static bool
719 0 : reinit_results(TDSSOCKET * tds, size_t num_cols, const struct metadata_t meta[])
720 : {
721 : TDSRESULTINFO *info;
722 : int i;
723 :
724 0 : assert(tds);
725 0 : assert(num_cols);
726 0 : assert(meta);
727 :
728 0 : tds_free_all_results(tds);
729 0 : tds->rows_affected = TDS_NO_COUNT;
730 :
731 0 : if ((info = alloc_results(num_cols)) == NULL)
732 : return false;
733 :
734 0 : tds_set_current_results(tds, info);
735 0 : if (tds->cur_cursor) {
736 0 : tds_free_results(tds->cur_cursor->res_info);
737 0 : tds->cur_cursor->res_info = info;
738 0 : tdsdump_log(TDS_DBG_INFO1, "set current_results to cursor->res_info\n");
739 : } else {
740 0 : tds->res_info = info;
741 0 : tdsdump_log(TDS_DBG_INFO1, "set current_results (%u column%s) to tds->res_info\n", (unsigned) num_cols, (num_cols==1? "":"s"));
742 : }
743 :
744 0 : tdsdump_log(TDS_DBG_INFO1, "setting up %u columns\n", (unsigned) num_cols);
745 :
746 0 : for (i = 0; i < num_cols; i++) {
747 0 : set_result_column(tds, info->columns[i], meta[i].name, &meta[i].col);
748 0 : info->columns[i]->bcp_terminator = (char*) meta[i].pacross; /* overload available pointer */
749 : }
750 :
751 : if (num_cols > 0) {
752 : static const char dashes[31] = "------------------------------";
753 0 : tdsdump_log(TDS_DBG_INFO1, " %-20s %-15s %-15s %-7s\n", "name", "size/wsize", "type/wtype", "utype");
754 0 : tdsdump_log(TDS_DBG_INFO1, " %-20s %15s %15s %7s\n", dashes+10, dashes+30-15, dashes+30-15, dashes+30-7);
755 : }
756 0 : for (i = 0; i < num_cols; i++) {
757 0 : TDSCOLUMN *curcol = info->columns[i];
758 :
759 0 : tdsdump_log(TDS_DBG_INFO1, " %-20s %7d/%-7d %7d/%-7d %7d\n",
760 0 : tds_dstr_cstr(&curcol->column_name),
761 : curcol->column_size, curcol->on_server.column_size,
762 0 : curcol->column_type, curcol->on_server.column_type,
763 : curcol->column_usertype);
764 : }
765 :
766 : #if 1
767 : /* all done now allocate a row for tds_process_row to use */
768 0 : if (TDS_FAILED(tds_alloc_row(info))) return false;
769 : #endif
770 0 : return true;
771 : }
772 :
773 : typedef struct pivot_t
774 : {
775 : DBPROCESS *dbproc;
776 : STATUS status;
777 : DB_RESULT_STATE dbresults_state;
778 :
779 : AGG_T *output;
780 : KEY_T *across;
781 : size_t nout, nacross;
782 : } PIVOT_T;
783 :
784 : static bool
785 0 : pivot_key_equal(const PIVOT_T *a, const PIVOT_T *b)
786 : {
787 0 : assert(a && b);
788 :
789 0 : return a->dbproc == b->dbproc;
790 : }
791 :
792 : static PIVOT_T *pivots = NULL;
793 : static size_t npivots = 0;
794 :
795 : PIVOT_T *
796 175566 : dbrows_pivoted(DBPROCESS *dbproc)
797 : {
798 : PIVOT_T P;
799 :
800 175566 : assert(dbproc);
801 175566 : P.dbproc = dbproc;
802 :
803 351132 : return (PIVOT_T *) tds_find(&P, pivots, npivots, sizeof(*pivots), (compare_func) pivot_key_equal);
804 : }
805 :
806 : STATUS
807 0 : dbnextrow_pivoted(DBPROCESS *dbproc, PIVOT_T *pp)
808 : {
809 : int i;
810 : AGG_T candidate, *pout;
811 :
812 0 : assert(pp);
813 0 : assert(dbproc && dbproc->tds_socket);
814 0 : assert(dbproc->tds_socket->res_info);
815 0 : assert(dbproc->tds_socket->res_info->columns || 0 == dbproc->tds_socket->res_info->num_cols);
816 :
817 0 : for (pout = pp->output; pout < pp->output + pp->nout; pout++) {
818 0 : if (pout->row_key.keys != NULL)
819 : break;
820 : }
821 :
822 0 : if (pout == pp->output + pp->nout) {
823 0 : dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
824 0 : return NO_MORE_ROWS;
825 : }
826 :
827 0 : memset(&candidate, 0, sizeof(candidate));
828 0 : key_cpy(&candidate.row_key, &pout->row_key);
829 :
830 : /* "buffer_transfer_bound_data" */
831 0 : for (i = 0; i < dbproc->tds_socket->res_info->num_cols; i++) {
832 0 : struct col_t *pval = NULL;
833 0 : TDSCOLUMN *pcol = dbproc->tds_socket->res_info->columns[i];
834 0 : assert(pcol);
835 :
836 0 : if (pcol->column_nullbind) {
837 0 : if (pcol->column_cur_size < 0) {
838 0 : *(DBINT *)(pcol->column_nullbind) = -1;
839 : } else {
840 0 : *(DBINT *)(pcol->column_nullbind) = 0;
841 : }
842 : }
843 0 : if (!pcol->column_varaddr) {
844 0 : fprintf(stderr, "no pcol->column_varaddr in col %d\n", i);
845 0 : continue;
846 : }
847 :
848 : /* find column in output */
849 0 : if (pcol->bcp_terminator == NULL) { /* not a cross-tab column */
850 0 : pval = &candidate.row_key.keys[i];
851 : } else {
852 : AGG_T *pcan;
853 0 : key_cpy(&candidate.col_key, (KEY_T *) pcol->bcp_terminator);
854 0 : if ((pcan = tds_find(&candidate, pout, pp->output + pp->nout - pout,
855 : sizeof(*pp->output), (compare_func) agg_next)) != NULL) {
856 : /* flag this output as used */
857 0 : pout->row_key.keys = NULL;
858 0 : pval = &pcan->value;
859 : }
860 : }
861 :
862 0 : if (!pval || col_null(pval)) { /* nothing in output for this x,y location */
863 0 : dbgetnull(dbproc, pcol->column_bindtype, pcol->column_bindlen, (BYTE *) pcol->column_varaddr);
864 0 : continue;
865 : }
866 :
867 : assert(pval);
868 :
869 : #if 0
870 : printf("\ncopying col %d, type %d/%d, len %d to %p ", i, pval->type, pcol->column_type, pval->len, pcol->column_varaddr);
871 : switch (pval->type) {
872 : case 48:
873 : printf("value %d", (int)pval->ti); break;
874 : case 56:
875 : printf("value %d", (int)pval->si); break;
876 : }
877 : printf("\n");
878 : #endif
879 0 : pcol->column_size = pval->len;
880 0 : pcol->column_data = col_buffer(pval);
881 :
882 0 : copy_data_to_host_var( dbproc,
883 : pval->type,
884 0 : col_buffer(pval),
885 : pval->len,
886 0 : (BYTE *) pcol->column_varaddr,
887 0 : pcol->column_bindlen,
888 0 : pcol->column_bindtype,
889 0 : (DBINT*) pcol->column_nullbind
890 : );
891 : }
892 :
893 : return REG_ROW;
894 : }
895 :
896 : /**
897 : * Pivot the rows, creating a new resultset
898 : *
899 : * Call dbpivot() immediately after dbresults(). It calls dbnextrow() as long as
900 : * it returns REG_ROW, transforming the results into a cross-tab report.
901 : * dbpivot() modifies the metadata such that DB-Library can be used tranparently:
902 : * retrieve the rows as usual with dbnumcols(), dbnextrow(), etc.
903 : *
904 : * @dbproc, our old friend
905 : * @nkeys the number of left-edge columns to group by
906 : * @keys an array of left-edge columns to group by
907 : * @ncols the number of top-edge columns to group by
908 : * @cols an array of top-edge columns to group by
909 : * @func the aggregation function to use
910 : * @val the number of the column to which @func is applied
911 : *
912 : * @returns the return code from the final call to dbnextrow().
913 : * Success is normally indicated by NO_MORE_ROWS.
914 : */
915 : RETCODE
916 0 : dbpivot(DBPROCESS *dbproc, int nkeys, int *keys, int ncols, int *cols, DBPIVOT_FUNC func, int val)
917 : {
918 : enum { logalot = 1 };
919 : PIVOT_T P, *pp;
920 0 : AGG_T input, *pout = NULL;
921 : struct metadata_t *metadata, *pmeta;
922 0 : size_t i, nmeta = 0;
923 :
924 0 : tdsdump_log(TDS_DBG_FUNC, "dbpivot(%p, %d,%p, %d,%p, %p, %d)\n", dbproc, nkeys, keys, ncols, cols, func, val);
925 : if (logalot) {
926 0 : char buffer[1024] = {'\0'}, *s = buffer;
927 : const static char *const names[2] = { "\tkeys (down)", "\n\tcols (across)" };
928 0 : int *p = keys, *pend = p + nkeys;
929 :
930 0 : for (i=0; i < 2; i++) {
931 0 : const char *sep = "";
932 0 : s += sprintf(s, "%s: ", names[i]);
933 0 : for ( ; p < pend; p++) {
934 0 : s += sprintf(s, "%s%d", sep, *p);
935 0 : sep = ", ";
936 : }
937 0 : p = cols;
938 0 : pend = p + ncols;
939 0 : assert(s < buffer + sizeof(buffer));
940 : }
941 0 : tdsdump_log(TDS_DBG_FUNC, "%s\n", buffer);
942 : }
943 :
944 0 : memset(&input, 0, sizeof(input));
945 :
946 0 : P.dbproc = dbproc;
947 0 : if ((pp = tds_find(&P, pivots, npivots, sizeof(*pivots), (compare_func) pivot_key_equal)) == NULL ) {
948 0 : pp = TDS_RESIZE(pivots, 1 + npivots);
949 0 : if (!pp)
950 : return FAIL;
951 0 : pp += npivots++;
952 : } else {
953 0 : agg_free(pp->output);
954 0 : key_free(pp->across);
955 : }
956 0 : memset(pp, 0, sizeof(*pp));
957 :
958 0 : if ((input.row_key.keys = tds_new0(struct col_t, nkeys)) == NULL)
959 : return FAIL;
960 0 : input.row_key.nkeys = nkeys;
961 0 : for (i=0; i < nkeys; i++) {
962 0 : int type = dbcoltype(dbproc, keys[i]);
963 0 : int len = dbcollen(dbproc, keys[i]);
964 0 : assert(type && len);
965 :
966 0 : if (!col_init(input.row_key.keys+i, type, len))
967 : return FAIL;
968 0 : if (FAIL == dbbind(dbproc, keys[i], bind_type(type), input.row_key.keys[i].len, col_buffer(input.row_key.keys+i)))
969 : return FAIL;
970 0 : if (FAIL == dbnullbind(dbproc, keys[i], &input.row_key.keys[i].null_indicator))
971 : return FAIL;
972 : }
973 :
974 0 : if ((input.col_key.keys = tds_new0(struct col_t, ncols)) == NULL)
975 : return FAIL;
976 0 : input.col_key.nkeys = ncols;
977 0 : for (i=0; i < ncols; i++) {
978 0 : int type = dbcoltype(dbproc, cols[i]);
979 0 : int len = dbcollen(dbproc, cols[i]);
980 0 : assert(type && len);
981 :
982 0 : if (!col_init(input.col_key.keys+i, type, len))
983 : return FAIL;
984 0 : if (FAIL == dbbind(dbproc, cols[i], bind_type(type), input.col_key.keys[i].len, col_buffer(input.col_key.keys+i)))
985 : return FAIL;
986 0 : if (FAIL == dbnullbind(dbproc, cols[i], &input.col_key.keys[i].null_indicator))
987 : return FAIL;
988 : }
989 :
990 : /* value */ {
991 0 : int type = dbcoltype(dbproc, val);
992 0 : int len = dbcollen(dbproc, val);
993 0 : assert(type && len);
994 :
995 0 : if (!col_init(&input.value, type, len))
996 : return FAIL;
997 0 : if (FAIL == dbbind(dbproc, val, bind_type(type), input.value.len, col_buffer(&input.value)))
998 : return FAIL;
999 0 : if (FAIL == dbnullbind(dbproc, val, &input.value.null_indicator))
1000 : return FAIL;
1001 : }
1002 :
1003 0 : while ((pp->status = dbnextrow(dbproc)) == REG_ROW) {
1004 : /* add to unique list of crosstab columns */
1005 0 : if (tds_find(&input.col_key, pp->across, pp->nacross, sizeof(*pp->across), (compare_func) key_equal) == NULL) {
1006 0 : if (!TDS_RESIZE(pp->across, 1 + pp->nacross))
1007 : return FAIL;
1008 0 : key_cpy(pp->across + pp->nacross, &input.col_key);
1009 : }
1010 0 : assert(pp->across);
1011 :
1012 0 : if ((pout = tds_find(&input, pp->output, pp->nout, sizeof(*pp->output), (compare_func) agg_equal)) == NULL ) {
1013 0 : if (!TDS_RESIZE(pp->output, 1 + pp->nout))
1014 : return FAIL;
1015 0 : pout = pp->output + pp->nout++;
1016 :
1017 :
1018 0 : if ((pout->row_key.keys = tds_new0(struct col_t, input.row_key.nkeys)) == NULL)
1019 : return FAIL;
1020 0 : key_cpy(&pout->row_key, &input.row_key);
1021 :
1022 0 : if ((pout->col_key.keys = tds_new0(struct col_t, input.col_key.nkeys)) == NULL)
1023 : return FAIL;
1024 0 : key_cpy(&pout->col_key, &input.col_key);
1025 :
1026 0 : if (!col_init(&pout->value, input.value.type, input.value.len))
1027 : return FAIL;
1028 : }
1029 :
1030 0 : func(&pout->value, &input.value);
1031 :
1032 : }
1033 :
1034 : /* Mark this proc as pivoted, so that dbnextrow() sees it when the application calls it */
1035 0 : pp->dbproc = dbproc;
1036 0 : pp->dbresults_state = dbproc->dbresults_state;
1037 0 : dbproc->dbresults_state = pp->output < pout? _DB_RES_RESULTSET_ROWS : _DB_RES_RESULTSET_EMPTY;
1038 :
1039 : /*
1040 : * Initialize new metadata
1041 : */
1042 0 : nmeta = input.row_key.nkeys + pp->nacross;
1043 0 : metadata = tds_new0(struct metadata_t, nmeta);
1044 0 : if (!metadata) {
1045 0 : dbperror(dbproc, SYBEMEM, errno);
1046 0 : return FAIL;
1047 : }
1048 0 : assert(pp->across || pp->nacross == 0);
1049 :
1050 : /* key columns are passed through as-is, verbatim */
1051 0 : for (i=0; i < input.row_key.nkeys; i++) {
1052 0 : assert(i < nkeys);
1053 0 : metadata[i].name = strdup(dbcolname(dbproc, keys[i]));
1054 0 : metadata[i].pacross = NULL;
1055 0 : col_cpy(&metadata[i].col, input.row_key.keys+i);
1056 : }
1057 :
1058 : /* pivoted columms are found in the "across" data */
1059 0 : for (i=0, pmeta = metadata + input.row_key.nkeys; i < pp->nacross; i++) {
1060 : struct col_t col;
1061 0 : if (!col_init(&col, SYBFLT8, sizeof(double)))
1062 0 : return FAIL;
1063 0 : assert(pmeta + i < metadata + nmeta);
1064 0 : pmeta[i].name = make_col_name(dbproc, pp->across+i);
1065 0 : if (!pmeta[i].name)
1066 : return FAIL;
1067 0 : assert(pp->across);
1068 0 : pmeta[i].pacross = pp->across + i;
1069 0 : col_cpy(&pmeta[i].col, pp->nout? &pp->output[0].value : &col);
1070 : }
1071 :
1072 0 : if (!reinit_results(dbproc->tds_socket, nmeta, metadata)) {
1073 : return FAIL;
1074 : }
1075 :
1076 0 : return SUCCEED;
1077 :
1078 : #if 0
1079 : for (pp->pout=pp->output; pp->pout < pp->output + pp->nout; pp->pout++) {
1080 : char name[256] = {0};
1081 :
1082 : assert(pp->pout->col_key.keys[0].len < sizeof(name));
1083 : memset(name, '\0', sizeof(name));
1084 : memcpy(name, pp->pout->col_key.keys[0].s, pp->pout->col_key.keys[0].len),
1085 : printf("%5d %-30s %5d\n", pp->pout->row_key.keys[0].i,
1086 : name,
1087 : pp->pout->value.i );
1088 : }
1089 : exit(1);
1090 : #endif
1091 : }
1092 :
1093 : /*
1094 : * Aggregation functions
1095 : */
1096 :
1097 : void
1098 0 : dbpivot_count (struct col_t *tgt, const struct col_t *src)
1099 : {
1100 0 : assert( tgt && src);
1101 0 : assert (src->type);
1102 :
1103 0 : tgt->type = SYBINT4;
1104 :
1105 0 : if (! col_null(src))
1106 0 : tgt->data.i++;
1107 0 : }
1108 :
1109 : void
1110 0 : dbpivot_sum (struct col_t *tgt, const struct col_t *src)
1111 : {
1112 0 : assert( tgt && src);
1113 0 : assert (src->type);
1114 :
1115 0 : tgt->type = src->type;
1116 :
1117 0 : if (col_null(src))
1118 : return;
1119 :
1120 0 : switch (src->type) {
1121 0 : case SYBINT1:
1122 0 : tgt->data.ti += src->data.ti;
1123 0 : break;
1124 0 : case SYBINT2:
1125 0 : tgt->data.si += src->data.si;
1126 0 : break;
1127 0 : case SYBINT4:
1128 0 : tgt->data.i += src->data.i;
1129 0 : break;
1130 0 : case SYBFLT8:
1131 0 : tgt->data.f += src->data.f;
1132 0 : break;
1133 0 : case SYBREAL:
1134 0 : tgt->data.r += src->data.r;
1135 0 : break;
1136 :
1137 0 : case SYBCHAR:
1138 : case SYBVARCHAR:
1139 : case SYBINTN:
1140 : case SYBDATETIME:
1141 : case SYBBIT:
1142 : case SYBTEXT:
1143 : case SYBNTEXT:
1144 : case SYBIMAGE:
1145 : case SYBMONEY4:
1146 : case SYBMONEY:
1147 : case SYBDATETIME4:
1148 : case SYBBINARY:
1149 : case SYBVOID:
1150 : case SYBVARBINARY:
1151 : case SYBBITN:
1152 : case SYBNUMERIC:
1153 : case SYBDECIMAL:
1154 : case SYBFLTN:
1155 : case SYBMONEYN:
1156 : case SYBDATETIMN:
1157 : default:
1158 0 : tdsdump_log(TDS_DBG_INFO1, "dbpivot_sum(): invalid operand %d\n", src->type);
1159 0 : tgt->type = SYBINT4;
1160 0 : tgt->data.i = 0;
1161 0 : break;
1162 : }
1163 : }
1164 :
1165 : void
1166 0 : dbpivot_min (struct col_t *tgt, const struct col_t *src)
1167 : {
1168 0 : assert( tgt && src);
1169 0 : assert (src->type);
1170 :
1171 0 : tgt->type = src->type;
1172 :
1173 0 : if (col_null(src))
1174 : return;
1175 :
1176 0 : switch (src->type) {
1177 0 : case SYBINT1:
1178 0 : tgt->data.ti = tgt->data.ti < src->data.ti? tgt->data.ti : src->data.ti;
1179 0 : break;
1180 0 : case SYBINT2:
1181 0 : tgt->data.si = tgt->data.si < src->data.si? tgt->data.si : src->data.si;
1182 0 : break;
1183 0 : case SYBINT4:
1184 0 : tgt->data.i = tgt->data.i < src->data.i? tgt->data.i : src->data.i;
1185 0 : break;
1186 0 : case SYBFLT8:
1187 0 : tgt->data.f = tgt->data.f < src->data.f? tgt->data.f : src->data.f;
1188 0 : break;
1189 0 : case SYBREAL:
1190 0 : tgt->data.r = tgt->data.r < src->data.r? tgt->data.r : src->data.r;
1191 0 : break;
1192 :
1193 0 : case SYBCHAR:
1194 : case SYBVARCHAR:
1195 : case SYBINTN:
1196 : case SYBDATETIME:
1197 : case SYBBIT:
1198 : case SYBTEXT:
1199 : case SYBNTEXT:
1200 : case SYBIMAGE:
1201 : case SYBMONEY4:
1202 : case SYBMONEY:
1203 : case SYBDATETIME4:
1204 : case SYBBINARY:
1205 : case SYBVOID:
1206 : case SYBVARBINARY:
1207 : case SYBBITN:
1208 : case SYBNUMERIC:
1209 : case SYBDECIMAL:
1210 : case SYBFLTN:
1211 : case SYBMONEYN:
1212 : case SYBDATETIMN:
1213 : default:
1214 0 : tdsdump_log(TDS_DBG_INFO1, "dbpivot_sum(): invalid operand %d\n", src->type);
1215 0 : tgt->type = SYBINT4;
1216 0 : tgt->data.i = 0;
1217 0 : break;
1218 : }
1219 : }
1220 :
1221 : void
1222 0 : dbpivot_max (struct col_t *tgt, const struct col_t *src)
1223 : {
1224 0 : assert( tgt && src);
1225 0 : assert (src->type);
1226 :
1227 0 : tgt->type = src->type;
1228 :
1229 0 : if (col_null(src))
1230 : return;
1231 :
1232 0 : switch (src->type) {
1233 0 : case SYBINT1:
1234 0 : tgt->data.ti = tgt->data.ti > src->data.ti? tgt->data.ti : src->data.ti;
1235 0 : break;
1236 0 : case SYBINT2:
1237 0 : tgt->data.si = tgt->data.si > src->data.si? tgt->data.si : src->data.si;
1238 0 : break;
1239 0 : case SYBINT4:
1240 0 : tgt->data.i = tgt->data.i > src->data.i? tgt->data.i : src->data.i;
1241 0 : break;
1242 0 : case SYBFLT8:
1243 0 : tgt->data.f = tgt->data.f > src->data.f? tgt->data.f : src->data.f;
1244 0 : break;
1245 0 : case SYBREAL:
1246 0 : tgt->data.r = tgt->data.r > src->data.r? tgt->data.r : src->data.r;
1247 0 : break;
1248 :
1249 0 : case SYBCHAR:
1250 : case SYBVARCHAR:
1251 : case SYBINTN:
1252 : case SYBDATETIME:
1253 : case SYBBIT:
1254 : case SYBTEXT:
1255 : case SYBNTEXT:
1256 : case SYBIMAGE:
1257 : case SYBMONEY4:
1258 : case SYBMONEY:
1259 : case SYBDATETIME4:
1260 : case SYBBINARY:
1261 : case SYBVOID:
1262 : case SYBVARBINARY:
1263 : case SYBBITN:
1264 : case SYBNUMERIC:
1265 : case SYBDECIMAL:
1266 : case SYBFLTN:
1267 : case SYBMONEYN:
1268 : case SYBDATETIMN:
1269 : default:
1270 0 : tdsdump_log(TDS_DBG_INFO1, "dbpivot_sum(): invalid operand %d\n", src->type);
1271 0 : tgt->type = SYBINT4;
1272 0 : tgt->data.i = 0;
1273 0 : break;
1274 : }
1275 : }
1276 :
1277 : static const struct name_t {
1278 : char name[14];
1279 : DBPIVOT_FUNC func;
1280 : } names[] =
1281 : { { "count", dbpivot_count }
1282 : , { "sum", dbpivot_sum }
1283 : , { "min", dbpivot_min }
1284 : , { "max", dbpivot_max }
1285 : };
1286 :
1287 : static bool
1288 0 : name_equal( const struct name_t *n1, const struct name_t *n2 )
1289 : {
1290 0 : assert(n1 && n2);
1291 0 : return strcmp(n1->name, n2->name) == 0;
1292 : }
1293 :
1294 : DBPIVOT_FUNC
1295 0 : dbpivot_lookup_name( const char name[] )
1296 : {
1297 0 : struct name_t *n = TDS_FIND(name, names, (compare_func) name_equal);
1298 :
1299 0 : return n ? n->func : NULL;
1300 : }
|